summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2024-02-01 15:48:30 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2024-02-01 15:48:30 +0300
commit16577c5292bf23956a5ce81e7e1fe1c3633b3016 (patch)
treec8f57395975a0f805aaf33893775b6ba18bd2b49
parent1c7f67f47770a0c8591aeff7c54766c6f5d00cca (diff)
parent62e234c114d2b6ead93a1d39593c66b648c3d0a6 (diff)
Merge branch 'libodb-oracle' into multi-package
-rw-r--r--libodb-oracle/.gitignore25
-rw-r--r--libodb-oracle/INSTALL6
-rw-r--r--libodb-oracle/LICENSE21
-rw-r--r--libodb-oracle/NCUEL294
-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/manifest22
-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/buildfile161
-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/build2/config-stub.h5
-rw-r--r--libodb-oracle/odb/oracle/details/build2/config-vc-stub.h5
-rw-r--r--libodb-oracle/odb/oracle/details/build2/config-vc.h15
-rw-r--r--libodb-oracle/odb/oracle/details/build2/config.h17
-rw-r--r--libodb-oracle/odb/oracle/details/config-vc.h5
-rw-r--r--libodb-oracle/odb/oracle/details/config.h.in12
-rw-r--r--libodb-oracle/odb/oracle/details/config.hxx21
-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.hxx78
-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-build2-stub.hxx4
-rw-r--r--libodb-oracle/odb/oracle/version-build2.hxx0
-rw-r--r--libodb-oracle/odb/oracle/version-build2.hxx.in42
-rw-r--r--libodb-oracle/odb/oracle/version.hxx48
-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
112 files changed, 19714 insertions, 0 deletions
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..c5effce
--- /dev/null
+++ b/libodb-oracle/LICENSE
@@ -0,0 +1,21 @@
+Copyright (c) 2009-2024 Code Synthesis Tools CC.
+
+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 Tools CC.
+
+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..e8e179c
--- /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 Tools CC 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/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..ab1502d
--- /dev/null
+++ b/libodb-oracle/manifest
@@ -0,0 +1,22 @@
+: 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/libodb-oracle/
+email: odb-users@codesynthesis.com
+build-warning-email: odb-builds@codesynthesis.com
+builds: none ; Requires proprietary Oracle Call Interface library.
+requires: c++11
+requires: oci ; Oracle Call Interface library.
+depends: * build2 >= 0.16.0-
+depends: * bpkg >= 0.16.0-
+depends: libodb [2.5.0-b.26.1 2.5.0-b.27)
+depends: * cli ^1.2.0- ? ($config.libodb_oracle.develop)
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..c1cb778
--- /dev/null
+++ b/libodb-oracle/odb/oracle/buildfile
@@ -0,0 +1,161 @@
+# 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-build2} {hxx}{version-build2} \
+ details/{hxx ixx txx cxx}{* -options} \
+ details/build2/{h}{*} \
+ $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-build2}: in{version-build2} $src_root/manifest
+hxx{version-build2}:
+{
+ dist = true
+ clean = ($src_root != $out_root)
+}
+
+# Build options.
+#
+cxx.poptions =+ "-I$out_root" "-I$src_root" -DLIBODB_ORACLE_BUILD2
+
+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" -DLIBODB_ORACLE_BUILD2
+ 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
+}
+
+# We want these to be picked up whether LIBODB_ORACLE_BUILD2 is defined or not.
+#
+hxx{version}@./: install = false
+hxx{version-build2}: install = $install_include/version.hxx
+hxx{version-build2-stub}@./: install = $install_include/version-build2.hxx
+
+details/build2/
+{
+ h{*}: install = false
+
+ if ($cxx.target.system == 'win32-msvc')
+ {
+ h{config-vc}@./: install = $install_include/details/
+ h{config-vc-stub}@./: install = $install_include/details/build2/config-vc.h
+ }
+ else
+ {
+ h{config}@./: install = $install_include/details/
+ h{config-stub}@./: install = $install_include/details/build2/config.h
+ }
+}
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/build2/config-stub.h b/libodb-oracle/odb/oracle/details/build2/config-stub.h
new file mode 100644
index 0000000..2159951
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/build2/config-stub.h
@@ -0,0 +1,5 @@
+/* file : odb/oracle/details/build2/config-stub.h
+ * license : ODB NCUEL; see accompanying LICENSE file
+ */
+
+#include <odb/oracle/details/config.h>
diff --git a/libodb-oracle/odb/oracle/details/build2/config-vc-stub.h b/libodb-oracle/odb/oracle/details/build2/config-vc-stub.h
new file mode 100644
index 0000000..697da2e
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/build2/config-vc-stub.h
@@ -0,0 +1,5 @@
+/* file : odb/oracle/details/build2/config-vc-stub.h
+ * license : ODB NCUEL; see accompanying LICENSE file
+ */
+
+#include <odb/oracle/details/config-vc.h>
diff --git a/libodb-oracle/odb/oracle/details/build2/config-vc.h b/libodb-oracle/odb/oracle/details/build2/config-vc.h
new file mode 100644
index 0000000..8b9dbdd
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/build2/config-vc.h
@@ -0,0 +1,15 @@
+/* file : odb/oracle/details/build2/config-vc.h
+ * license : ODB NCUEL; see accompanying LICENSE file
+ */
+
+/* Configuration file for Windows/VC++ for the build2 build. */
+
+#ifndef ODB_ORACLE_DETAILS_CONFIG_VC_H
+#define ODB_ORACLE_DETAILS_CONFIG_VC_H
+
+/* Define LIBODB_ORACLE_BUILD2 for the installed case. */
+#ifndef LIBODB_ORACLE_BUILD2
+# define LIBODB_ORACLE_BUILD2
+#endif
+
+#endif /* ODB_ORACLE_DETAILS_CONFIG_VC_H */
diff --git a/libodb-oracle/odb/oracle/details/build2/config.h b/libodb-oracle/odb/oracle/details/build2/config.h
new file mode 100644
index 0000000..017c1ba
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/build2/config.h
@@ -0,0 +1,17 @@
+/* file : odb/oracle/details/build2/config.h
+ * license : ODB NCUEL; see accompanying LICENSE file
+ */
+
+/* Static configuration file for the build2 build. The installed case
+ (when LIBODB_ORACLE_BUILD2 is not necessarily defined) is the only
+ reason we have it. */
+
+#ifndef ODB_ORACLE_DETAILS_CONFIG_H
+#define ODB_ORACLE_DETAILS_CONFIG_H
+
+/* Define LIBODB_ORACLE_BUILD2 for the installed case. */
+#ifndef LIBODB_ORACLE_BUILD2
+# define LIBODB_ORACLE_BUILD2
+#endif
+
+#endif /* ODB_ORACLE_DETAILS_CONFIG_H */
diff --git a/libodb-oracle/odb/oracle/details/config-vc.h b/libodb-oracle/odb/oracle/details/config-vc.h
new file mode 100644
index 0000000..8036f07
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/config-vc.h
@@ -0,0 +1,5 @@
+/* file : odb/oracle/details/config-vc.h
+ * license : ODB NCUEL; see accompanying LICENSE file
+ */
+
+/* Dummy configuration file for Windows/VC++. */
diff --git a/libodb-oracle/odb/oracle/details/config.h.in b/libodb-oracle/odb/oracle/details/config.h.in
new file mode 100644
index 0000000..3624466
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/config.h.in
@@ -0,0 +1,12 @@
+/* file : odb/oracle/details/config.h.in
+ * license : ODB NCUEL; see accompanying LICENSE file
+ */
+
+/* This file is automatically processed by configure. */
+
+#ifndef ODB_ORACLE_DETAILS_CONFIG_H
+#define ODB_ORACLE_DETAILS_CONFIG_H
+
+#undef LIBODB_ORACLE_STATIC_LIB
+
+#endif /* ODB_ORACLE_DETAILS_CONFIG_H */
diff --git a/libodb-oracle/odb/oracle/details/config.hxx b/libodb-oracle/odb/oracle/details/config.hxx
new file mode 100644
index 0000000..89f715b
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/config.hxx
@@ -0,0 +1,21 @@
+// 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
+#elif !defined(LIBODB_ORACLE_BUILD2)
+# ifdef _MSC_VER
+# include <odb/oracle/details/config-vc.h>
+# else
+# include <odb/oracle/details/config.h>
+# endif
+#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..7a7aea5
--- /dev/null
+++ b/libodb-oracle/odb/oracle/details/export.hxx
@@ -0,0 +1,78 @@
+// 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>
+
+#include <odb/oracle/details/config.hxx>
+
+// 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.
+
+#ifdef LIBODB_ORACLE_BUILD2
+
+#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
+
+#else // LIBODB_ORACLE_BUILD2
+
+#ifdef LIBODB_ORACLE_STATIC_LIB
+# define LIBODB_ORACLE_EXPORT
+#else
+# ifdef _WIN32
+# ifdef _MSC_VER
+# ifdef LIBODB_ORACLE_DYNAMIC_LIB
+# define LIBODB_ORACLE_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_ORACLE_EXPORT __declspec(dllimport)
+# endif
+# else
+# ifdef LIBODB_ORACLE_DYNAMIC_LIB
+# ifdef DLL_EXPORT
+# define LIBODB_ORACLE_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_ORACLE_EXPORT
+# endif
+# else
+# define LIBODB_ORACLE_EXPORT __declspec(dllimport)
+# endif
+# endif
+# else
+# define LIBODB_ORACLE_EXPORT
+# endif
+#endif
+
+#endif // LIBODB_ORACLE_BUILD2
+
+#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-build2-stub.hxx b/libodb-oracle/odb/oracle/version-build2-stub.hxx
new file mode 100644
index 0000000..d7da05c
--- /dev/null
+++ b/libodb-oracle/odb/oracle/version-build2-stub.hxx
@@ -0,0 +1,4 @@
+// file : odb/oracle/version-build2-stub.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/oracle/version.hxx>
diff --git a/libodb-oracle/odb/oracle/version-build2.hxx b/libodb-oracle/odb/oracle/version-build2.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libodb-oracle/odb/oracle/version-build2.hxx
diff --git a/libodb-oracle/odb/oracle/version-build2.hxx.in b/libodb-oracle/odb/oracle/version-build2.hxx.in
new file mode 100644
index 0000000..6006a0e
--- /dev/null
+++ b/libodb-oracle/odb/oracle/version-build2.hxx.in
@@ -0,0 +1,42 @@
+// file : odb/oracle/version-build2.hxx.in
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef LIBODB_ORACLE_VERSION // Note: using the version macro itself.
+
+// The 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 $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, LIBODB_SNAPSHOT)$
+
+#endif // LIBODB_ORACLE_VERSION
diff --git a/libodb-oracle/odb/oracle/version.hxx b/libodb-oracle/odb/oracle/version.hxx
new file mode 100644
index 0000000..1523172
--- /dev/null
+++ b/libodb-oracle/odb/oracle/version.hxx
@@ -0,0 +1,48 @@
+// file : odb/oracle/version.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifdef LIBODB_ORACLE_BUILD2
+# include <odb/oracle/version-build2.hxx>
+#else
+
+#ifndef ODB_ORACLE_VERSION_HXX
+#define ODB_ORACLE_VERSION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/oracle/details/config.hxx>
+#include <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
+//
+
+// Check that we have compatible ODB version.
+//
+#if ODB_VERSION != 20476
+# error incompatible odb interface version detected
+#endif
+
+// libodb-oracle version: odb interface version plus the bugfix
+// version.
+//
+#define LIBODB_ORACLE_VERSION 2049976
+#define LIBODB_ORACLE_VERSION_STR "2.5.0-b.26"
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_VERSION_HXX
+#endif // LIBODB_ORACLE_BUILD2
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/}