summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2024-02-01 18:10:52 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2024-02-01 18:10:52 +0300
commitae5a90db9df0cb9f4f2df8218bc1ee81556490ac (patch)
tree6446af3bdcf112f1687e59313a3ed4d698b7e1cf
parent3fa01c83a095f1f5be99189236ec348f5f2fa2c1 (diff)
parent2895ad78dbdb43e57fc34558b4530b4e105fc72d (diff)
Merge branch 'libodb-mssql' into multi-package
-rw-r--r--libodb-mssql/.gitignore25
-rw-r--r--libodb-mssql/INSTALL6
-rw-r--r--libodb-mssql/LICENSE21
-rw-r--r--libodb-mssql/NCUEL294
-rw-r--r--libodb-mssql/README20
-rw-r--r--libodb-mssql/build/.gitignore3
-rw-r--r--libodb-mssql/build/bootstrap.build10
-rw-r--r--libodb-mssql/build/export.build9
-rw-r--r--libodb-mssql/build/root.build19
-rw-r--r--libodb-mssql/buildfile9
-rw-r--r--libodb-mssql/manifest23
-rw-r--r--libodb-mssql/odb/mssql/auto-handle.cxx17
-rw-r--r--libodb-mssql/odb/mssql/auto-handle.hxx88
-rw-r--r--libodb-mssql/odb/mssql/binding.hxx65
-rw-r--r--libodb-mssql/odb/mssql/buildfile165
-rw-r--r--libodb-mssql/odb/mssql/connection-factory.cxx159
-rw-r--r--libodb-mssql/odb/mssql/connection-factory.hxx134
-rw-r--r--libodb-mssql/odb/mssql/connection.cxx287
-rw-r--r--libodb-mssql/odb/mssql/connection.hxx187
-rw-r--r--libodb-mssql/odb/mssql/connection.ixx44
-rw-r--r--libodb-mssql/odb/mssql/container-statements.hxx353
-rw-r--r--libodb-mssql/odb/mssql/container-statements.txx96
-rw-r--r--libodb-mssql/odb/mssql/database.cxx580
-rw-r--r--libodb-mssql/odb/mssql/database.hxx629
-rw-r--r--libodb-mssql/odb/mssql/database.ixx644
-rw-r--r--libodb-mssql/odb/mssql/details/.gitignore1
-rw-r--r--libodb-mssql/odb/mssql/details/build2/config-stub.h5
-rw-r--r--libodb-mssql/odb/mssql/details/build2/config-vc-stub.h5
-rw-r--r--libodb-mssql/odb/mssql/details/build2/config-vc.h15
-rw-r--r--libodb-mssql/odb/mssql/details/build2/config.h17
-rw-r--r--libodb-mssql/odb/mssql/details/config-vc.h5
-rw-r--r--libodb-mssql/odb/mssql/details/config.h.in12
-rw-r--r--libodb-mssql/odb/mssql/details/config.hxx21
-rw-r--r--libodb-mssql/odb/mssql/details/conversion.hxx58
-rw-r--r--libodb-mssql/odb/mssql/details/export.hxx78
-rw-r--r--libodb-mssql/odb/mssql/details/options.cli63
-rw-r--r--libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.cxx1125
-rw-r--r--libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.hxx562
-rw-r--r--libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.ixx372
-rw-r--r--libodb-mssql/odb/mssql/error.cxx273
-rw-r--r--libodb-mssql/odb/mssql/error.hxx40
-rw-r--r--libodb-mssql/odb/mssql/exceptions.cxx109
-rw-r--r--libodb-mssql/odb/mssql/exceptions.hxx138
-rw-r--r--libodb-mssql/odb/mssql/forward.hxx91
-rw-r--r--libodb-mssql/odb/mssql/mssql-fwd.hxx216
-rw-r--r--libodb-mssql/odb/mssql/mssql-types.hxx161
-rw-r--r--libodb-mssql/odb/mssql/mssql.hxx69
-rw-r--r--libodb-mssql/odb/mssql/no-id-object-result.hxx85
-rw-r--r--libodb-mssql/odb/mssql/no-id-object-result.txx154
-rw-r--r--libodb-mssql/odb/mssql/no-id-object-statements.hxx137
-rw-r--r--libodb-mssql/odb/mssql/no-id-object-statements.txx39
-rw-r--r--libodb-mssql/odb/mssql/polymorphic-object-result.hxx99
-rw-r--r--libodb-mssql/odb/mssql/polymorphic-object-result.txx325
-rw-r--r--libodb-mssql/odb/mssql/polymorphic-object-statements.hxx469
-rw-r--r--libodb-mssql/odb/mssql/polymorphic-object-statements.txx140
-rw-r--r--libodb-mssql/odb/mssql/prepared-query.cxx15
-rw-r--r--libodb-mssql/odb/mssql/prepared-query.hxx34
-rw-r--r--libodb-mssql/odb/mssql/query-const-expr.cxx14
-rw-r--r--libodb-mssql/odb/mssql/query-dynamic.cxx157
-rw-r--r--libodb-mssql/odb/mssql/query-dynamic.hxx32
-rw-r--r--libodb-mssql/odb/mssql/query-dynamic.ixx30
-rw-r--r--libodb-mssql/odb/mssql/query-dynamic.txx25
-rw-r--r--libodb-mssql/odb/mssql/query.cxx406
-rw-r--r--libodb-mssql/odb/mssql/query.hxx2711
-rw-r--r--libodb-mssql/odb/mssql/query.ixx34
-rw-r--r--libodb-mssql/odb/mssql/query.txx169
-rw-r--r--libodb-mssql/odb/mssql/section-statements.hxx201
-rw-r--r--libodb-mssql/odb/mssql/section-statements.txx50
-rw-r--r--libodb-mssql/odb/mssql/simple-object-result.hxx89
-rw-r--r--libodb-mssql/odb/mssql/simple-object-result.txx186
-rw-r--r--libodb-mssql/odb/mssql/simple-object-statements.cxx15
-rw-r--r--libodb-mssql/odb/mssql/simple-object-statements.hxx606
-rw-r--r--libodb-mssql/odb/mssql/simple-object-statements.ixx68
-rw-r--r--libodb-mssql/odb/mssql/simple-object-statements.txx173
-rw-r--r--libodb-mssql/odb/mssql/statement-cache.hxx59
-rw-r--r--libodb-mssql/odb/mssql/statement-cache.txx60
-rw-r--r--libodb-mssql/odb/mssql/statement-processing.cxx356
-rw-r--r--libodb-mssql/odb/mssql/statement.cxx1740
-rw-r--r--libodb-mssql/odb/mssql/statement.hxx558
-rw-r--r--libodb-mssql/odb/mssql/statement.ixx41
-rw-r--r--libodb-mssql/odb/mssql/statements-base.cxx15
-rw-r--r--libodb-mssql/odb/mssql/statements-base.hxx63
-rw-r--r--libodb-mssql/odb/mssql/tracer.cxx60
-rw-r--r--libodb-mssql/odb/mssql/tracer.hxx61
-rw-r--r--libodb-mssql/odb/mssql/traits-calls.hxx190
-rw-r--r--libodb-mssql/odb/mssql/traits.cxx616
-rw-r--r--libodb-mssql/odb/mssql/traits.hxx2176
-rw-r--r--libodb-mssql/odb/mssql/traits.txx399
-rw-r--r--libodb-mssql/odb/mssql/transaction-impl.cxx103
-rw-r--r--libodb-mssql/odb/mssql/transaction-impl.hxx49
-rw-r--r--libodb-mssql/odb/mssql/transaction.cxx26
-rw-r--r--libodb-mssql/odb/mssql/transaction.hxx88
-rw-r--r--libodb-mssql/odb/mssql/transaction.ixx57
-rw-r--r--libodb-mssql/odb/mssql/version-build2-stub.hxx4
-rw-r--r--libodb-mssql/odb/mssql/version-build2.hxx0
-rw-r--r--libodb-mssql/odb/mssql/version-build2.hxx.in42
-rw-r--r--libodb-mssql/odb/mssql/version.hxx48
-rw-r--r--libodb-mssql/odb/mssql/view-result.hxx85
-rw-r--r--libodb-mssql/odb/mssql/view-result.txx153
-rw-r--r--libodb-mssql/odb/mssql/view-statements.hxx83
-rw-r--r--libodb-mssql/odb/mssql/view-statements.txx30
-rw-r--r--libodb-mssql/tests/.gitignore1
-rw-r--r--libodb-mssql/tests/basics/buildfile6
-rw-r--r--libodb-mssql/tests/basics/driver.cxx37
-rw-r--r--libodb-mssql/tests/build/.gitignore3
-rw-r--r--libodb-mssql/tests/build/bootstrap.build8
-rw-r--r--libodb-mssql/tests/build/root.build23
-rw-r--r--libodb-mssql/tests/buildfile4
108 files changed, 21030 insertions, 0 deletions
diff --git a/libodb-mssql/.gitignore b/libodb-mssql/.gitignore
new file mode 100644
index 0000000..1c363a0
--- /dev/null
+++ b/libodb-mssql/.gitignore
@@ -0,0 +1,25 @@
+# Compiler/linker output.
+#
+*.d
+*.t
+*.i
+*.i.*
+*.ii
+*.ii.*
+*.o
+*.obj
+*.gcm
+*.pcm
+*.ifc
+*.so
+*.dylib
+*.dll
+*.a
+*.lib
+*.exp
+*.pdb
+*.ilk
+*.exe
+*.exe.dlls/
+*.exe.manifest
+*.pc
diff --git a/libodb-mssql/INSTALL b/libodb-mssql/INSTALL
new file mode 100644
index 0000000..b6e2be5
--- /dev/null
+++ b/libodb-mssql/INSTALL
@@ -0,0 +1,6 @@
+The easiest way to build this package is with the bpkg package manager:
+
+$ bpkg build libodb-mssql
+
+But if you don't want to use the package manager, then you can also build it
+manually using the standard build2 build system.
diff --git a/libodb-mssql/LICENSE b/libodb-mssql/LICENSE
new file mode 100644
index 0000000..c5effce
--- /dev/null
+++ b/libodb-mssql/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-mssql/NCUEL b/libodb-mssql/NCUEL
new file mode 100644
index 0000000..e8e179c
--- /dev/null
+++ b/libodb-mssql/NCUEL
@@ -0,0 +1,294 @@
+ ODB NON-COMMERCIAL USE AND EVALUATION LICENSE (NCUEL)
+
+INTENT
+
+The intent of this license is to allow you to use ODB with commercial
+databases, such as Oracle, Microsoft SQL Server, IBM DB/2, etc., free
+of charge non-commercially or for evaluation.
+
+Furthermore, if a commercial database has a free edition, often called
+express edition, such as Oracle Express, Microsoft SQL Server Express,
+IBM DB/2 Express-C, etc., that can be used for commercial purposes free
+of charge, then this license allows you to use ODB with such an edition
+for commercial purposes also free of charge.
+
+Note also that the development of an application that will be used for
+commercial purposes constitutes a commercial use and is not allowed,
+except with a free edition of a commercial database. However, this
+license allows you to evaluate ODB; that is, to use the Software for
+a reasonable period for the purpose of determining its suitability for
+a particular application as well as to conduct exploratory development
+or proof-of-concept prototyping.
+
+Finally, any application that uses ODB under this license, whether non-
+commercially, for evaluation, or commercially with a free edition of a
+database, is subject to the terms and conditions similar to that of the
+GPL version 2. In particular, this means that if and when you distribute
+your application, you are required to also release its source code.
+
+If you have any questions concerning this License, please contact us at:
+info@codesynthesis.com.
+
+LEGAL TERMS AND CONDITIONS
+
+This Code Synthesis 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-mssql/README b/libodb-mssql/README
new file mode 100644
index 0000000..21671b7
--- /dev/null
+++ b/libodb-mssql/README
@@ -0,0 +1,20 @@
+ODB is an object-relational mapping (ORM) system for C++. It provides
+tools, APIs, and library support that allow you to persist C++ objects
+to a relational database (RDBMS) without having to deal with tables,
+columns, or SQL and without manually writing any of the mapping code.
+For more information see:
+
+http://www.codesynthesis.com/products/odb/
+
+This package contains the Microsoft SQL Server ODB runtime library.
+Every application that includes code generated for the SQL Server
+database will need to link to this library.
+
+See the NEWS file for the user-visible changes from the previous release.
+
+See the LICENSE file for distribution conditions.
+
+See the INSTALL file for prerequisites and installation instructions.
+
+Send questions, bug reports, or any other feedback to the
+odb-users@codesynthesis.com mailing list.
diff --git a/libodb-mssql/build/.gitignore b/libodb-mssql/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-mssql/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-mssql/build/bootstrap.build b/libodb-mssql/build/bootstrap.build
new file mode 100644
index 0000000..fddf8ad
--- /dev/null
+++ b/libodb-mssql/build/bootstrap.build
@@ -0,0 +1,10 @@
+# file : build/bootstrap.build
+# license : ODB NCUEL; see accompanying LICENSE file
+
+project = libodb-mssql
+
+using version
+using config
+using dist
+using test
+using install
diff --git a/libodb-mssql/build/export.build b/libodb-mssql/build/export.build
new file mode 100644
index 0000000..ad90a96
--- /dev/null
+++ b/libodb-mssql/build/export.build
@@ -0,0 +1,9 @@
+# file : build/export.build
+# license : ODB NCUEL; see accompanying LICENSE file
+
+$out_root/
+{
+ include odb/mssql/
+}
+
+export $out_root/odb/mssql/lib{odb-mssql}
diff --git a/libodb-mssql/build/root.build b/libodb-mssql/build/root.build
new file mode 100644
index 0000000..794e083
--- /dev/null
+++ b/libodb-mssql/build/root.build
@@ -0,0 +1,19 @@
+# file : build/root.build
+# license : ODB NCUEL; see accompanying LICENSE file
+
+config [bool] config.libodb_mssql.develop ?= false
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+ixx{*}: extension = ixx
+txx{*}: extension = txx
+cxx{*}: extension = cxx
+
+if ($cxx.target.system == 'win32-msvc')
+ cxx.poptions += -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS
+
+if ($cxx.class == 'msvc')
+ cxx.coptions += /wd4251 /wd4275 /wd4800
diff --git a/libodb-mssql/buildfile b/libodb-mssql/buildfile
new file mode 100644
index 0000000..db1aceb
--- /dev/null
+++ b/libodb-mssql/buildfile
@@ -0,0 +1,9 @@
+# file : buildfile
+# license : ODB NCUEL; see accompanying LICENSE file
+
+./: {*/ -build/} doc{INSTALL NEWS README} legal{NCUEL LICENSE} manifest
+
+# Don't install tests or the INSTALL file.
+#
+tests/: install = false
+doc{INSTALL}@./: install = false
diff --git a/libodb-mssql/manifest b/libodb-mssql/manifest
new file mode 100644
index 0000000..68e5a84
--- /dev/null
+++ b/libodb-mssql/manifest
@@ -0,0 +1,23 @@
+: 1
+name: libodb-mssql
+version: 2.5.0-b.26.z
+project: odb
+summary: Microsoft SQL Server ODB runtime library
+license: other: ODB NCUEL ; Non-Commercial Use and Evaluation License.
+license: other: proprietary ; Not free/open source.
+topics: C++, ORM, Microsoft SQL Server, SQL
+description-file: README
+changes-file: NEWS
+url: https://www.codesynthesis.com/products/odb/
+doc-url: https://www.codesynthesis.com/products/odb/doc/manual.xhtml
+src-url: https://git.codesynthesis.com/cgit/odb/libodb-mssql/
+email: odb-users@codesynthesis.com
+build-warning-email: odb-builds@codesynthesis.com
+builds: windows ; Requires not yet packaged libunixodbc (unixODBC).
+requires: c++11
+# @@ DEP
+#requires: ? libunixodbc ; Only on UNIX.
+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_mssql.develop)
diff --git a/libodb-mssql/odb/mssql/auto-handle.cxx b/libodb-mssql/odb/mssql/auto-handle.cxx
new file mode 100644
index 0000000..ca42040
--- /dev/null
+++ b/libodb-mssql/odb/mssql/auto-handle.cxx
@@ -0,0 +1,17 @@
+// file : odb/mssql/auto-handle.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/mssql.hxx>
+#include <odb/mssql/auto-handle.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ void
+ free_handle (SQLHANDLE h, SQLSMALLINT htype)
+ {
+ SQLFreeHandle (htype, h);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/auto-handle.hxx b/libodb-mssql/odb/mssql/auto-handle.hxx
new file mode 100644
index 0000000..f6934e4
--- /dev/null
+++ b/libodb-mssql/odb/mssql/auto-handle.hxx
@@ -0,0 +1,88 @@
+// file : odb/mssql/auto-handle.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_AUTO_HANDLE_HXX
+#define ODB_MSSQL_AUTO_HANDLE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/version.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ LIBODB_MSSQL_EXPORT void
+ free_handle (SQLHANDLE, SQLSMALLINT htype);
+
+ template <SQLSMALLINT htype>
+ class auto_handle
+ {
+ public:
+ auto_handle (SQLHANDLE h = 0)
+ : h_ (h)
+ {
+ }
+
+ ~auto_handle ()
+ {
+ if (h_ != 0)
+ free_handle (h_, htype);
+ }
+
+ operator SQLHANDLE () const
+ {
+ return h_;
+ }
+
+ SQLHANDLE
+ get () const
+ {
+ return h_;
+ }
+
+ SQLHANDLE
+ release ()
+ {
+ SQLHANDLE h (h_);
+ h_ = 0;
+ return h;
+ }
+
+ void
+ reset (SQLHANDLE h = 0)
+ {
+ if (h_ != 0)
+ free_handle (h_, htype);
+
+ h_ = h;
+ }
+
+#ifdef ODB_CXX11
+ auto_handle (auto_handle&& ah) noexcept: h_ (ah.release ()) {}
+ auto_handle& operator= (auto_handle&& ah) noexcept
+ {
+ if (this != &ah)
+ reset (ah.release ());
+ return *this;
+ }
+#endif
+
+ private:
+ auto_handle (const auto_handle&);
+ auto_handle& operator= (const auto_handle&);
+
+ private:
+ SQLHANDLE h_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_AUTO_HANDLE_HXX
diff --git a/libodb-mssql/odb/mssql/binding.hxx b/libodb-mssql/odb/mssql/binding.hxx
new file mode 100644
index 0000000..c7d1bd8
--- /dev/null
+++ b/libodb-mssql/odb/mssql/binding.hxx
@@ -0,0 +1,65 @@
+// file : odb/mssql/binding.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_BINDING_HXX
+#define ODB_MSSQL_BINDING_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/mssql-types.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class LIBODB_MSSQL_EXPORT binding
+ {
+ public:
+ typedef mssql::bind bind_type;
+ typedef mssql::change_callback change_callback_type;
+
+ binding ()
+ : bind (0), count (0), version (0),
+ batch (0), skip (0), status (0),
+ change_callback (0) {}
+
+ binding (bind_type* b, std::size_t n)
+ : bind (b), count (n), version (0),
+ batch (1), skip (0), status (0),
+ change_callback (0)
+ {
+ }
+
+ binding (bind_type* b, std::size_t n,
+ std::size_t bt, std::size_t s, SQLUSMALLINT* st)
+ : bind (b), count (n), version (0),
+ batch (bt), skip (s), status (st),
+ change_callback (0)
+ {
+ }
+
+ bind_type* bind;
+ std::size_t count;
+ std::size_t version;
+
+ std::size_t batch;
+ std::size_t skip;
+ SQLUSMALLINT* status; // Batch status array.
+
+ change_callback_type* change_callback;
+
+ private:
+ binding (const binding&);
+ binding& operator= (const binding&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_BINDING_HXX
diff --git a/libodb-mssql/odb/mssql/buildfile b/libodb-mssql/odb/mssql/buildfile
new file mode 100644
index 0000000..5a99ebc
--- /dev/null
+++ b/libodb-mssql/odb/mssql/buildfile
@@ -0,0 +1,165 @@
+# file : odb/mssql/buildfile
+# license : ODB NCUEL; see accompanying LICENSE file
+
+define cli: file
+cli{*}: extension = cli
+
+import int_libs = libodb%lib{odb}
+
+# On Windows ODBC is a pre-installed system library so we pass it to the
+# linker directly
+#
+imp_libs =
+
+if ($cc.target.class != 'windows')
+ import imp_libs = libunixodbc%lib{odbc}
+
+lib{odb-mssql}: {hxx ixx txx cxx}{* -version-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_MSSQL_BUILD2
+
+obja{*}: cxx.poptions += -DLIBODB_MSSQL_STATIC_BUILD
+objs{*}: cxx.poptions += -DLIBODB_MSSQL_SHARED_BUILD
+
+if ($cc.target.class == 'windows')
+ cxx.libs += ($cxx.target.system == "mingw32" ? -lodbc32 : odbc32.lib)
+
+# Export options.
+#
+lib{odb-mssql}:
+{
+ cxx.export.poptions = "-I$out_root" "-I$src_root" -DLIBODB_MSSQL_BUILD2
+ cxx.export.libs = $int_libs
+}
+
+liba{odb-mssql}: cxx.export.poptions += -DLIBODB_MSSQL_STATIC
+libs{odb-mssql}: cxx.export.poptions += -DLIBODB_MSSQL_SHARED
+
+# For pre-releases use the complete version to make sure they cannot be used
+# in place of another pre-release or the final version. See the version module
+# for details on the version.* variable values.
+#
+if $version.pre_release
+ lib{odb-mssql}: bin.lib.version = @"-$version.project_id"
+else
+ lib{odb-mssql}: bin.lib.version = @"-$version.major.$version.minor"
+
+develop = $config.libodb_mssql.develop
+
+## Consumption build ($develop == false).
+#
+
+# Use pregenerated versions in the consumption build.
+#
+lib{odb-mssql}: details/pregenerated/{hxx ixx cxx}{**}: include = (!$develop)
+
+if! $develop
+ cxx.poptions =+ "-I($src_base/details/pregenerated)" # Note: must come first.
+
+# Don't install pregenerated headers since they are only used internally in
+# the database implementation (also below).
+#
+details/pregenerated/{hxx ixx}{*}: install = false
+
+# Distribute pregenerated versions only in the consumption build.
+#
+details/pregenerated/{hxx ixx cxx}{*}: dist = (!$develop)
+
+#
+##
+
+## Development build ($develop == true).
+#
+
+lib{odb-mssql}: details/{hxx ixx cxx}{options}: include = $develop
+
+if $develop
+ import! [metadata] cli = cli%exe{cli}
+
+# In the development build distribute regenerated {hxx ixx cxx}{options},
+# remapping their locations to the paths of the pregenerated versions (which
+# are only distributed in the consumption build; see above). This way we make
+# sure that the distributed files are always up-to-date.
+#
+<details/{hxx ixx cxx}{options}>: details/cli{options} $cli
+{
+ install = false
+ dist = ($develop ? pregenerated/odb/mssql/details/ : false)
+
+ # Symlink the generated code in src for convenience of development.
+ #
+ backlink = true
+}
+%
+if $develop
+{{
+ options = --include-with-brackets --include-prefix odb/mssql/details \
+ --guard-prefix LIBODB_MSSQL_DETAILS --generate-file-scanner \
+ --cli-namespace odb::mssql::details::cli --long-usage \
+ --generate-specifier --no-combined-flags
+
+ $cli $options -o $out_base/details/ $path($<[0])
+
+ # If the result differs from the pregenerated version, copy it over.
+ #
+ d = [dir_path] $src_base/details/pregenerated/odb/mssql/details/
+
+ if diff $d/options.hxx $path($>[0]) >- && \
+ diff $d/options.ixx $path($>[1]) >- && \
+ diff $d/options.cxx $path($>[2]) >-
+ exit
+ end
+
+ cp $path($>[0]) $d/options.hxx
+ cp $path($>[1]) $d/options.ixx
+ cp $path($>[2]) $d/options.cxx
+}}
+
+# Install into the odb/mssql/ subdirectory of, say, /usr/include/
+# recreating subdirectories.
+#
+install_include = [dir_path] include/odb/mssql/
+
+{hxx ixx txx}{*}:
+{
+ install = $install_include
+ install.subdirs = true
+}
+
+# We want these to be picked up whether LIBODB_MSSQL_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-mssql/odb/mssql/connection-factory.cxx b/libodb-mssql/odb/mssql/connection-factory.cxx
new file mode 100644
index 0000000..f673b92
--- /dev/null
+++ b/libodb-mssql/odb/mssql/connection-factory.cxx
@@ -0,0 +1,159 @@
+// file : odb/mssql/connection-factory.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/connection-factory.hxx>
+#include <odb/mssql/exceptions.hxx>
+
+#include <odb/details/lock.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ using namespace details;
+
+ namespace mssql
+ {
+ // new_connection_factory
+ //
+ connection_ptr new_connection_factory::
+ connect ()
+ {
+ return connection_ptr (new (shared) connection (*this));
+ }
+
+ // connection_pool_factory
+ //
+ connection_pool_factory::pooled_connection_ptr connection_pool_factory::
+ create ()
+ {
+ return pooled_connection_ptr (new (shared) pooled_connection (*this));
+ }
+
+ connection_pool_factory::
+ ~connection_pool_factory ()
+ {
+ // Wait for all the connections currently in use to return to
+ // the pool.
+ //
+ lock l (mutex_);
+ while (in_use_ != 0)
+ {
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ connection_ptr connection_pool_factory::
+ connect ()
+ {
+ lock l (mutex_);
+
+ while (true)
+ {
+ // See if we have a spare connection.
+ //
+ if (connections_.size () != 0)
+ {
+ shared_ptr<pooled_connection> c (connections_.back ());
+ connections_.pop_back ();
+
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // See if we can create a new one.
+ //
+ if (max_ == 0 || in_use_ < max_)
+ {
+ shared_ptr<pooled_connection> c (create ());
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // Wait until someone releases a connection.
+ //
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ void connection_pool_factory::
+ database (database_type& db)
+ {
+ bool first (db_ == 0);
+
+ connection_factory::database (db);
+
+ if (!first)
+ return;
+
+ if (min_ > 0)
+ {
+ connections_.reserve (min_);
+
+ for(size_t i (0); i < min_; ++i)
+ connections_.push_back (create ());
+ }
+ }
+
+ bool connection_pool_factory::
+ release (pooled_connection* c)
+ {
+ c->callback_ = 0;
+
+ lock l (mutex_);
+
+ // Determine if we need to keep or free this connection.
+ //
+ bool keep (!c->failed () &&
+ (waiters_ != 0 ||
+ min_ == 0 ||
+ (connections_.size () + in_use_ <= min_)));
+
+ in_use_--;
+
+ if (keep)
+ {
+ connections_.push_back (pooled_connection_ptr (inc_ref (c)));
+ connections_.back ()->recycle ();
+ }
+
+ if (waiters_ != 0)
+ cond_.signal ();
+
+ return !keep;
+ }
+
+ //
+ // connection_pool_factory::pooled_connection
+ //
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f)
+ : connection (f)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f, SQLHDBC handle)
+ : connection (f, handle)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ bool connection_pool_factory::pooled_connection::
+ zero_counter (void* arg)
+ {
+ pooled_connection* c (static_cast<pooled_connection*> (arg));
+ return static_cast<connection_pool_factory&> (c->factory_).release (c);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/connection-factory.hxx b/libodb-mssql/odb/mssql/connection-factory.hxx
new file mode 100644
index 0000000..14861a5
--- /dev/null
+++ b/libodb-mssql/odb/mssql/connection-factory.hxx
@@ -0,0 +1,134 @@
+// file : odb/mssql/connection-factory.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_CONNECTION_FACTORY_HXX
+#define ODB_MSSQL_CONNECTION_FACTORY_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/connection.hxx>
+
+#include <odb/details/mutex.hxx>
+#include <odb/details/condition.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class LIBODB_MSSQL_EXPORT new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory () {}
+
+ virtual connection_ptr
+ connect ();
+
+ private:
+ new_connection_factory (const new_connection_factory&);
+ new_connection_factory& operator= (const new_connection_factory&);
+ };
+
+ class LIBODB_MSSQL_EXPORT connection_pool_factory:
+ public connection_factory
+ {
+ public:
+ // The max_connections argument specifies the maximum number of
+ // concurrent connections this pool will maintain. If this value
+ // is 0 then the pool will create a new connection every time all
+ // of the existing connections are in use.
+ //
+ // The min_connections argument specifies the minimum number of
+ // connections that should be maintained by the pool. If the
+ // number of connections maintained by the pool exceeds this
+ // number and there are no active waiters for a new connection,
+ // then the pool will release the excess connections. If this
+ // value is 0 then the pool will maintain all the connections
+ // that were ever created.
+ //
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0)
+ : max_ (max_connections),
+ min_ (min_connections),
+ in_use_ (0),
+ waiters_ (0),
+ cond_ (mutex_)
+ {
+ // max_connections == 0 means unlimited.
+ //
+ assert (max_connections == 0 || max_connections >= min_connections);
+ }
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ database (database_type&);
+
+ virtual
+ ~connection_pool_factory ();
+
+ private:
+ connection_pool_factory (const connection_pool_factory&);
+ connection_pool_factory& operator= (const connection_pool_factory&);
+
+ protected:
+ class LIBODB_MSSQL_EXPORT pooled_connection: public connection
+ {
+ public:
+ pooled_connection (connection_pool_factory&);
+ pooled_connection (connection_pool_factory&, SQLHDBC);
+
+ private:
+ static bool
+ zero_counter (void*);
+
+ private:
+ friend class connection_pool_factory;
+
+ shared_base::refcount_callback cb_;
+ };
+
+ friend class pooled_connection;
+
+ typedef details::shared_ptr<pooled_connection> pooled_connection_ptr;
+ typedef std::vector<pooled_connection_ptr> connections;
+
+ // This function is called whenever the pool needs to create a new
+ // connection.
+ //
+ virtual pooled_connection_ptr
+ create ();
+
+ protected:
+ // Return true if the connection should be deleted, false otherwise.
+ //
+ bool
+ release (pooled_connection*);
+
+ protected:
+ const std::size_t max_;
+ const std::size_t min_;
+
+ std::size_t in_use_; // Number of connections currently in use.
+ std::size_t waiters_; // Number of threads waiting for a connection.
+
+ connections connections_;
+
+ details::mutex mutex_;
+ details::condition cond_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_CONNECTION_FACTORY_HXX
diff --git a/libodb-mssql/odb/mssql/connection.cxx b/libodb-mssql/odb/mssql/connection.cxx
new file mode 100644
index 0000000..5181fab
--- /dev/null
+++ b/libodb-mssql/odb/mssql/connection.cxx
@@ -0,0 +1,287 @@
+// file : odb/mssql/connection.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <string>
+#include <cstdint> //intptr_t
+
+#include <odb/mssql/mssql.hxx>
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/transaction.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/statement-cache.hxx>
+#include <odb/mssql/error.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ static const intptr_t transaction_isolation_map[] =
+ {
+ SQL_TXN_READ_UNCOMMITTED,
+ SQL_TXN_READ_COMMITTED,
+ SQL_TXN_REPEATABLE_READ,
+ SQL_TXN_SS_SNAPSHOT,
+ SQL_TXN_SERIALIZABLE
+ };
+
+ connection::
+ connection (connection_factory& cf)
+ : odb::connection (cf),
+ state_ (state_disconnected),
+ statement_cache_ (new statement_cache_type (*this)),
+ long_data_buffer_ (0)
+ {
+ SQLRETURN r;
+
+ database_type& db (database ());
+
+ // Allocate the connection handle.
+ //
+ {
+ SQLHANDLE h;
+ r = SQLAllocHandle (SQL_HANDLE_DBC, db.environment (), &h);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, db.environment (), SQL_HANDLE_ENV);
+
+ handle_.reset (h);
+ }
+
+ // Set the manual commit mode.
+ //
+ r = SQLSetConnectAttrA (handle_,
+ SQL_ATTR_AUTOCOMMIT,
+ (SQLPOINTER) SQL_AUTOCOMMIT_OFF,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ // Still use the handle version of translate_error since there
+ // is no connection yet.
+ //
+ translate_error (r, handle_, SQL_HANDLE_DBC);
+
+ // Enable Multiple Active Result Sets (MARS).
+ //
+ r = SQLSetConnectAttrA (handle_,
+ SQL_COPT_SS_MARS_ENABLED,
+ (SQLPOINTER) SQL_MARS_ENABLED_YES,
+ SQL_IS_UINTEGER);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, handle_, SQL_HANDLE_DBC);
+
+ // Set transaction isolation level.
+ //
+ transaction_isolation ti (db.transaction_isolation ());
+ switch (ti)
+ {
+ case isolation_read_committed:
+ {
+ break; // SQL Server default.
+ }
+ case isolation_read_uncommitted:
+ case isolation_repeatable_read:
+ case isolation_serializable:
+ {
+ r = SQLSetConnectAttrA (handle_,
+ SQL_ATTR_TXN_ISOLATION,
+ (SQLPOINTER) transaction_isolation_map[ti],
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, handle_, SQL_HANDLE_DBC);
+ break;
+ }
+ case isolation_snapshot:
+ {
+ r = SQLSetConnectAttrA (handle_,
+ SQL_COPT_SS_TXN_ISOLATION,
+ (SQLPOINTER) transaction_isolation_map[ti],
+ SQL_IS_INTEGER);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, handle_, SQL_HANDLE_DBC);
+ break;
+ }
+ }
+
+ // Connect.
+ //
+ {
+ SQLSMALLINT out_conn_str_size;
+ r = SQLDriverConnectA (handle_,
+ 0, // Parent window handle.
+ (SQLCHAR*) db.connect_string ().c_str (),
+ SQL_NTS,
+ 0, // Output connection string buffer.
+ 0, // Size of output connection string buffer.
+ &out_conn_str_size,
+ SQL_DRIVER_NOPROMPT);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, handle_, SQL_HANDLE_DBC);
+
+ state_ = state_connected;
+ }
+
+ // If an exception is thrown after this line, we will not disconnect
+ // the connection.
+ //
+ }
+
+ connection::
+ connection (connection_factory& cf, SQLHDBC handle)
+ : odb::connection (cf),
+ handle_ (handle),
+ state_ (state_connected),
+ statement_cache_ (new statement_cache_type (*this)),
+ long_data_buffer_ (0)
+ {
+ }
+
+ connection::
+ ~connection ()
+ {
+ // Deallocate prepared statements before we close the connection.
+ //
+ recycle ();
+ clear_prepared_map ();
+ statement_cache_.reset ();
+ direct_stmt_.reset ();
+
+ if (state_ != state_disconnected)
+ SQLDisconnect (handle_); // Ignore any errors.
+ }
+
+ transaction_impl* connection::
+ begin ()
+ {
+ return new transaction_impl (connection_ptr (inc_ref (this)));
+ }
+
+ unsigned long long connection::
+ execute (const char* s, std::size_t n)
+ {
+ {
+ odb::tracer* t;
+ if ((t = transaction_tracer ()) ||
+ (t = tracer ()) ||
+ (t = database ().tracer ()))
+ {
+ string str (s, n);
+ t->execute (*this, str.c_str ());
+ }
+ }
+
+ SQLRETURN r;
+
+ // Allocate the statement if necessary.
+ //
+ if (direct_stmt_ == 0)
+ {
+ SQLHANDLE h;
+ r = SQLAllocHandle (SQL_HANDLE_STMT, handle_, &h);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this);
+
+ direct_stmt_.reset (h);
+
+ // Disable escape sequences.
+ //
+ r = SQLSetStmtAttr (direct_stmt_,
+ SQL_ATTR_NOSCAN,
+ (SQLPOINTER) SQL_NOSCAN_OFF,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this, direct_stmt_);
+
+ // Disable data retrieval for SELECT statements.
+ //
+ r = SQLSetStmtAttr (direct_stmt_,
+ SQL_ATTR_RETRIEVE_DATA,
+ (SQLPOINTER) SQL_RD_OFF,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this, direct_stmt_);
+ }
+
+ // Execute.
+ //
+ r = SQLExecDirectA (direct_stmt_, (SQLCHAR*) s, (SQLINTEGER) n);
+
+ // SQL_NO_DATA indicates that a DML statement hasn't affected
+ // any rows.
+ //
+ if (r == SQL_NO_DATA)
+ return 0;
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this, direct_stmt_);
+
+ // Get the number of affected/returned rows.
+ //
+ SQLLEN rows;
+
+ // See if this is a select statement.
+ //
+ SQLSMALLINT cols;
+ r = SQLNumResultCols (direct_stmt_, &cols);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this, direct_stmt_);
+
+ if (cols != 0)
+ {
+ for (rows = 0;; ++rows)
+ {
+ r = SQLFetch (direct_stmt_);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this, direct_stmt_);
+ }
+
+ r = SQLCloseCursor (direct_stmt_);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this, direct_stmt_);
+ }
+ else
+ {
+ r = SQLRowCount (direct_stmt_, &rows);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *this, direct_stmt_);
+
+ // -1 means the row count is not available. In particular, the
+ // Native Client ODBC driver returns this value for DDL statements.
+ //
+ if (rows == -1)
+ rows = 0;
+ }
+
+ return static_cast<unsigned long long> (rows);
+ }
+
+ // connection_factory
+ //
+ connection_factory::
+ ~connection_factory ()
+ {
+ }
+
+ void connection_factory::
+ database (database_type& db)
+ {
+ odb::connection_factory::db_ = &db;
+ db_ = &db;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/connection.hxx b/libodb-mssql/odb/mssql/connection.hxx
new file mode 100644
index 0000000..204d37e
--- /dev/null
+++ b/libodb-mssql/odb/mssql/connection.hxx
@@ -0,0 +1,187 @@
+// file : odb/mssql/connection.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_CONNECTION_HXX
+#define ODB_MSSQL_CONNECTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/connection.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/unique-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/query.hxx>
+#include <odb/mssql/tracer.hxx>
+#include <odb/mssql/transaction-impl.hxx>
+#include <odb/mssql/auto-handle.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class statement_cache;
+ class connection_factory;
+
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+
+ class LIBODB_MSSQL_EXPORT connection: public odb::connection
+ {
+ public:
+ typedef mssql::statement_cache statement_cache_type;
+ typedef mssql::database database_type;
+
+ virtual
+ ~connection ();
+
+ connection (connection_factory&);
+ connection (connection_factory&, SQLHDBC handle);
+
+ database_type&
+ database ();
+
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ public:
+ using odb::connection::execute;
+
+ virtual unsigned long long
+ execute (const char* statement, std::size_t length);
+
+ // Query preparation.
+ //
+ public:
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const mssql::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef mssql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ using odb::connection::tracer;
+
+ public:
+ bool
+ failed () const
+ {
+ return state_ == state_failed;
+ }
+
+ void
+ mark_failed ()
+ {
+ state_ = state_failed;
+ }
+
+ public:
+ SQLHDBC
+ handle ()
+ {
+ return handle_;
+ }
+
+ statement_cache_type&
+ statement_cache ()
+ {
+ return *statement_cache_;
+ }
+
+ details::buffer&
+ long_data_buffer ()
+ {
+ return long_data_buffer_;
+ }
+
+ private:
+ connection (const connection&);
+ connection& operator= (const connection&);
+
+ private:
+ friend class transaction_impl; // invalidate_results()
+
+ private:
+ auto_handle<SQL_HANDLE_DBC> handle_;
+
+ enum
+ {
+ state_disconnected,
+ state_connected,
+ state_failed
+ } state_;
+
+ // Statement handle for direct execution.
+ //
+ auto_handle<SQL_HANDLE_STMT> direct_stmt_;
+ details::unique_ptr<statement_cache_type> statement_cache_;
+ details::buffer long_data_buffer_;
+ };
+
+ class LIBODB_MSSQL_EXPORT connection_factory:
+ public odb::connection_factory
+ {
+ public:
+ typedef mssql::database database_type;
+
+ virtual void
+ database (database_type&);
+
+ database_type&
+ database () {return *db_;}
+
+ virtual connection_ptr
+ connect () = 0;
+
+ virtual
+ ~connection_factory ();
+
+ connection_factory (): db_ (0) {}
+
+ // Needed to break the circular connection_factory-database dependency
+ // (odb::connection_factory has the odb::database member).
+ //
+ protected:
+ database_type* db_;
+ };
+ }
+}
+
+#include <odb/mssql/connection.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_CONNECTION_HXX
diff --git a/libodb-mssql/odb/mssql/connection.ixx b/libodb-mssql/odb/mssql/connection.ixx
new file mode 100644
index 0000000..8ec8294
--- /dev/null
+++ b/libodb-mssql/odb/mssql/connection.ixx
@@ -0,0 +1,44 @@
+// file : odb/mssql/connection.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mssql
+ {
+ inline database& connection::
+ database ()
+ {
+ return static_cast<connection_factory&> (factory_).database ();
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const mssql::query_base& q)
+ {
+ return query_<T, id_mssql>::call (*this, n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, mssql::query_base (q));
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/container-statements.hxx b/libodb-mssql/odb/mssql/container-statements.hxx
new file mode 100644
index 0000000..16f4bf9
--- /dev/null
+++ b/libodb-mssql/odb/mssql/container-statements.hxx
@@ -0,0 +1,353 @@
+// file : odb/mssql/container-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_CONTAINER_STATEMENTS_HXX
+#define ODB_MSSQL_CONTAINER_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/mssql-types.hxx>
+#include <odb/mssql/statement.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class connection;
+
+ // Template argument is the generated abstract container traits type.
+ // That is, it doesn't need to provide column counts and statements.
+ //
+ template <typename T>
+ class container_statements
+ {
+ public:
+ typedef T traits;
+
+ typedef typename traits::data_image_type data_image_type;
+ typedef typename traits::functions_type functions_type;
+
+ typedef mssql::insert_statement insert_statement_type;
+ typedef mssql::select_statement select_statement_type;
+ typedef mssql::delete_statement delete_statement_type;
+
+ typedef mssql::connection connection_type;
+
+ container_statements (connection_type&, binding& id_binding);
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Functions.
+ //
+ functions_type&
+ functions ()
+ {
+ return functions_;
+ }
+
+ // Schema version.
+ //
+ const schema_version_migration&
+ version_migration () const {return *svm_;}
+
+ void
+ version_migration (const schema_version_migration& svm) {svm_ = &svm;}
+
+ // Id image binding (external).
+ //
+ const binding&
+ id_binding ()
+ {
+ return id_binding_;
+ }
+
+ // Data image. The image is split into the id (that comes as a
+ // binding) and index/key plus value which are in data_image_type.
+ // The select binding is a subset of the full binding (no id).
+ //
+ data_image_type&
+ data_image ()
+ {
+ return data_image_;
+ }
+
+ bind*
+ data_bind ()
+ {
+ return insert_image_binding_.bind;
+ }
+
+ bool
+ data_binding_test_version () const
+ {
+ return data_id_binding_version_ != id_binding_.version ||
+ data_image_version_ != data_image_.version ||
+ insert_image_binding_.version == 0;
+ }
+
+ void
+ data_binding_update_version ()
+ {
+ data_id_binding_version_ = id_binding_.version;
+ data_image_version_ = data_image_.version;
+ insert_image_binding_.version++;
+ select_image_binding_.version++;
+ }
+
+ //
+ // Statements.
+ //
+
+ insert_statement_type&
+ insert_statement ()
+ {
+ if (insert_ == 0)
+ insert_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ insert_text_,
+ versioned_, // Process if versioned.
+ insert_image_binding_,
+ false,
+ false,
+ 0,
+ false));
+
+ return *insert_;
+ }
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ select_text_,
+ versioned_, // Process if versioned.
+ false, // Don't optimize.
+ id_binding_,
+ select_image_binding_,
+ false));
+
+ return *select_;
+ }
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (delete_ == 0)
+ delete_.reset (
+ new (details::shared) delete_statement_type (
+ conn_, delete_text_, id_binding_, false));
+
+ return *delete_;
+ }
+
+ private:
+ container_statements (const container_statements&);
+ container_statements& operator= (const container_statements&);
+
+ protected:
+ connection_type& conn_;
+ binding& id_binding_;
+
+ functions_type functions_;
+
+ data_image_type data_image_;
+ std::size_t data_image_version_;
+ std::size_t data_id_binding_version_;
+
+ binding insert_image_binding_;
+ binding select_image_binding_;
+
+ const char* insert_text_;
+ const char* select_text_;
+ const char* delete_text_;
+
+ bool versioned_;
+ const schema_version_migration* svm_;
+
+ details::shared_ptr<insert_statement_type> insert_;
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<delete_statement_type> delete_;
+ };
+
+ template <typename T>
+ class smart_container_statements: public container_statements<T>
+ {
+ public:
+ typedef T traits;
+ typedef typename traits::cond_image_type cond_image_type;
+
+ typedef mssql::update_statement update_statement_type;
+ typedef mssql::delete_statement delete_statement_type;
+
+ typedef mssql::connection connection_type;
+
+ smart_container_statements (connection_type&, binding& id_binding);
+
+ // Condition image. The image is split into the id (that comes as
+ // a binding) and index/key/value which is in cond_image_type.
+ //
+ cond_image_type&
+ cond_image ()
+ {
+ return cond_image_;
+ }
+
+ bind*
+ cond_bind ()
+ {
+ return cond_image_binding_.bind;
+ }
+
+ bool
+ cond_binding_test_version () const
+ {
+ return cond_id_binding_version_ != this->id_binding_.version ||
+ cond_image_version_ != cond_image_.version ||
+ cond_image_binding_.version == 0;
+ }
+
+ void
+ cond_binding_update_version ()
+ {
+ cond_id_binding_version_ = this->id_binding_.version;
+ cond_image_version_ = cond_image_.version;
+ cond_image_binding_.version++;
+ }
+
+ // Update image. The image is split as follows: value comes
+ // from the data image, id comes as binding, and index/key
+ // comes from the condition image.
+ //
+ bind*
+ update_bind ()
+ {
+ return update_image_binding_.bind;
+ }
+
+ bool
+ update_binding_test_version () const
+ {
+ return update_id_binding_version_ != this->id_binding_.version ||
+ update_cond_image_version_ != cond_image_.version ||
+ update_data_image_version_ != this->data_image_.version ||
+ update_image_binding_.version == 0;
+ }
+
+ void
+ update_binding_update_version ()
+ {
+ update_id_binding_version_ = this->id_binding_.version;
+ update_cond_image_version_ = cond_image_.version;
+ update_data_image_version_ = this->data_image_.version;
+ update_image_binding_.version++;
+ }
+
+ //
+ // Statements.
+ //
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (this->delete_ == 0)
+ this->delete_.reset (
+ new (details::shared) delete_statement_type (
+ this->conn_,
+ this->delete_text_,
+ this->cond_image_binding_,
+ false));
+
+ return *this->delete_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ this->conn_,
+ update_text_,
+ this->versioned_, // Process if versioned.
+ update_image_binding_,
+ 0,
+ false));
+
+ return *update_;
+ }
+
+ protected:
+ cond_image_type cond_image_;
+ std::size_t cond_image_version_;
+ std::size_t cond_id_binding_version_;
+ binding cond_image_binding_;
+
+ std::size_t update_id_binding_version_;
+ std::size_t update_cond_image_version_;
+ std::size_t update_data_image_version_;
+ binding update_image_binding_;
+
+ const char* update_text_;
+
+ details::shared_ptr<update_statement_type> update_;
+ };
+
+ // Template argument is the generated concrete container traits type.
+ //
+ template <typename T>
+ class container_statements_impl: public T::statements_type
+ {
+ public:
+ typedef T traits;
+ typedef typename T::statements_type base;
+ typedef mssql::connection connection_type;
+
+ container_statements_impl (connection_type&, binding&);
+
+ private:
+ container_statements_impl (const container_statements_impl&);
+ container_statements_impl& operator= (const container_statements_impl&);
+
+ private:
+ bind data_image_bind_[traits::data_column_count];
+ };
+
+ template <typename T>
+ class smart_container_statements_impl: public container_statements_impl<T>
+ {
+ public:
+ typedef T traits;
+ typedef mssql::connection connection_type;
+
+ smart_container_statements_impl (connection_type&, binding&);
+
+ private:
+ bind cond_image_bind_[traits::cond_column_count];
+ bind update_image_bind_[traits::value_column_count +
+ traits::cond_column_count];
+ };
+ }
+}
+
+#include <odb/mssql/container-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_CONTAINER_STATEMENTS_HXX
diff --git a/libodb-mssql/odb/mssql/container-statements.txx b/libodb-mssql/odb/mssql/container-statements.txx
new file mode 100644
index 0000000..6a45086
--- /dev/null
+++ b/libodb-mssql/odb/mssql/container-statements.txx
@@ -0,0 +1,96 @@
+// file : odb/mssql/container-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mssql
+ {
+ // container_statements
+ //
+ template <typename T>
+ container_statements<T>::
+ container_statements (connection_type& conn, binding& id)
+ : conn_ (conn),
+ id_binding_ (id),
+ functions_ (this),
+ insert_image_binding_ (0, 0), // Initialized by impl.
+ select_image_binding_ (0, 0), // Initialized by impl.
+ svm_ (0)
+ {
+ functions_.insert_ = &traits::insert;
+ functions_.select_ = &traits::select;
+ functions_.delete__ = &traits::delete_;
+
+ data_image_.version = 0;
+ data_image_version_ = 0;
+ data_id_binding_version_ = 0;
+ }
+
+ // smart_container_statements
+ //
+ template <typename T>
+ smart_container_statements<T>::
+ smart_container_statements (connection_type& conn, binding& id)
+ : container_statements<T> (conn, id),
+ cond_image_binding_ (0, 0), // Initialized by impl.
+ update_image_binding_ (0, 0) // Initialized by impl.
+ {
+ this->functions_.update_ = &traits::update;
+
+ cond_image_.version = 0;
+ cond_image_version_ = 0;
+ cond_id_binding_version_ = 0;
+
+ update_id_binding_version_ = 0;
+ update_cond_image_version_ = 0;
+ update_data_image_version_ = 0;
+ }
+
+ // container_statements_impl
+ //
+ template <typename T>
+ container_statements_impl<T>::
+ container_statements_impl (connection_type& conn, binding& id)
+ : base (conn, id)
+ {
+ this->insert_image_binding_.bind = data_image_bind_;
+ this->insert_image_binding_.count = traits::data_column_count;
+
+ this->select_image_binding_.bind = data_image_bind_ +
+ traits::id_column_count;
+ this->select_image_binding_.count = traits::data_column_count -
+ traits::id_column_count;
+
+ std::memset (data_image_bind_, 0, sizeof (data_image_bind_));
+
+ this->insert_text_ = traits::insert_statement;
+ this->select_text_ = traits::select_statement;
+ this->delete_text_ = traits::delete_statement;
+
+ this->versioned_ = traits::versioned;
+ }
+
+ // smart_container_statements_impl
+ //
+ template <typename T>
+ smart_container_statements_impl<T>::
+ smart_container_statements_impl (connection_type& conn, binding& id)
+ : container_statements_impl<T> (conn, id)
+ {
+ this->cond_image_binding_.bind = cond_image_bind_;
+ this->cond_image_binding_.count = traits::cond_column_count;
+
+ this->update_image_binding_.bind = update_image_bind_;
+ this->update_image_binding_.count = traits::value_column_count +
+ traits::cond_column_count;
+
+ std::memset (cond_image_bind_, 0, sizeof (cond_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+ this->update_text_ = traits::update_statement;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/database.cxx b/libodb-mssql/odb/mssql/database.cxx
new file mode 100644
index 0000000..6e68bcb
--- /dev/null
+++ b/libodb-mssql/odb/mssql/database.cxx
@@ -0,0 +1,580 @@
+// file : odb/mssql/database.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::strcmp, std::strncmp
+#include <sstream>
+
+#include <odb/mssql/mssql.hxx>
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/transaction.hxx>
+#include <odb/mssql/exceptions.hxx>
+#include <odb/mssql/error.hxx>
+
+#include <odb/mssql/details/options.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ using odb::details::transfer_ptr;
+
+ database::
+ database (const string& user,
+ const string& password,
+ const string& db,
+ const string& server,
+ const string& driver,
+ const string& extra_connect_string,
+ transaction_isolation_type transaction_isolation,
+ SQLHENV environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mssql),
+ user_ (user),
+ password_ (password),
+ db_ (db),
+ protocol_ (protocol_auto),
+ port_ (0),
+ server_ (server),
+ driver_ (driver),
+ extra_connect_string_ (extra_connect_string),
+ transaction_isolation_ (transaction_isolation),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ init ();
+ }
+
+ database::
+ database (const string& user,
+ const string& password,
+ const string& db,
+ protocol_type protocol,
+ const string& host,
+ const string& instance,
+ const string& driver,
+ const string& extra_connect_string,
+ transaction_isolation_type transaction_isolation,
+ SQLHENV environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mssql),
+ user_ (user),
+ password_ (password),
+ db_ (db),
+ protocol_ (protocol),
+ host_ (host),
+ instance_ (instance),
+ port_ (0),
+ driver_ (driver),
+ extra_connect_string_ (extra_connect_string),
+ transaction_isolation_ (transaction_isolation),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ init ();
+ }
+
+ database::
+ database (const string& user,
+ const string& password,
+ const string& db,
+ const string& host,
+ unsigned int port,
+ const string& driver,
+ const string& extra_connect_string,
+ transaction_isolation_type transaction_isolation,
+ SQLHENV environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mssql),
+ user_ (user),
+ password_ (password),
+ db_ (db),
+ protocol_ (protocol_tcp),
+ host_ (host),
+ port_ (port),
+ driver_ (driver),
+ extra_connect_string_ (extra_connect_string),
+ transaction_isolation_ (transaction_isolation),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ init ();
+ }
+
+ database::
+ database (const string& connect_string,
+ transaction_isolation_type transaction_isolation,
+ SQLHENV environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mssql),
+ protocol_ (protocol_auto),
+ port_ (0),
+ transaction_isolation_ (transaction_isolation),
+ connect_string_ (connect_string),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ init ();
+ }
+
+ database::
+ database (int& argc,
+ char* argv[],
+ bool erase,
+ const string& extra_connect_string,
+ transaction_isolation_type transaction_isolation,
+ SQLHENV environment,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mssql),
+ protocol_ (protocol_auto),
+ port_ (0),
+ extra_connect_string_ (extra_connect_string),
+ transaction_isolation_ (transaction_isolation),
+ environment_ (environment),
+ factory_ (factory.transfer ())
+ {
+ using namespace details;
+
+ try
+ {
+ cli::argv_file_scanner scan (argc, argv, "--options-file", erase);
+ options ops (scan, cli::unknown_mode::skip, cli::unknown_mode::skip);
+
+ user_ = ops.user ();
+ password_ = ops.password ();
+ db_ = ops.database ();
+ server_ = ops.server ();
+ driver_ = ops.driver ();
+ }
+ catch (const cli::exception& e)
+ {
+ ostringstream oss;
+ oss << e;
+ throw cli_exception (oss.str ());
+ }
+
+ init ();
+ }
+
+ /*
+
+ NOTE: This code hasn't been tested.
+
+ void database::
+ parse ()
+ {
+ // Parse the server string and extract individual parts (protocol,
+ // host, instance, and port).
+ //
+ string port;
+
+ if (server_.compare (0, 4, "lpc:") == 0)
+ {
+ // lpc:<host>[\<instance>]
+ //
+ protocol_ = protocol_shm;
+ string::size_type p (server_.find (4, '\\'));
+
+ if (p == string::npos)
+ host_.assign (server_, 4, string::npos);
+ else
+ {
+ host_.assign (server_, 4, p - 4);
+ instance_.assign (server_, p + 1, string::npos);
+ }
+ }
+ else if (server_.compare (0, 3, "np:") == 0)
+ {
+ // np:<host>\pipe\[MSSQL$<instance>\]sql\query
+ //
+ protocol_ = protocol_pipe;
+
+ string::size_type p (server_.find (3, '\\'));
+
+ if (p != string::npos)
+ {
+ host_.assign (server_, 3, p - 3);
+
+ p = server_.find (p + 1, '$');
+
+ if (p != string::npos)
+ {
+ p++;
+ instance_.assign (server_, p, server_.find (p, '\\') - p);
+ }
+ }
+ }
+ else
+ {
+ // <host>[\<instance>][,<port>]
+ // tcp:<host>[\<instance>][,<port>]
+ //
+ string::size_type p1 (0), p2;
+
+ if (server_.compare (0, 4, "tcp:") == 0)
+ {
+ protocol_ = protocol_tcp;
+ p1 = 4;
+ }
+
+ p2 = server_.find (p1, '\\');
+
+ if (p2 == string::npos)
+ {
+ p2 = server_.find (p1, ',');
+
+ if (p2 == string::npos)
+ host_.assign (server_, p1, string::npos);
+ else
+ {
+ host_.assign (server_, 4, p2 - p1);
+ port.assign (server_, p2 + 1, string::npos);
+ }
+ }
+ else
+ {
+ host_.assign (server_, 4, p2 - p1);
+
+ p1 = server_.find (p2 + 1, ',');
+
+ if (p1 == string::npos)
+ instance_.assign (server_, p2 + 1, string::npos);
+ else
+ {
+ instance_.assign (server_, p2 + 1, p1 - p2 - 1);
+ port.assign (server_, p1 + 1, string::npos);
+ }
+ }
+ }
+
+ if (!port.empty ())
+ {
+ istringstream is (port);
+ is >> port;
+ protocol_ = protocol_tcp;
+ }
+ }
+ */
+
+ void database::
+ init ()
+ {
+ SQLRETURN r;
+
+ if (environment_ == 0)
+ {
+ r = SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &environment_);
+
+ if (!SQL_SUCCEEDED (r))
+ throw database_exception (
+ 0, "?????", "unable to allocate environment handle");
+
+ auto_environment_.reset (environment_);
+
+ // Set ODBC version.
+ //
+ r = SQLSetEnvAttr (environment_,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, environment_, SQL_HANDLE_ENV);
+ }
+
+ // Build the connection string.
+ //
+ if (connect_string_.empty ())
+ {
+ // Find the driver.
+ //
+ if (driver_.empty ())
+ {
+ for (bool first (true);; )
+ {
+ char desc[256];
+ SQLSMALLINT desc_size, attr_size;
+
+ r = SQLDriversA (environment_,
+ first ? SQL_FETCH_FIRST : SQL_FETCH_NEXT,
+ (SQLCHAR*) desc,
+ sizeof (desc),
+ &desc_size,
+ 0,
+ 0,
+ &attr_size);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (!SQL_SUCCEEDED (r))
+ translate_error (r, environment_, SQL_HANDLE_ENV);
+
+ // Native Client 9.0 (first version).
+ //
+ if (strcmp (desc, "SQL Native Client") == 0 ||
+ strncmp (desc, "SQL Server Native Client", 24) == 0)
+ {
+ // Compare driver strings lexicographically. Provided that
+ // Microsoft keeps its naming consistent, we will get the
+ // correct result. For example, "SQL Server Native Client
+ // 10.0" (SQL Server 2008) will be greater than "SQL Native
+ // Client" (SQL Server 2005). Similarly, "SQL Server Native
+ // Client 11.0" (SQL Server 2012) will be preferred over
+ // "SQL Server Native Client 10.0" (SQL Server 2008).
+ //
+ if (desc > driver_)
+ driver_ = desc;
+ }
+
+ if (first)
+ first = false;
+ }
+ }
+
+ connect_string_ += "DRIVER={";
+ connect_string_ += driver_;
+ connect_string_ += "};";
+
+ // If necessary, assemble the server address string, depending
+ // on which protocol we are using.
+ if (server_.empty ())
+ {
+ switch (protocol_)
+ {
+ case protocol_auto:
+ {
+ server_ = (host_.empty () ? "localhost" : host_.c_str ());
+
+ if (!instance_.empty ())
+ {
+ server_ += '\\';
+ server_ += instance_;
+ }
+
+ break;
+ }
+ case protocol_tcp:
+ {
+ server_ = "tcp:";
+ server_ += (host_.empty () ? "localhost" : host_.c_str ());
+
+ // Port seems to take precedence over instance. For example,
+ // if you specify both, and the instance name is invalid, the
+ // Native Client driver still connects without any problems.
+ //
+ if (port_ != 0)
+ {
+ ostringstream os;
+ os << port_;
+ server_ += ',';
+ server_ += os.str ();
+ }
+ else if (!instance_.empty ())
+ {
+ server_ += '\\';
+ server_ += instance_;
+ }
+
+ break;
+ }
+ case protocol_lpc:
+ {
+ server_ = "lpc:";
+ server_ += (host_.empty () ? "localhost" : host_.c_str ());
+
+ if (!instance_.empty ())
+ {
+ server_ += '\\';
+ server_ += instance_;
+ }
+
+ break;
+ }
+ case protocol_np:
+ {
+ server_ = "np:\\\\";
+ server_ += (host_.empty () ? "." : host_.c_str ());
+ server_ += "\\pipe\\";
+
+ if (!instance_.empty ())
+ {
+ server_ += "MSSQL$";
+ server_ += instance_;
+ server_ += '\\';
+ }
+
+ server_ += "sql\\query";
+ break;
+ }
+ }
+ }
+
+ // The Address attribute seems to be preferred over SERVER. However,
+ // SQL Server 2005 Native Client only seem to support Address since
+ // SP1. Since we don't know the exact driver version, for now always
+ // use SERVER with SQL Server 2005 driver.
+ //
+ connect_string_ += (driver_ == "SQL Native Client"
+ ? "SERVER={"
+ : "Address={");
+
+ connect_string_ += server_;
+ connect_string_ += "};";
+
+ // Add login information.
+ //
+ if (user_.empty ())
+ // Windows authentication.
+ //
+ connect_string_ += "Trusted_Connection=yes;";
+ else
+ {
+ connect_string_ += "UID={";
+ connect_string_ += user_;
+ connect_string_ += "};";
+
+ if (!password_.empty ())
+ {
+ connect_string_ += "PWD={";
+ connect_string_ += password_;
+ connect_string_ += "};";
+ }
+ }
+
+ // Add database.
+ //
+ if (!db_.empty ())
+ {
+ connect_string_ += "Database={";
+ connect_string_ += db_;
+ connect_string_ += "};";
+ }
+
+ // Add any extra connection attributes.
+ //
+ if (!extra_connect_string_.empty ())
+ connect_string_ += extra_connect_string_;
+ }
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ void database::
+ print_usage (ostream& os)
+ {
+ details::options::print_usage (os);
+ }
+
+ database::
+ ~database ()
+ {
+ }
+
+ transaction_impl* database::
+ begin ()
+ {
+ return new transaction_impl (*this);
+ }
+
+ odb::connection* database::
+ connection_ ()
+ {
+ connection_ptr c (factory_->connect ());
+ return c.release ();
+ }
+
+ const database::schema_version_info& database::
+ load_schema_version (const string& name) const
+ {
+ schema_version_info& svi (schema_version_map_[name]);
+
+ // Construct the SELECT statement text.
+ //
+ string text ("SELECT [version], [migration] FROM ");
+
+ if (!svi.version_table.empty ())
+ text += svi.version_table; // Already quoted.
+ else if (!schema_version_table_.empty ())
+ text += schema_version_table_; // Already quoted.
+ else
+ text += "[schema_version]";
+
+ text += " WHERE [name] = ?";
+
+ // Bind parameters and results.
+ //
+ SQLLEN psize[1] = {static_cast<SQLLEN> (name.size ())};
+ bind pbind[1] = {
+ {bind::string, const_cast<char*> (name.c_str ()), &psize[0], 0}};
+ binding param (pbind, 1);
+ param.version++;
+
+ signed char migration;
+ SQLLEN rsize[2];
+ bind rbind[2] = {{bind::bigint, &svi.version, &rsize[0], 0},
+ {bind::bit, &migration, &rsize[1], 0}};
+ binding result (rbind, 2);
+ result.version++;
+
+ // If we are not in transaction, start one.
+ //
+ transaction t;
+ if (!transaction::has_current ())
+ t.reset (factory_->connect ()->begin (), false);
+
+ mssql::connection& c (
+ t.finalized ()
+ ? transaction::current ().connection (const_cast<database&> (*this))
+ : t.connection (const_cast<database&> (*this)));
+
+ try
+ {
+ select_statement st (c,
+ text.c_str (),
+ false, // Don't process.
+ false, // Don't optimize.
+ param,
+ result,
+ false);
+
+ st.execute ();
+ auto_result ar (st);
+
+ switch (st.fetch ())
+ {
+ case select_statement::success:
+ {
+ svi.migration = migration != 0;
+ assert (st.fetch () == select_statement::no_data);
+ break;
+ }
+ case select_statement::no_data:
+ {
+ svi.version = 0; // No schema.
+ break;
+ }
+ }
+ }
+ catch (const database_exception& e)
+ {
+ // Detect the case where there is no version table. The SQL Server-
+ // specific error code (208) seems to be too generic.
+ //
+ if (e.begin ()->sqlstate () == "42S02")
+ svi.version = 0; // No schema.
+ else
+ throw;
+ }
+
+ if (!t.finalized ())
+ t.commit ();
+
+ return svi;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/database.hxx b/libodb-mssql/odb/mssql/database.hxx
new file mode 100644
index 0000000..5367bc5
--- /dev/null
+++ b/libodb-mssql/odb/mssql/database.hxx
@@ -0,0 +1,629 @@
+// file : odb/mssql/database.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_DATABASE_HXX
+#define ODB_MSSQL_DATABASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <iosfwd> // std::ostream
+
+#include <odb/database.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/transfer-ptr.hxx>
+
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/query.hxx>
+#include <odb/mssql/tracer.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/connection-factory.hxx>
+#include <odb/mssql/auto-handle.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class transaction_impl;
+
+ enum protocol
+ {
+ protocol_auto,
+ protocol_tcp, // TCP/IP.
+ protocol_lpc, // Shared memory (local procedure call).
+ protocol_np // Named pipes.
+ };
+
+ enum transaction_isolation
+ {
+ isolation_read_uncommitted,
+ isolation_read_committed, // SQL Server default.
+ isolation_repeatable_read,
+ isolation_snapshot,
+ isolation_serializable
+ };
+
+ class LIBODB_MSSQL_EXPORT database: public odb::database
+ {
+ public:
+ typedef mssql::protocol protocol_type;
+ typedef mssql::transaction_isolation transaction_isolation_type;
+
+ // Connect to the specified server using the latest available SQL
+ // Server Native Client ODBC driver by default. If user is empty,
+ // then use Windows authentication. If db is empty, then use the
+ // default database for this user.
+ //
+ database (const std::string& user,
+ const std::string& password,
+ const std::string& db,
+ const std::string& server,
+ const std::string& driver = "",
+ const std::string& extra_connect_string = "",
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // By default connect to the default instance on localhost using
+ // default protocol and the latest available SQL Server Native
+ // Client ODBC driver. If user is empty, then use Windows
+ // authentication. If db is empty, then use the default database
+ // for this user.
+ //
+ database (const std::string& user,
+ const std::string& password,
+ const std::string& db,
+ protocol_type protocol = protocol_auto,
+ const std::string& host = "",
+ const std::string& instance = "",
+ const std::string& driver = "",
+ const std::string& extra_connect_string = "",
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Connect using TCP/IP to the specified host and port. If port is
+ // 0, use the default port (1433).
+ //
+ database (const std::string& user,
+ const std::string& password,
+ const std::string& db,
+ const std::string& host,
+ unsigned int port,
+ const std::string& driver = "",
+ const std::string& extra_connect_string = "",
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Connect using a custom SQL Server Native Client ODBC driver
+ // conection string.
+ //
+ database (const std::string& connect_string,
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Extract the database parameters from the command line. The
+ // following options are recognized:
+ //
+ // --user | -U
+ // --password | -P
+ // --database | -d
+ // --server | -S
+ // --driver
+ // --options-file
+ //
+ // For more information, see the output of the print_usage() function
+ // below. If erase is true, the above options are removed from the
+ // argv array and the argc count is updated accordingly. This
+ // constructor may throw the cli_exception exception.
+ //
+ database (int& argc,
+ char* argv[],
+ bool erase = false,
+ const std::string& extra_connect_string = "",
+ transaction_isolation_type = isolation_read_committed,
+ SQLHENV environment = 0,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Move-constructible but not move-assignable.
+ //
+ // Note: noexcept is not specified since odb::database(odb::database&&)
+ // can throw.
+ //
+#ifdef ODB_CXX11
+ database (database&&);
+#endif
+
+ static void
+ print_usage (std::ostream&);
+
+ // Object persistence API.
+ //
+ public:
+
+ // Make the object persistent.
+ //
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk persist. Can be a range of references or pointers (including
+ // smart pointers) to objects.
+ //
+ template <typename I>
+ void
+ persist (I begin, I end, bool continue_failed = true);
+
+ // Load an object. Throw object_not_persistent if not found.
+ //
+ template <typename T>
+ typename object_traits<T>::pointer_type
+ load (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ void
+ load (const typename object_traits<T>::id_type& id, T& object);
+
+ // Load (or reload, if it is already loaded) a section of an object.
+ //
+ template <typename T>
+ void
+ load (T& object, section&);
+
+ // Reload an object.
+ //
+ template <typename T>
+ void
+ reload (T& object);
+
+ template <typename T>
+ void
+ reload (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ reload (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Loan an object if found. Return NULL/false if not found.
+ //
+ template <typename T>
+ typename object_traits<T>::pointer_type
+ find (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ bool
+ find (const typename object_traits<T>::id_type& id, T& object);
+
+ // Update the state of a modified objects.
+ //
+ template <typename T>
+ void
+ update (T& object);
+
+ template <typename T>
+ void
+ update (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ update (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk update. Can be a range of references or pointers (including
+ // smart pointers) to objects.
+ //
+ template <typename I>
+ void
+ update (I begin, I end, bool continue_failed = true);
+
+ // Update a section of an object. Throws the section_not_loaded
+ // exception if the section is not loaded. Note also that this
+ // function does not clear the changed flag if it is set.
+ //
+ template <typename T>
+ void
+ update (const T& object, const section&);
+
+ // Make the object transient. Throw object_not_persistent if not
+ // found.
+ //
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ void
+ erase (T& object);
+
+ template <typename T>
+ void
+ erase (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk erase.
+ //
+ template <typename T, typename I>
+ void
+ erase (I id_begin, I id_end, bool continue_failed = true);
+
+ // Can be a range of references or pointers (including smart pointers)
+ // to objects.
+ //
+ template <typename I>
+ void
+ erase (I obj_begin, I obj_end, bool continue_failed = true);
+
+ // Erase multiple objects matching a query predicate.
+ //
+ template <typename T>
+ unsigned long long
+ erase_query ();
+
+ template <typename T>
+ unsigned long long
+ erase_query (const char*);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const std::string&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const mssql::query_base&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const odb::query_base&);
+
+ // Query API.
+ //
+ template <typename T>
+ result<T>
+ query ();
+
+ template <typename T>
+ result<T>
+ query (const char*);
+
+ template <typename T>
+ result<T>
+ query (const std::string&);
+
+ template <typename T>
+ result<T>
+ query (const mssql::query_base&);
+
+ template <typename T>
+ result<T>
+ query (const odb::query_base&);
+
+ // Query one API.
+ //
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one ();
+
+ template <typename T>
+ bool
+ query_one (T& object);
+
+ template <typename T>
+ T
+ query_value ();
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const char*);
+
+ template <typename T>
+ bool
+ query_one (const char*, T& object);
+
+ template <typename T>
+ T
+ query_value (const char*);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const std::string&);
+
+ template <typename T>
+ bool
+ query_one (const std::string&, T& object);
+
+ template <typename T>
+ T
+ query_value (const std::string&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const mssql::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const mssql::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const mssql::query_base&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const odb::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const odb::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const odb::query_base&);
+
+ // Query preparation.
+ //
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const mssql::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // Transactions.
+ //
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ public:
+ connection_ptr
+ connection ();
+
+ public:
+ const std::string&
+ user () const
+ {
+ return user_;
+ }
+
+ const std::string&
+ password () const
+ {
+ return password_;
+ }
+
+ const std::string&
+ db () const
+ {
+ return db_;
+ }
+
+ protocol_type
+ protocol () const
+ {
+ return protocol_;
+ }
+
+ const std::string&
+ host () const
+ {
+ return host_;
+ }
+
+ const std::string&
+ instance () const
+ {
+ return instance_;
+ }
+
+ unsigned int
+ port () const
+ {
+ return port_;
+ }
+
+ const std::string&
+ server () const
+ {
+ return server_;
+ }
+
+ const std::string&
+ driver () const
+ {
+ return driver_;
+ }
+
+ const std::string&
+ extra_connect_string () const
+ {
+ return extra_connect_string_;
+ }
+
+ transaction_isolation_type
+ transaction_isolation () const
+ {
+ return transaction_isolation_;
+ }
+
+ const std::string&
+ connect_string () const
+ {
+ return connect_string_;
+ }
+
+ SQLHENV
+ environment ()
+ {
+ return environment_;
+ }
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef mssql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::database::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::database::tracer (t);
+ }
+
+ using odb::database::tracer;
+
+ // Database schema version.
+ //
+ protected:
+ virtual const schema_version_info&
+ load_schema_version (const std::string& schema_name) const;
+
+ public:
+ // Database id constant (useful for meta-programming).
+ //
+ static const odb::database_id database_id = id_mssql;
+
+ public:
+ virtual
+ ~database ();
+
+ protected:
+ virtual odb::connection*
+ connection_ ();
+
+ private:
+ void
+ init ();
+
+ private:
+ // Note: remember to update move ctor if adding any new members.
+ //
+ std::string user_;
+ std::string password_;
+ std::string db_;
+ protocol_type protocol_;
+ std::string host_;
+ std::string instance_;
+ unsigned int port_;
+ std::string server_;
+ std::string driver_;
+ std::string extra_connect_string_;
+ transaction_isolation_type transaction_isolation_;
+ std::string connect_string_;
+
+ auto_handle<SQL_HANDLE_ENV> auto_environment_;
+ SQLHENV environment_;
+
+ details::unique_ptr<connection_factory> factory_;
+ };
+ }
+}
+
+#include <odb/mssql/database.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_DATABASE_HXX
diff --git a/libodb-mssql/odb/mssql/database.ixx b/libodb-mssql/odb/mssql/database.ixx
new file mode 100644
index 0000000..ae1b83b
--- /dev/null
+++ b/libodb-mssql/odb/mssql/database.ixx
@@ -0,0 +1,644 @@
+// file : odb/mssql/database.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <utility> // move()
+
+#include <odb/mssql/transaction.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+#ifdef ODB_CXX11
+ inline database::
+ database (database&& db) // Has to be inline.
+ : odb::database (std::move (db)),
+ user_ (std::move (db.user_)),
+ password_ (std::move (db.password_)),
+ db_ (std::move (db.db_)),
+ protocol_ (db.protocol_),
+ host_ (std::move (db.host_)),
+ instance_ (std::move (db.instance_)),
+ port_ (db.port_),
+ server_ (std::move (db.server_)),
+ driver_ (std::move (db.driver_)),
+ extra_connect_string_ (std::move (db.extra_connect_string_)),
+ transaction_isolation_ (db.transaction_isolation_),
+ connect_string_ (std::move (db.connect_string_)),
+ auto_environment_ (std::move (db.auto_environment_)),
+ environment_ (db.environment_),
+ factory_ (std::move (db.factory_))
+ {
+ factory_->database (*this); // New database instance.
+ }
+#endif
+
+ inline connection_ptr database::
+ connection ()
+ {
+ // Go through the virtual connection_() function instead of
+ // directly to allow overriding.
+ //
+ return connection_ptr (
+ static_cast<mssql::connection*> (connection_ ()));
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T& obj)
+ {
+ return persist_<T, id_mssql> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const T& obj)
+ {
+ return persist_<const T, id_mssql> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T>& p)
+ {
+ const P<T>& cr (p);
+ return persist<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ return persist<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const typename object_traits<T>::pointer_type& pobj)
+ {
+ return persist_<T, id_mssql> (pobj);
+ }
+
+ template <typename I>
+ inline void database::
+ persist (I b, I e, bool cont)
+ {
+ persist_<I, id_mssql> (b, e, cont);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ load (const typename object_traits<T>::id_type& id)
+ {
+ return load_<T, id_mssql> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ load (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return load_<T, id_mssql> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ load (T& obj, section& s)
+ {
+ return load_<T, id_mssql> (obj, s);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ find (const typename object_traits<T>::id_type& id)
+ {
+ return find_<T, id_mssql> (id);
+ }
+
+ template <typename T>
+ inline bool database::
+ find (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return find_<T, id_mssql> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T& obj)
+ {
+ reload_<T, id_mssql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T* p)
+ {
+ reload<T> (*p);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (const P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (const P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T>
+ inline void database::
+ reload (const typename object_traits<T>::pointer_type& pobj)
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ reload (odb::pointer_traits<pointer_type>::get_ref (pobj));
+ }
+
+ template <typename T>
+ inline void database::
+ update (T& obj)
+ {
+ update_<T, id_mssql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ update (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (P<T>& p)
+ {
+ const P<T>& cr (p);
+ update<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ update<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const typename object_traits<T>::pointer_type& pobj)
+ {
+ update_<T, id_mssql> (pobj);
+ }
+
+ template <typename I>
+ inline void database::
+ update (I b, I e, bool cont)
+ {
+ update_<I, id_mssql> (b, e, cont);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const T& obj, const section& s)
+ {
+ update_<T, id_mssql> (obj, s);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::id_type& id)
+ {
+ return erase_<T, id_mssql> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T& obj)
+ {
+ return erase_<T, id_mssql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (P<T>& p)
+ {
+ const P<T>& cr (p);
+ erase<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ erase<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::pointer_type& pobj)
+ {
+ erase_<T, id_mssql> (pobj);
+ }
+
+ template <typename T, typename I>
+ inline void database::
+ erase (I idb, I ide, bool cont)
+ {
+ erase_id_<I, T, id_mssql> (idb, ide, cont);
+ }
+
+ template <typename I>
+ inline void database::
+ erase (I ob, I oe, bool cont)
+ {
+ erase_object_<I, id_mssql> (ob, oe, cont);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query ()
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (mssql::query_base ());
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const char* q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const std::string& q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const mssql::query_base& q)
+ {
+ // T is always object_type.
+ //
+ return object_traits_impl<T, id_mssql>::erase_query (*this, q);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return erase_query<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query ()
+ {
+ return query<T> (mssql::query_base ());
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const char* q)
+ {
+ return query<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const std::string& q)
+ {
+ return query<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const mssql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_<T, id_mssql>::call (*this, q);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one ()
+ {
+ return query_one<T> (mssql::query_base ());
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (T& o)
+ {
+ return query_one<T> (mssql::query_base (), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value ()
+ {
+ return query_value<T> (mssql::query_base ());
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const char* q)
+ {
+ return query_one<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const char* q, T& o)
+ {
+ return query_one<T> (mssql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const char* q)
+ {
+ return query_value<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const std::string& q)
+ {
+ return query_one<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const std::string& q, T& o)
+ {
+ return query_one<T> (mssql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const std::string& q)
+ {
+ return query_value<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const mssql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_mssql> (q);
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const mssql::query_base& q, T& o)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_mssql> (q, o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const mssql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_value_<T, id_mssql> (q);
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const odb::query_base& q, T& o)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (mssql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_value<T> (mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, mssql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const mssql::query_base& q)
+ {
+ // Throws if not in transaction.
+ //
+ mssql::connection& c (transaction::current ().connection (*this));
+ return c.prepare_query<T> (n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, mssql::query_base (q));
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/details/.gitignore b/libodb-mssql/odb/mssql/details/.gitignore
new file mode 100644
index 0000000..b298f89
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/.gitignore
@@ -0,0 +1 @@
+/options.?xx
diff --git a/libodb-mssql/odb/mssql/details/build2/config-stub.h b/libodb-mssql/odb/mssql/details/build2/config-stub.h
new file mode 100644
index 0000000..e402d2f
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/build2/config-stub.h
@@ -0,0 +1,5 @@
+/* file : odb/mssql/details/build2/config-stub.h
+ * license : ODB NCUEL; see accompanying LICENSE file
+ */
+
+#include <odb/mssql/details/config.h>
diff --git a/libodb-mssql/odb/mssql/details/build2/config-vc-stub.h b/libodb-mssql/odb/mssql/details/build2/config-vc-stub.h
new file mode 100644
index 0000000..36ae27f
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/build2/config-vc-stub.h
@@ -0,0 +1,5 @@
+/* file : odb/mssql/details/build2/config-vc-stub.h
+ * license : ODB NCUEL; see accompanying LICENSE file
+ */
+
+#include <odb/mssql/details/config-vc.h>
diff --git a/libodb-mssql/odb/mssql/details/build2/config-vc.h b/libodb-mssql/odb/mssql/details/build2/config-vc.h
new file mode 100644
index 0000000..1489d09
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/build2/config-vc.h
@@ -0,0 +1,15 @@
+/* file : odb/mssql/details/build2/config-vc.h
+ * license : ODB NCUEL; see accompanying LICENSE file
+ */
+
+/* Configuration file for Windows/VC++ for the build2 build. */
+
+#ifndef ODB_MSSQL_DETAILS_CONFIG_VC_H
+#define ODB_MSSQL_DETAILS_CONFIG_VC_H
+
+/* Define LIBODB_MSSQL_BUILD2 for the installed case. */
+#ifndef LIBODB_MSSQL_BUILD2
+# define LIBODB_MSSQL_BUILD2
+#endif
+
+#endif /* ODB_MSSQL_DETAILS_CONFIG_VC_H */
diff --git a/libodb-mssql/odb/mssql/details/build2/config.h b/libodb-mssql/odb/mssql/details/build2/config.h
new file mode 100644
index 0000000..de736e2
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/build2/config.h
@@ -0,0 +1,17 @@
+/* file : odb/mssql/details/build2/config.h
+ * license : ODB NCUEL; see accompanying LICENSE file
+ */
+
+/* Static configuration file for the build2 build. The installed case
+ (when LIBODB_MSSQL_BUILD2 is not necessarily defined) is the only
+ reason we have it. */
+
+#ifndef ODB_MSSQL_DETAILS_CONFIG_H
+#define ODB_MSSQL_DETAILS_CONFIG_H
+
+/* Define LIBODB_MSSQL_BUILD2 for the installed case. */
+#ifndef LIBODB_MSSQL_BUILD2
+# define LIBODB_MSSQL_BUILD2
+#endif
+
+#endif /* ODB_MSSQL_DETAILS_CONFIG_H */
diff --git a/libodb-mssql/odb/mssql/details/config-vc.h b/libodb-mssql/odb/mssql/details/config-vc.h
new file mode 100644
index 0000000..e93b86b
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/config-vc.h
@@ -0,0 +1,5 @@
+/* file : odb/mssql/details/config-vc.h
+ * license : ODB NCUEL; see accompanying LICENSE file
+ */
+
+/* Dummy configuration file for Windows/VC++. */
diff --git a/libodb-mssql/odb/mssql/details/config.h.in b/libodb-mssql/odb/mssql/details/config.h.in
new file mode 100644
index 0000000..9ddb75a
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/config.h.in
@@ -0,0 +1,12 @@
+/* file : odb/mssql/details/config.h.in
+ * license : ODB NCUEL; see accompanying LICENSE file
+ */
+
+/* This file is automatically processed by configure. */
+
+#ifndef ODB_MSSQL_DETAILS_CONFIG_H
+#define ODB_MSSQL_DETAILS_CONFIG_H
+
+#undef LIBODB_MSSQL_STATIC_LIB
+
+#endif /* ODB_MSSQL_DETAILS_CONFIG_H */
diff --git a/libodb-mssql/odb/mssql/details/config.hxx b/libodb-mssql/odb/mssql/details/config.hxx
new file mode 100644
index 0000000..ff2a5af
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/config.hxx
@@ -0,0 +1,21 @@
+// file : odb/mssql/details/config.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_DETAILS_CONFIG_HXX
+#define ODB_MSSQL_DETAILS_CONFIG_HXX
+
+// no pre
+
+#ifdef ODB_COMPILER
+# error libodb-mssql header included in odb-compiled header
+#elif !defined(LIBODB_MSSQL_BUILD2)
+# ifdef _MSC_VER
+# include <odb/mssql/details/config-vc.h>
+# else
+# include <odb/mssql/details/config.h>
+# endif
+#endif
+
+// no post
+
+#endif // ODB_MSSQL_DETAILS_CONFIG_HXX
diff --git a/libodb-mssql/odb/mssql/details/conversion.hxx b/libodb-mssql/odb/mssql/details/conversion.hxx
new file mode 100644
index 0000000..35f368d
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/conversion.hxx
@@ -0,0 +1,58 @@
+// file : odb/mssql/details/conversion.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_DETAILS_CONVERSION_HXX
+#define ODB_MSSQL_DETAILS_CONVERSION_HXX
+
+#include <odb/mssql/traits.hxx>
+
+#include <odb/details/meta/answer.hxx>
+
+namespace odb
+{
+ // @@ Revise this.
+ //
+ namespace details {}
+
+ namespace mssql
+ {
+ namespace details
+ {
+ using namespace odb::details;
+
+ // Detect whether conversion is specified in type_traits.
+ //
+ template <typename T>
+ meta::yes
+ conversion_p_test (typename type_traits<T>::conversion*);
+
+ template <typename T>
+ meta::no
+ conversion_p_test (...);
+
+ template <typename T>
+ struct conversion_p
+ {
+ static const bool value =
+ sizeof (conversion_p_test<T> (0)) == sizeof (meta::yes);
+ };
+
+ template <typename T, bool = conversion_p<T>::value>
+ struct conversion;
+
+ template <typename T>
+ struct conversion<T, true>
+ {
+ static const char* to () {return type_traits<T>::conversion::to ();}
+ };
+
+ template <typename T>
+ struct conversion<T, false>
+ {
+ static const char* to () {return 0;}
+ };
+ }
+ }
+}
+
+#endif // ODB_MSSQL_DETAILS_CONVERSION_HXX
diff --git a/libodb-mssql/odb/mssql/details/export.hxx b/libodb-mssql/odb/mssql/details/export.hxx
new file mode 100644
index 0000000..94e762b
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/export.hxx
@@ -0,0 +1,78 @@
+// file : odb/mssql/details/export.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_DETAILS_EXPORT_HXX
+#define ODB_MSSQL_DETAILS_EXPORT_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/mssql/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_MSSQL_BUILD2
+
+#if defined(LIBODB_MSSQL_STATIC) // Using static.
+# define LIBODB_MSSQL_EXPORT
+#elif defined(LIBODB_MSSQL_STATIC_BUILD) // Building static.
+# define LIBODB_MSSQL_EXPORT
+#elif defined(LIBODB_MSSQL_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBODB_MSSQL_EXPORT __declspec(dllimport)
+# else
+# define LIBODB_MSSQL_EXPORT
+# endif
+#elif defined(LIBODB_MSSQL_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBODB_MSSQL_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_MSSQL_EXPORT
+# endif
+#else
+// If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared but in case
+// of shared will be sub-optimal compared to having dllimport.
+//
+# define LIBODB_MSSQL_EXPORT // Using static or shared.
+#endif
+
+#else // LIBODB_MSSQL_BUILD2
+
+#ifdef LIBODB_MSSQL_STATIC_LIB
+# define LIBODB_MSSQL_EXPORT
+#else
+# ifdef _WIN32
+# ifdef _MSC_VER
+# ifdef LIBODB_MSSQL_DYNAMIC_LIB
+# define LIBODB_MSSQL_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_MSSQL_EXPORT __declspec(dllimport)
+# endif
+# else
+# ifdef LIBODB_MSSQL_DYNAMIC_LIB
+# ifdef DLL_EXPORT
+# define LIBODB_MSSQL_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_MSSQL_EXPORT
+# endif
+# else
+# define LIBODB_MSSQL_EXPORT __declspec(dllimport)
+# endif
+# endif
+# else
+# define LIBODB_MSSQL_EXPORT
+# endif
+#endif
+
+#endif // LIBODB_MSSQL_BUILD2
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_DETAILS_EXPORT_HXX
diff --git a/libodb-mssql/odb/mssql/details/options.cli b/libodb-mssql/odb/mssql/details/options.cli
new file mode 100644
index 0000000..dcf92e5
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/options.cli
@@ -0,0 +1,63 @@
+// file : odb/mssql/details/options.cli
+// license : ODB NCUEL; see accompanying LICENSE file
+
+include <string>;
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace details
+ {
+ class options
+ {
+ std::string --user | -U
+ {
+ "<name>",
+ "SQL Server database user. If not specified, then Windows
+ authentication is used."
+ };
+
+ std::string --password | -P
+ {
+ "<str>",
+ "SQL Server database password. Omit this option if the user
+ password is blank or Windows authentication is used."
+ };
+
+ std::string --database | -d
+ {
+ "<name>",
+ "SQL Server database name. If not specified, then the default
+ database for this user is used."
+ };
+
+ std::string --server | -S
+ {
+ "<addr>",
+ "SQL Server instance address in the
+ \c{[\i{protocol}\b{:}]\i{host}[\b{\\}\i{instance}][\b{,}\i{port}]}
+ format, where \ci{protocol} can be \cb{tcp} (TCP/IP),
+ \cb{lpc} (shared memory), or \cb{np} (named pipe). If not specifid,
+ then \cb{localhost} is used."
+ };
+
+ std::string --driver
+ {
+ "<name>",
+ "SQL Server Native Client ODBC driver name. If not specified, then
+ the latest available driver is used."
+ };
+
+ std::string --options-file
+ {
+ "<file>",
+ "Read additional options from <file>. Each option should appear on a
+ separate line optionally followed by space or equal sign (\cb{=})
+ and an option value. Empty lines and lines starting with \cb{#} are
+ ignored."
+ };
+ };
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.cxx b/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.cxx
new file mode 100644
index 0000000..905beb8
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.cxx
@@ -0,0 +1,1125 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <odb/mssql/details/options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <utility>
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <fstream>
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // unknown_option
+ //
+ unknown_option::
+ ~unknown_option () throw ()
+ {
+ }
+
+ void unknown_option::
+ print (::std::ostream& os) const
+ {
+ os << "unknown option '" << option ().c_str () << "'";
+ }
+
+ const char* unknown_option::
+ what () const throw ()
+ {
+ return "unknown option";
+ }
+
+ // unknown_argument
+ //
+ unknown_argument::
+ ~unknown_argument () throw ()
+ {
+ }
+
+ void unknown_argument::
+ print (::std::ostream& os) const
+ {
+ os << "unknown argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unknown_argument::
+ what () const throw ()
+ {
+ return "unknown argument";
+ }
+
+ // missing_value
+ //
+ missing_value::
+ ~missing_value () throw ()
+ {
+ }
+
+ void missing_value::
+ print (::std::ostream& os) const
+ {
+ os << "missing value for option '" << option ().c_str () << "'";
+ }
+
+ const char* missing_value::
+ what () const throw ()
+ {
+ return "missing option value";
+ }
+
+ // invalid_value
+ //
+ invalid_value::
+ ~invalid_value () throw ()
+ {
+ }
+
+ void invalid_value::
+ print (::std::ostream& os) const
+ {
+ os << "invalid value '" << value ().c_str () << "' for option '"
+ << option ().c_str () << "'";
+
+ if (!message ().empty ())
+ os << ": " << message ().c_str ();
+ }
+
+ const char* invalid_value::
+ what () const throw ()
+ {
+ return "invalid option value";
+ }
+
+ // eos_reached
+ //
+ void eos_reached::
+ print (::std::ostream& os) const
+ {
+ os << what ();
+ }
+
+ const char* eos_reached::
+ what () const throw ()
+ {
+ return "end of argument stream reached";
+ }
+
+ // file_io_failure
+ //
+ file_io_failure::
+ ~file_io_failure () throw ()
+ {
+ }
+
+ void file_io_failure::
+ print (::std::ostream& os) const
+ {
+ os << "unable to open file '" << file ().c_str () << "' or read failure";
+ }
+
+ const char* file_io_failure::
+ what () const throw ()
+ {
+ return "unable to open file or read failure";
+ }
+
+ // unmatched_quote
+ //
+ unmatched_quote::
+ ~unmatched_quote () throw ()
+ {
+ }
+
+ void unmatched_quote::
+ print (::std::ostream& os) const
+ {
+ os << "unmatched quote in argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unmatched_quote::
+ what () const throw ()
+ {
+ return "unmatched quote";
+ }
+
+ // scanner
+ //
+ scanner::
+ ~scanner ()
+ {
+ }
+
+ // argv_scanner
+ //
+ bool argv_scanner::
+ more ()
+ {
+ return i_ < argc_;
+ }
+
+ const char* argv_scanner::
+ peek ()
+ {
+ if (i_ < argc_)
+ return argv_[i_];
+ else
+ throw eos_reached ();
+ }
+
+ const char* argv_scanner::
+ next ()
+ {
+ if (i_ < argc_)
+ {
+ const char* r (argv_[i_]);
+
+ if (erase_)
+ {
+ for (int i (i_ + 1); i < argc_; ++i)
+ argv_[i - 1] = argv_[i];
+
+ --argc_;
+ argv_[argc_] = 0;
+ }
+ else
+ ++i_;
+
+ ++start_position_;
+ return r;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ void argv_scanner::
+ skip ()
+ {
+ if (i_ < argc_)
+ {
+ ++i_;
+ ++start_position_;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ std::size_t argv_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ // argv_file_scanner
+ //
+ int argv_file_scanner::zero_argc_ = 0;
+ std::string argv_file_scanner::empty_string_;
+
+ bool argv_file_scanner::
+ more ()
+ {
+ if (!args_.empty ())
+ return true;
+
+ while (base::more ())
+ {
+ // See if the next argument is the file option.
+ //
+ const char* a (base::peek ());
+ const option_info* oi = 0;
+ const char* ov = 0;
+
+ if (!skip_)
+ {
+ if ((oi = find (a)) != 0)
+ {
+ base::next ();
+
+ if (!base::more ())
+ throw missing_value (a);
+
+ ov = base::next ();
+ }
+ else if (std::strncmp (a, "-", 1) == 0)
+ {
+ if ((ov = std::strchr (a, '=')) != 0)
+ {
+ std::string o (a, 0, ov - a);
+ if ((oi = find (o.c_str ())) != 0)
+ {
+ base::next ();
+ ++ov;
+ }
+ }
+ }
+ }
+
+ if (oi != 0)
+ {
+ if (oi->search_func != 0)
+ {
+ std::string f (oi->search_func (ov, oi->arg));
+
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ load (ov);
+
+ if (!args_.empty ())
+ return true;
+ }
+ else
+ {
+ if (!skip_)
+ skip_ = (std::strcmp (a, "--") == 0);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ const char* argv_file_scanner::
+ peek ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? base::peek () : args_.front ().value.c_str ();
+ }
+
+ const std::string& argv_file_scanner::
+ peek_file ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? empty_string_ : *args_.front ().file;
+ }
+
+ std::size_t argv_file_scanner::
+ peek_line ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? 0 : args_.front ().line;
+ }
+
+ const char* argv_file_scanner::
+ next ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::next ();
+ else
+ {
+ hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);
+ args_.pop_front ();
+ ++start_position_;
+ return hold_[i_].c_str ();
+ }
+ }
+
+ void argv_file_scanner::
+ skip ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::skip ();
+ else
+ {
+ args_.pop_front ();
+ ++start_position_;
+ }
+ }
+
+ const argv_file_scanner::option_info* argv_file_scanner::
+ find (const char* a) const
+ {
+ for (std::size_t i (0); i < options_count_; ++i)
+ if (std::strcmp (a, options_[i].option) == 0)
+ return &options_[i];
+
+ return 0;
+ }
+
+ std::size_t argv_file_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ void argv_file_scanner::
+ load (const std::string& file)
+ {
+ using namespace std;
+
+ ifstream is (file.c_str ());
+
+ if (!is.is_open ())
+ throw file_io_failure (file);
+
+ files_.push_back (file);
+
+ arg a;
+ a.file = &*files_.rbegin ();
+
+ for (a.line = 1; !is.eof (); ++a.line)
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ throw file_io_failure (file);
+
+ string::size_type n (line.size ());
+
+ // Trim the line from leading and trailing whitespaces.
+ //
+ if (n != 0)
+ {
+ const char* f (line.c_str ());
+ const char* l (f + n);
+
+ const char* of (f);
+ while (f < l && (*f == ' ' || *f == '\t' || *f == '\r'))
+ ++f;
+
+ --l;
+
+ const char* ol (l);
+ while (l > f && (*l == ' ' || *l == '\t' || *l == '\r'))
+ --l;
+
+ if (f != of || l != ol)
+ line = f <= l ? string (f, l - f + 1) : string ();
+ }
+
+ // Ignore empty lines, those that start with #.
+ //
+ if (line.empty () || line[0] == '#')
+ continue;
+
+ string::size_type p (string::npos);
+ if (line.compare (0, 1, "-") == 0)
+ {
+ p = line.find (' ');
+
+ string::size_type q (line.find ('='));
+ if (q != string::npos && q < p)
+ p = q;
+ }
+
+ string s1;
+ if (p != string::npos)
+ {
+ s1.assign (line, 0, p);
+
+ // Skip leading whitespaces in the argument.
+ //
+ if (line[p] == '=')
+ ++p;
+ else
+ {
+ n = line.size ();
+ for (++p; p < n; ++p)
+ {
+ char c (line[p]);
+ if (c != ' ' && c != '\t' && c != '\r')
+ break;
+ }
+ }
+ }
+ else if (!skip_)
+ skip_ = (line == "--");
+
+ string s2 (line, p != string::npos ? p : 0);
+
+ // If the string (which is an option value or argument) is
+ // wrapped in quotes, remove them.
+ //
+ n = s2.size ();
+ char cf (s2[0]), cl (s2[n - 1]);
+
+ if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
+ {
+ if (n == 1 || cf != cl)
+ throw unmatched_quote (s2);
+
+ s2 = string (s2, 1, n - 2);
+ }
+
+ if (!s1.empty ())
+ {
+ // See if this is another file option.
+ //
+ const option_info* oi;
+ if (!skip_ && (oi = find (s1.c_str ())))
+ {
+ if (s2.empty ())
+ throw missing_value (oi->option);
+
+ if (oi->search_func != 0)
+ {
+ string f (oi->search_func (s2.c_str (), oi->arg));
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ {
+ // If the path of the file being parsed is not simple and the
+ // path of the file that needs to be loaded is relative, then
+ // complete the latter using the former as a base.
+ //
+#ifndef _WIN32
+ string::size_type p (file.find_last_of ('/'));
+ bool c (p != string::npos && s2[0] != '/');
+#else
+ string::size_type p (file.find_last_of ("/\\"));
+ bool c (p != string::npos && s2[1] != ':');
+#endif
+ if (c)
+ s2.insert (0, file, 0, p + 1);
+
+ load (s2);
+ }
+
+ continue;
+ }
+
+ a.value = s1;
+ args_.push_back (a);
+ }
+
+ a.value = s2;
+ args_.push_back (a);
+ }
+ }
+
+ template <typename X>
+ struct parser
+ {
+ static void
+ parse (X& x, bool& xs, scanner& s)
+ {
+ using namespace std;
+
+ const char* o (s.next ());
+ if (s.more ())
+ {
+ string v (s.next ());
+ istringstream is (v);
+ if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<bool>
+ {
+ static void
+ parse (bool& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ const char* v (s.next ());
+
+ if (std::strcmp (v, "1") == 0 ||
+ std::strcmp (v, "true") == 0 ||
+ std::strcmp (v, "TRUE") == 0 ||
+ std::strcmp (v, "True") == 0)
+ x = true;
+ else if (std::strcmp (v, "0") == 0 ||
+ std::strcmp (v, "false") == 0 ||
+ std::strcmp (v, "FALSE") == 0 ||
+ std::strcmp (v, "False") == 0)
+ x = false;
+ else
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<std::string>
+ {
+ static void
+ parse (std::string& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ x = s.next ();
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X>
+ struct parser<std::pair<X, std::size_t> >
+ {
+ static void
+ parse (std::pair<X, std::size_t>& x, bool& xs, scanner& s)
+ {
+ x.second = s.position ();
+ parser<X>::parse (x.first, xs, s);
+ }
+ };
+
+ template <typename X>
+ struct parser<std::vector<X> >
+ {
+ static void
+ parse (std::vector<X>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.push_back (x);
+ xs = true;
+ }
+ };
+
+ template <typename X, typename C>
+ struct parser<std::set<X, C> >
+ {
+ static void
+ parse (std::set<X, C>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.insert (x);
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::map<K, V, C> >
+ {
+ static void
+ parse (std::map<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m[k] = v;
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::multimap<K, V, C> >
+ {
+ static void
+ parse (std::multimap<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m.insert (typename std::multimap<K, V, C>::value_type (k, v));
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X, typename T, T X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, s);
+ }
+
+ template <typename X, bool X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ s.next ();
+ x.*M = true;
+ }
+
+ template <typename X, typename T, T X::*M, bool X::*S>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, x.*S, s);
+ }
+ }
+ }
+ }
+}
+
+#include <map>
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace details
+ {
+ // options
+ //
+
+ options::
+ options ()
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ server_ (),
+ server_specified_ (false),
+ driver_ (),
+ driver_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ bool erase,
+ ::odb::mssql::details::cli::unknown_mode opt,
+ ::odb::mssql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ server_ (),
+ server_specified_ (false),
+ driver_ (),
+ driver_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mssql::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ ::odb::mssql::details::cli::unknown_mode opt,
+ ::odb::mssql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ server_ (),
+ server_specified_ (false),
+ driver_ (),
+ driver_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mssql::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::mssql::details::cli::unknown_mode opt,
+ ::odb::mssql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ server_ (),
+ server_specified_ (false),
+ driver_ (),
+ driver_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mssql::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::mssql::details::cli::unknown_mode opt,
+ ::odb::mssql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ server_ (),
+ server_specified_ (false),
+ driver_ (),
+ driver_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mssql::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (::odb::mssql::details::cli::scanner& s,
+ ::odb::mssql::details::cli::unknown_mode opt,
+ ::odb::mssql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ server_ (),
+ server_specified_ (false),
+ driver_ (),
+ driver_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ _parse (s, opt, arg);
+ }
+
+ ::odb::mssql::details::cli::usage_para options::
+ print_usage (::std::ostream& os, ::odb::mssql::details::cli::usage_para p)
+ {
+ CLI_POTENTIALLY_UNUSED (os);
+
+ if (p != ::odb::mssql::details::cli::usage_para::none)
+ os << ::std::endl;
+
+ os << "--user|-U <name> SQL Server database user. If not specified, then Windows" << ::std::endl
+ << " authentication is used." << ::std::endl;
+
+ os << std::endl
+ << "--password|-P <str> SQL Server database password. Omit this option if the" << ::std::endl
+ << " user password is blank or Windows authentication is used." << ::std::endl;
+
+ os << std::endl
+ << "--database|-d <name> SQL Server database name. If not specified, then the" << ::std::endl
+ << " default database for this user is used." << ::std::endl;
+
+ os << std::endl
+ << "--server|-S <addr> SQL Server instance address in the" << ::std::endl
+ << " [protocol:]host[\\instance][,port] format, where protocol" << ::std::endl
+ << " can be tcp (TCP/IP), lpc (shared memory), or np (named" << ::std::endl
+ << " pipe). If not specifid, then localhost is used." << ::std::endl;
+
+ os << std::endl
+ << "--driver <name> SQL Server Native Client ODBC driver name. If not" << ::std::endl
+ << " specified, then the latest available driver is used." << ::std::endl;
+
+ os << std::endl
+ << "--options-file <file> Read additional options from <file>. Each option should" << ::std::endl
+ << " appear on a separate line optionally followed by space or" << ::std::endl
+ << " equal sign (=) and an option value. Empty lines and lines" << ::std::endl
+ << " starting with # are ignored." << ::std::endl;
+
+ p = ::odb::mssql::details::cli::usage_para::option;
+
+ return p;
+ }
+
+ typedef
+ std::map<std::string, void (*) (options&, ::odb::mssql::details::cli::scanner&)>
+ _cli_options_map;
+
+ static _cli_options_map _cli_options_map_;
+
+ struct _cli_options_map_init
+ {
+ _cli_options_map_init ()
+ {
+ _cli_options_map_["--user"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::user_,
+ &options::user_specified_ >;
+ _cli_options_map_["-U"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::user_,
+ &options::user_specified_ >;
+ _cli_options_map_["--password"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::password_,
+ &options::password_specified_ >;
+ _cli_options_map_["-P"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::password_,
+ &options::password_specified_ >;
+ _cli_options_map_["--database"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["-d"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["--server"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::server_,
+ &options::server_specified_ >;
+ _cli_options_map_["-S"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::server_,
+ &options::server_specified_ >;
+ _cli_options_map_["--driver"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::driver_,
+ &options::driver_specified_ >;
+ _cli_options_map_["--options-file"] =
+ &::odb::mssql::details::cli::thunk< options, std::string, &options::options_file_,
+ &options::options_file_specified_ >;
+ }
+ };
+
+ static _cli_options_map_init _cli_options_map_init_;
+
+ bool options::
+ _parse (const char* o, ::odb::mssql::details::cli::scanner& s)
+ {
+ _cli_options_map::const_iterator i (_cli_options_map_.find (o));
+
+ if (i != _cli_options_map_.end ())
+ {
+ (*(i->second)) (*this, s);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool options::
+ _parse (::odb::mssql::details::cli::scanner& s,
+ ::odb::mssql::details::cli::unknown_mode opt_mode,
+ ::odb::mssql::details::cli::unknown_mode arg_mode)
+ {
+ bool r = false;
+ bool opt = true;
+
+ while (s.more ())
+ {
+ const char* o = s.peek ();
+
+ if (std::strcmp (o, "--") == 0)
+ {
+ opt = false;
+ s.skip ();
+ r = true;
+ continue;
+ }
+
+ if (opt)
+ {
+ if (_parse (o, s))
+ {
+ r = true;
+ continue;
+ }
+
+ if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
+ {
+ // Handle combined option values.
+ //
+ std::string co;
+ if (const char* v = std::strchr (o, '='))
+ {
+ co.assign (o, 0, v - o);
+ ++v;
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (co.c_str ()),
+ const_cast<char*> (v)
+ };
+
+ ::odb::mssql::details::cli::argv_scanner ns (0, ac, av);
+
+ if (_parse (co.c_str (), ns))
+ {
+ // Parsed the option but not its value?
+ //
+ if (ns.end () != 2)
+ throw ::odb::mssql::details::cli::invalid_value (co, v);
+
+ s.next ();
+ r = true;
+ continue;
+ }
+ else
+ {
+ // Set the unknown option and fall through.
+ //
+ o = co.c_str ();
+ }
+ }
+
+ switch (opt_mode)
+ {
+ case ::odb::mssql::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::mssql::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::mssql::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::mssql::details::cli::unknown_option (o);
+ }
+ }
+
+ break;
+ }
+ }
+
+ switch (arg_mode)
+ {
+ case ::odb::mssql::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::mssql::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::mssql::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::mssql::details::cli::unknown_argument (o);
+ }
+ }
+
+ break;
+ }
+
+ return r;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
diff --git a/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.hxx b/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.hxx
new file mode 100644
index 0000000..104395b
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.hxx
@@ -0,0 +1,562 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+#ifndef LIBODB_MSSQL_DETAILS_OPTIONS_HXX
+#define LIBODB_MSSQL_DETAILS_OPTIONS_HXX
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <list>
+#include <deque>
+#include <iosfwd>
+#include <string>
+#include <cstddef>
+#include <exception>
+
+#ifndef CLI_POTENTIALLY_UNUSED
+# if defined(_MSC_VER) || defined(__xlC__)
+# define CLI_POTENTIALLY_UNUSED(x) (void*)&x
+# else
+# define CLI_POTENTIALLY_UNUSED(x) (void)x
+# endif
+#endif
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ class usage_para
+ {
+ public:
+ enum value
+ {
+ none,
+ text,
+ option
+ };
+
+ usage_para (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ class unknown_mode
+ {
+ public:
+ enum value
+ {
+ skip,
+ stop,
+ fail
+ };
+
+ unknown_mode (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ // Exceptions.
+ //
+
+ class exception: public std::exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const = 0;
+ };
+
+ ::std::ostream&
+ operator<< (::std::ostream&, const exception&);
+
+ class unknown_option: public exception
+ {
+ public:
+ virtual
+ ~unknown_option () throw ();
+
+ unknown_option (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class unknown_argument: public exception
+ {
+ public:
+ virtual
+ ~unknown_argument () throw ();
+
+ unknown_argument (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ class missing_value: public exception
+ {
+ public:
+ virtual
+ ~missing_value () throw ();
+
+ missing_value (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class invalid_value: public exception
+ {
+ public:
+ virtual
+ ~invalid_value () throw ();
+
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message = std::string ());
+
+ const std::string&
+ option () const;
+
+ const std::string&
+ value () const;
+
+ const std::string&
+ message () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ std::string value_;
+ std::string message_;
+ };
+
+ class eos_reached: public exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class file_io_failure: public exception
+ {
+ public:
+ virtual
+ ~file_io_failure () throw ();
+
+ file_io_failure (const std::string& file);
+
+ const std::string&
+ file () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string file_;
+ };
+
+ class unmatched_quote: public exception
+ {
+ public:
+ virtual
+ ~unmatched_quote () throw ();
+
+ unmatched_quote (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ // Command line argument scanner interface.
+ //
+ // The values returned by next() are guaranteed to be valid
+ // for the two previous arguments up until a call to a third
+ // peek() or next().
+ //
+ // The position() function returns a monotonically-increasing
+ // number which, if stored, can later be used to determine the
+ // relative position of the argument returned by the following
+ // call to next(). Note that if multiple scanners are used to
+ // extract arguments from multiple sources, then the end
+ // position of the previous scanner should be used as the
+ // start position of the next.
+ //
+ class scanner
+ {
+ public:
+ virtual
+ ~scanner ();
+
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+
+ virtual std::size_t
+ position () = 0;
+ };
+
+ class argv_scanner: public scanner
+ {
+ public:
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ int
+ end () const;
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ protected:
+ std::size_t start_position_;
+ int i_;
+ int& argc_;
+ char** argv_;
+ bool erase_;
+ };
+
+ class argv_file_scanner: public argv_scanner
+ {
+ public:
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t start_position = 0);
+
+ struct option_info
+ {
+ // If search_func is not NULL, it is called, with the arg
+ // value as the second argument, to locate the options file.
+ // If it returns an empty string, then the file is ignored.
+ //
+ const char* option;
+ std::string (*search_func) (const char*, void* arg);
+ void* arg;
+ };
+
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const option_info* options = 0,
+ std::size_t options_count = 0,
+ std::size_t start_position = 0);
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ // Return the file path if the peeked at argument came from a file and
+ // the empty string otherwise. The reference is guaranteed to be valid
+ // till the end of the scanner lifetime.
+ //
+ const std::string&
+ peek_file ();
+
+ // Return the 1-based line number if the peeked at argument came from
+ // a file and zero otherwise.
+ //
+ std::size_t
+ peek_line ();
+
+ private:
+ const option_info*
+ find (const char*) const;
+
+ void
+ load (const std::string& file);
+
+ typedef argv_scanner base;
+
+ const std::string option_;
+ option_info option_info_;
+ const option_info* options_;
+ std::size_t options_count_;
+
+ struct arg
+ {
+ std::string value;
+ const std::string* file;
+ std::size_t line;
+ };
+
+ std::deque<arg> args_;
+ std::list<std::string> files_;
+
+ // Circular buffer of two arguments.
+ //
+ std::string hold_[2];
+ std::size_t i_;
+
+ bool skip_;
+
+ static int zero_argc_;
+ static std::string empty_string_;
+ };
+
+ template <typename X>
+ struct parser;
+ }
+ }
+ }
+}
+
+#include <string>
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace details
+ {
+ class options
+ {
+ public:
+ options ();
+
+ options (int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::mssql::details::cli::unknown_mode option = ::odb::mssql::details::cli::unknown_mode::fail,
+ ::odb::mssql::details::cli::unknown_mode argument = ::odb::mssql::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::mssql::details::cli::unknown_mode option = ::odb::mssql::details::cli::unknown_mode::fail,
+ ::odb::mssql::details::cli::unknown_mode argument = ::odb::mssql::details::cli::unknown_mode::stop);
+
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::mssql::details::cli::unknown_mode option = ::odb::mssql::details::cli::unknown_mode::fail,
+ ::odb::mssql::details::cli::unknown_mode argument = ::odb::mssql::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::mssql::details::cli::unknown_mode option = ::odb::mssql::details::cli::unknown_mode::fail,
+ ::odb::mssql::details::cli::unknown_mode argument = ::odb::mssql::details::cli::unknown_mode::stop);
+
+ options (::odb::mssql::details::cli::scanner&,
+ ::odb::mssql::details::cli::unknown_mode option = ::odb::mssql::details::cli::unknown_mode::fail,
+ ::odb::mssql::details::cli::unknown_mode argument = ::odb::mssql::details::cli::unknown_mode::stop);
+
+ // Option accessors.
+ //
+ const std::string&
+ user () const;
+
+ bool
+ user_specified () const;
+
+ const std::string&
+ password () const;
+
+ bool
+ password_specified () const;
+
+ const std::string&
+ database () const;
+
+ bool
+ database_specified () const;
+
+ const std::string&
+ server () const;
+
+ bool
+ server_specified () const;
+
+ const std::string&
+ driver () const;
+
+ bool
+ driver_specified () const;
+
+ const std::string&
+ options_file () const;
+
+ bool
+ options_file_specified () const;
+
+ // Print usage information.
+ //
+ static ::odb::mssql::details::cli::usage_para
+ print_usage (::std::ostream&,
+ ::odb::mssql::details::cli::usage_para = ::odb::mssql::details::cli::usage_para::none);
+
+ // Implementation details.
+ //
+ protected:
+ bool
+ _parse (const char*, ::odb::mssql::details::cli::scanner&);
+
+ private:
+ bool
+ _parse (::odb::mssql::details::cli::scanner&,
+ ::odb::mssql::details::cli::unknown_mode option,
+ ::odb::mssql::details::cli::unknown_mode argument);
+
+ public:
+ std::string user_;
+ bool user_specified_;
+ std::string password_;
+ bool password_specified_;
+ std::string database_;
+ bool database_specified_;
+ std::string server_;
+ bool server_specified_;
+ std::string driver_;
+ bool driver_specified_;
+ std::string options_file_;
+ bool options_file_specified_;
+ };
+ }
+ }
+}
+
+#include <odb/mssql/details/options.ixx>
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
+#endif // LIBODB_MSSQL_DETAILS_OPTIONS_HXX
diff --git a/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.ixx b/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.ixx
new file mode 100644
index 0000000..a406dc4
--- /dev/null
+++ b/libodb-mssql/odb/mssql/details/pregenerated/odb/mssql/details/options.ixx
@@ -0,0 +1,372 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <cassert>
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // usage_para
+ //
+ inline usage_para::
+ usage_para (value v)
+ : v_ (v)
+ {
+ }
+
+ // unknown_mode
+ //
+ inline unknown_mode::
+ unknown_mode (value v)
+ : v_ (v)
+ {
+ }
+
+ // exception
+ //
+ inline ::std::ostream&
+ operator<< (::std::ostream& os, const exception& e)
+ {
+ e.print (os);
+ return os;
+ }
+
+ // unknown_option
+ //
+ inline unknown_option::
+ unknown_option (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& unknown_option::
+ option () const
+ {
+ return option_;
+ }
+
+ // unknown_argument
+ //
+ inline unknown_argument::
+ unknown_argument (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unknown_argument::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // missing_value
+ //
+ inline missing_value::
+ missing_value (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& missing_value::
+ option () const
+ {
+ return option_;
+ }
+
+ // invalid_value
+ //
+ inline invalid_value::
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message)
+ : option_ (option),
+ value_ (value),
+ message_ (message)
+ {
+ }
+
+ inline const std::string& invalid_value::
+ option () const
+ {
+ return option_;
+ }
+
+ inline const std::string& invalid_value::
+ value () const
+ {
+ return value_;
+ }
+
+ inline const std::string& invalid_value::
+ message () const
+ {
+ return message_;
+ }
+
+ // file_io_failure
+ //
+ inline file_io_failure::
+ file_io_failure (const std::string& file)
+ : file_ (file)
+ {
+ }
+
+ inline const std::string& file_io_failure::
+ file () const
+ {
+ return file_;
+ }
+
+ // unmatched_quote
+ //
+ inline unmatched_quote::
+ unmatched_quote (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unmatched_quote::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // argv_scanner
+ //
+ inline argv_scanner::
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + 1),
+ i_ (1),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline argv_scanner::
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + static_cast<std::size_t> (start)),
+ i_ (start),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline int argv_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ // argv_file_scanner
+ //
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+
+ load (file);
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const option_info* options,
+ std::size_t options_count,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ load (file);
+ }
+ }
+ }
+ }
+}
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace details
+ {
+ // options
+ //
+
+ inline const std::string& options::
+ user () const
+ {
+ return this->user_;
+ }
+
+ inline bool options::
+ user_specified () const
+ {
+ return this->user_specified_;
+ }
+
+ inline const std::string& options::
+ password () const
+ {
+ return this->password_;
+ }
+
+ inline bool options::
+ password_specified () const
+ {
+ return this->password_specified_;
+ }
+
+ inline const std::string& options::
+ database () const
+ {
+ return this->database_;
+ }
+
+ inline bool options::
+ database_specified () const
+ {
+ return this->database_specified_;
+ }
+
+ inline const std::string& options::
+ server () const
+ {
+ return this->server_;
+ }
+
+ inline bool options::
+ server_specified () const
+ {
+ return this->server_specified_;
+ }
+
+ inline const std::string& options::
+ driver () const
+ {
+ return this->driver_;
+ }
+
+ inline bool options::
+ driver_specified () const
+ {
+ return this->driver_specified_;
+ }
+
+ inline const std::string& options::
+ options_file () const
+ {
+ return this->options_file_;
+ }
+
+ inline bool options::
+ options_file_specified () const
+ {
+ return this->options_file_specified_;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
diff --git a/libodb-mssql/odb/mssql/error.cxx b/libodb-mssql/odb/mssql/error.cxx
new file mode 100644
index 0000000..897d415
--- /dev/null
+++ b/libodb-mssql/odb/mssql/error.cxx
@@ -0,0 +1,273 @@
+// file : odb/mssql/error.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <string>
+#include <cstring> // std::strlen
+
+#include <odb/mssql/mssql.hxx>
+#include <odb/mssql/error.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/exceptions.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ static void
+ translate_error (SQLRETURN r,
+ SQLHANDLE h,
+ SQLSMALLINT htype,
+ connection* conn,
+ bool end_tran,
+ size_t pos,
+ multiple_exceptions* mex)
+ {
+ // First see if we have one of the errors indicated via the
+ // return error code.
+ //
+ switch (r)
+ {
+ case SQL_STILL_EXECUTING:
+ {
+ throw database_exception (0, "?????", "statement still executing");
+ break;
+ }
+ case SQL_NEED_DATA:
+ case SQL_NO_DATA:
+#if ODBCVER >= 0x0380
+ case SQL_PARAM_DATA_AVAILABLE:
+#endif
+ {
+ throw database_exception (
+ 0, "?????", "unhandled SQL_*_DATA condition");
+ break;
+ }
+ case SQL_INVALID_HANDLE:
+ {
+ throw database_exception (0, "?????", "invalid handle");
+ break;
+ }
+ }
+
+ // Otherwise the diagnostics is stored in the handle.
+ //
+ char sqlstate[SQL_SQLSTATE_SIZE + 1];
+ SQLINTEGER native_code; // Will be 0 if no natve code.
+ char msg[512]; // Will be truncated if doesn't fit.
+ SQLSMALLINT msg_size;
+
+ // We need to translate certain sqlstate codes to special exceptions,
+ // such as deadlock, timeout, etc. The problem is we can have multiple
+ // records potentially with different sqlstate codes. If we have both,
+ // say, a deadlock code and some other code, then we should probably
+ // throw database_exception, which is more severe. To implement this
+ // we are going to pre-scan the records looking for the codes we are
+ // interested in. If in the process we see any other code, then we
+ // stop and go ahead to prepare and throw database_exception.
+ //
+ enum code
+ {
+ code_none,
+ code_deadlock,
+ code_timeout,
+ code_connection_lost
+ };
+
+ code c (code_none);
+
+ for (SQLSMALLINT i (1);; ++i)
+ {
+ r = SQLGetDiagRecA (htype,
+ h,
+ i,
+ (SQLCHAR*) sqlstate,
+ &native_code,
+ 0,
+ 0,
+ &msg_size);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (SQL_SUCCEEDED (r))
+ {
+ code nc;
+ string s (sqlstate);
+
+ if (s == "40001") // Serialization failure (native code 1205).
+ nc = code_deadlock;
+ else if (s == "HYT00") // Timeout expired.
+ nc = code_timeout;
+ else if (s == "HYT01") // Connection timeout expired.
+ {
+ nc = code_timeout;
+
+ if (conn != 0)
+ conn->mark_failed ();
+ }
+ else if (s == "08S01") // Link failure.
+ {
+ nc = code_connection_lost;
+
+ if (conn != 0)
+ conn->mark_failed ();
+ }
+ else if (s == "01000") // General warning.
+ continue;
+ else
+ {
+ c = code_none;
+ break;
+ }
+
+ // If a call to SQLEndTran() fails, then the connection is
+ // put into the so called "suspended state" and should be
+ // disconnected unless we know the transaction was rolled
+ // back. See SQLEndTran() documentation for details.
+ //
+ if (end_tran &&
+ s != "25S03" && // Transaction is rolled back.
+ s != "40001" && // Serialization failure.
+ s != "40002" && // Integrity constraint.
+ s != "HYC00") // Optional feature not implemented.
+ conn->mark_failed ();
+
+ if (c != code_none && c != nc)
+ {
+ // Several different codes.
+ //
+ c = code_none;
+ break;
+ }
+
+ c = nc;
+ }
+ else
+ {
+ c = code_none;
+ break;
+ }
+ }
+
+ switch (c)
+ {
+ case code_deadlock:
+ throw deadlock ();
+ case code_timeout:
+ throw timeout ();
+ case code_connection_lost:
+ throw connection_lost ();
+ case code_none:
+ break;
+ }
+
+ // Some other error code. Prepare database_exception.
+ //
+ database_exception e;
+
+ for (SQLSMALLINT i (1);; ++i)
+ {
+ // If this is for a batch, filter out based on row association.
+ // Here we only ignore records that have the associated row
+ // number and this number doesn't match ours. In particular,
+ // this means that all the un-associated records which will be
+ // duplicated for all the failed rows, which seems like the
+ // correct thing to do.
+ //
+ if (mex != 0)
+ {
+ SQLLEN n;
+ r = SQLGetDiagField (htype,
+ h,
+ i,
+ SQL_DIAG_ROW_NUMBER,
+ &n,
+ 0,
+ 0);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (SQL_SUCCEEDED (r) &&
+ n != SQL_NO_ROW_NUMBER &&
+ n != SQL_ROW_NUMBER_UNKNOWN &&
+ n != static_cast<SQLLEN> (pos + 1)) // 1-based
+ continue;
+ }
+
+ r = SQLGetDiagRecA (htype,
+ h,
+ i,
+ (SQLCHAR*) sqlstate,
+ &native_code,
+ (SQLCHAR*) msg,
+ sizeof (msg),
+ &msg_size);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (SQL_SUCCEEDED (r))
+ {
+ if (conn != 0)
+ {
+ string s (sqlstate);
+
+ if (s == "08S01" || // Link failure.
+ s == "HYT01" || // Connection timeout.
+ (end_tran &&
+ s != "25S03" &&
+ s != "40001" &&
+ s != "40002" &&
+ s != "HYC00"))
+ conn->mark_failed ();
+ }
+
+ // Get rid of a trailing newline if there is one.
+ //
+ size_t n (strlen (msg));
+ if (n != 0 && msg[n - 1] == '\n')
+ msg[n - 1] = '\0';
+
+ e.append (native_code, sqlstate, msg);
+ }
+ else
+ e.append (0, "?????", "unable to extract information for this "
+ "diagnostic record");
+ }
+
+ if (e.size () == 0)
+ e.append (0, "?????", "no diagnostic record (using wrong handle?)");
+
+ if (mex == 0)
+ throw e;
+ else
+ // It could be that some of these errors are fatal. I guess we
+ // will just have to learn from experience which ones are. The
+ // client code can always treat specific error codes as fatal.
+ //
+ mex->insert (pos, e);
+ }
+
+ void
+ translate_error (SQLRETURN r, connection& c, bool end_tran)
+ {
+ translate_error (r, c.handle (), SQL_HANDLE_DBC, &c, end_tran, 0, 0);
+ }
+
+ void
+ translate_error (SQLRETURN r,
+ connection& c,
+ const auto_handle<SQL_HANDLE_STMT>& h,
+ size_t pos,
+ multiple_exceptions* mex)
+ {
+ translate_error (r, h, SQL_HANDLE_STMT, &c, false, pos, mex);
+ }
+
+ void
+ translate_error (SQLRETURN r, SQLHANDLE h, SQLSMALLINT htype)
+ {
+ translate_error (r, h, htype, 0, false, 0, 0);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/error.hxx b/libodb-mssql/odb/mssql/error.hxx
new file mode 100644
index 0000000..cb8cc7c
--- /dev/null
+++ b/libodb-mssql/odb/mssql/error.hxx
@@ -0,0 +1,40 @@
+// file : odb/mssql/error.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_ERROR_HXX
+#define ODB_MSSQL_ERROR_HXX
+
+#include <odb/pre.hxx>
+#include <cstddef> // std::size_t
+
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx> // connection, multiple_exceptions
+#include <odb/mssql/auto-handle.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ // Translate ODBC error given a handle and throw (or return, in case
+ // multiple_exceptions is not NULL) an appropriate exception.
+ //
+ LIBODB_MSSQL_EXPORT void
+ translate_error (SQLRETURN, connection&, bool end_tran = false);
+
+ LIBODB_MSSQL_EXPORT void
+ translate_error (SQLRETURN,
+ connection&,
+ const auto_handle<SQL_HANDLE_STMT>&,
+ std::size_t pos = 0, multiple_exceptions* = 0);
+
+ LIBODB_MSSQL_EXPORT void
+ translate_error (SQLRETURN, SQLHANDLE, SQLSMALLINT htype);
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_ERROR_HXX
diff --git a/libodb-mssql/odb/mssql/exceptions.cxx b/libodb-mssql/odb/mssql/exceptions.cxx
new file mode 100644
index 0000000..606678d
--- /dev/null
+++ b/libodb-mssql/odb/mssql/exceptions.cxx
@@ -0,0 +1,109 @@
+// file : odb/mssql/exceptions.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/mssql/exceptions.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // database_exception
+ //
+
+ database_exception::record::
+ record (SQLINTEGER e, const string& s, const string& m)
+ : error_ (e), sqlstate_ (s), message_ (m)
+ {
+ }
+
+ database_exception::
+ ~database_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ database_exception::
+ database_exception ()
+ {
+ }
+
+ database_exception::
+ database_exception (SQLINTEGER e, const string& s, const string& m)
+ {
+ append (e, s, m);
+ }
+
+ void database_exception::
+ append (SQLINTEGER e, const string& s, const string& m)
+ {
+ records_.push_back (record (e, s, m));
+
+ if (!what_.empty ())
+ what_ += '\n';
+
+ ostringstream ostr;
+ ostr << e << " (" << s << "): " << m;
+ what_ += ostr.str ();
+ }
+
+ const char* database_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ database_exception* database_exception::
+ clone () const
+ {
+ return new database_exception (*this);
+ }
+
+ //
+ // cli_exception
+ //
+
+ cli_exception::
+ cli_exception (const string& what)
+ : what_ (what)
+ {
+ }
+
+ cli_exception::
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* cli_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ cli_exception* cli_exception::
+ clone () const
+ {
+ return new cli_exception (*this);
+ }
+
+ //
+ // long_data_reload
+ //
+
+ const char* long_data_reload::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "attempt to re-load object or view with long data "
+ "from query result";
+ }
+
+ long_data_reload* long_data_reload::
+ clone () const
+ {
+ return new long_data_reload (*this);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/exceptions.hxx b/libodb-mssql/odb/mssql/exceptions.hxx
new file mode 100644
index 0000000..5240d8d
--- /dev/null
+++ b/libodb-mssql/odb/mssql/exceptions.hxx
@@ -0,0 +1,138 @@
+// file : odb/mssql/exceptions.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_EXCEPTIONS_HXX
+#define ODB_MSSQL_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <vector>
+
+#include <odb/exceptions.hxx>
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ struct LIBODB_MSSQL_EXPORT database_exception: odb::database_exception
+ {
+ struct record
+ {
+ record (SQLINTEGER error,
+ const std::string& sqlstate,
+ const std::string& message);
+
+ SQLINTEGER
+ error () const
+ {
+ return error_;
+ }
+
+ const std::string&
+ sqlstate () const
+ {
+ return sqlstate_;
+ }
+
+ const std::string&
+ message () const
+ {
+ return message_;
+ }
+
+ private:
+ SQLINTEGER error_;
+ std::string sqlstate_;
+ std::string message_;
+ };
+
+ typedef std::vector<record> records;
+
+ typedef records::size_type size_type;
+ typedef records::const_iterator iterator;
+
+ iterator
+ begin () const
+ {
+ return records_.begin ();
+ }
+
+ iterator
+ end () const
+ {
+ return records_.end ();
+ }
+
+ size_type
+ size () const
+ {
+ return records_.size ();
+ }
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual database_exception*
+ clone () const;
+
+ public:
+ ~database_exception () ODB_NOTHROW_NOEXCEPT;
+
+ database_exception ();
+ database_exception (SQLINTEGER error,
+ const std::string& sqlstate,
+ const std::string& message);
+
+ void
+ append (SQLINTEGER error,
+ const std::string& sqlstate,
+ const std::string& message);
+
+ private:
+ records records_;
+ std::string what_;
+ };
+
+ struct LIBODB_MSSQL_EXPORT cli_exception: odb::exception
+ {
+ cli_exception (const std::string& what);
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT;
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual cli_exception*
+ clone () const;
+
+ private:
+ std::string what_;
+ };
+
+ struct LIBODB_MSSQL_EXPORT long_data_reload: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual long_data_reload*
+ clone () const;
+ };
+
+ namespace core
+ {
+ using mssql::database_exception;
+ using mssql::cli_exception;
+ using mssql::long_data_reload;
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_EXCEPTIONS_HXX
diff --git a/libodb-mssql/odb/mssql/forward.hxx b/libodb-mssql/odb/mssql/forward.hxx
new file mode 100644
index 0000000..4f32b22
--- /dev/null
+++ b/libodb-mssql/odb/mssql/forward.hxx
@@ -0,0 +1,91 @@
+// file : odb/mssql/forward.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_FORWARD_HXX
+#define ODB_MSSQL_FORWARD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ namespace core
+ {
+ using namespace odb::common;
+ }
+
+ //
+ //
+ class database;
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+ class connection_factory;
+ class statement;
+ class transaction;
+ class tracer;
+
+ namespace core
+ {
+ using mssql::database;
+ using mssql::connection;
+ using mssql::connection_ptr;
+ using mssql::transaction;
+ using mssql::statement;
+ }
+
+ // Implementation details.
+ //
+ enum statement_kind
+ {
+ statement_select,
+ statement_insert,
+ statement_update,
+ statement_delete
+ };
+
+ class binding;
+ class select_statement;
+
+ template <typename T>
+ class object_statements;
+
+ template <typename T>
+ class polymorphic_root_object_statements;
+
+ template <typename T>
+ class polymorphic_derived_object_statements;
+
+ template <typename T>
+ class no_id_object_statements;
+
+ template <typename T>
+ class view_statements;
+
+ template <typename T>
+ class container_statements;
+
+ template <typename T>
+ class smart_container_statements;
+
+ template <typename T, typename ST>
+ class section_statements;
+
+ class query_base;
+ }
+
+ namespace details
+ {
+ template <>
+ struct counter_type<mssql::connection>
+ {
+ typedef shared_base counter;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_FORWARD_HXX
diff --git a/libodb-mssql/odb/mssql/mssql-fwd.hxx b/libodb-mssql/odb/mssql/mssql-fwd.hxx
new file mode 100644
index 0000000..44ac428
--- /dev/null
+++ b/libodb-mssql/odb/mssql/mssql-fwd.hxx
@@ -0,0 +1,216 @@
+// file : odb/mssql/mssql-fwd.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_MSSQL_FWD_HXX
+#define ODB_MSSQL_MSSQL_FWD_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+// Forward declaration for some of the types defined in sqltypes.h or
+// sqlncli.h. This allows us to avoid having to include these files
+// in public headers.
+//
+#ifdef _WIN32
+
+// Keep consistent with Windows ODBC headers.
+//
+
+typedef long SQLINTEGER;
+typedef unsigned long SQLUINTEGER;
+
+#ifdef _WIN64
+typedef __int64 SQLLEN;
+typedef unsigned __int64 SQLULEN;
+#else
+#ifndef SQLLEN
+typedef SQLINTEGER SQLLEN;
+typedef SQLUINTEGER SQLULEN;
+#endif
+#endif
+
+#else // _WIN32
+
+// Keep consistent with unixODBC headers.
+//
+
+template <std::size_t sizeof_long>
+struct odbc_types;
+
+template <>
+struct odbc_types<4>
+{
+ typedef long integer;
+ typedef unsigned long uinteger;
+
+ typedef integer len;
+ typedef uinteger ulen;
+};
+
+template <>
+struct odbc_types<8>
+{
+ typedef int integer;
+ typedef unsigned int uinteger;
+
+ typedef long len;
+ typedef unsigned long ulen;
+};
+
+typedef odbc_types<sizeof (long)>::integer SQLINTEGER;
+typedef odbc_types<sizeof (long)>::uinteger SQLUINTEGER;
+
+#ifndef SQLLEN
+typedef odbc_types<sizeof (long)>::len SQLLEN;
+typedef odbc_types<sizeof (long)>::ulen SQLULEN;
+#endif
+
+#endif // _WIN32
+
+typedef short SQLSMALLINT;
+typedef unsigned short SQLUSMALLINT;
+
+typedef SQLSMALLINT SQLRETURN;
+
+typedef void* SQLHANDLE;
+typedef SQLHANDLE SQLHENV;
+typedef SQLHANDLE SQLHDBC;
+typedef SQLHANDLE SQLHSTMT;
+typedef SQLHANDLE SQLHDESC;
+
+// If you get a redefinition error or warning for one of these macros,
+// then that means you included this header (or one that includes it),
+// before <sql.h> or <sqlext.h>. As a general rule, include <sql.h> or
+// <sqlext.h> before any of the ODB headers.
+//
+#ifndef SQL_HANDLE_ENV
+# define SQL_HANDLE_ENV 1
+# define SQL_HANDLE_DBC 2
+# define SQL_HANDLE_STMT 3
+# define SQL_HANDLE_DESC 4
+#endif
+
+#ifndef SQL_NULL_DATA
+# define SQL_NULL_DATA (-1)
+# define SQL_DATA_AT_EXEC (-2)
+# define SQL_NO_TOTAL (-4)
+#endif
+
+// The following types are our own equivalents of ODBC and Native Client
+// ODBC driver types. They are all PODs and should be layout-compatible
+// with the original types, which means they can be used interchangeably.
+//
+namespace odb
+{
+ namespace mssql
+ {
+ // UCS-2 character type (SQLWCHAR).
+ //
+#ifdef _WIN32
+ typedef wchar_t ucs2_char;
+#else
+ typedef unsigned short ucs2_char;
+#endif
+
+ // SQL_NUMERIC_STRUCT
+ //
+#ifndef SQL_MAX_NUMERIC_LEN
+#define SQL_MAX_NUMERIC_LEN 16
+#else
+# if SQL_MAX_NUMERIC_LEN != 16
+# error unexpected SQL_NUMERIC_STRUCT value
+# endif
+#endif
+
+ struct decimal
+ {
+ unsigned char precision;
+ signed char scale;
+ unsigned char sign; // 1 - positive, 0 - negative
+ unsigned char val[SQL_MAX_NUMERIC_LEN];
+ };
+
+ // DBMONEY
+ //
+ struct money
+ {
+ // 8-byte signed integer containing value * 10,000.
+ //
+ int high;
+ unsigned int low;
+ };
+
+ // DBMONEY4
+ //
+ struct smallmoney
+ {
+ int value; // 4-byte signed integer containing value * 10,000.
+ };
+
+ // SQL_DATE_STRUCT
+ //
+ struct date
+ {
+ SQLSMALLINT year;
+ SQLUSMALLINT month;
+ SQLUSMALLINT day;
+ };
+
+ // SQL_SS_TIME2_STRUCT
+ //
+#pragma pack(push,8)
+ struct time
+ {
+ SQLUSMALLINT hour;
+ SQLUSMALLINT minute;
+ SQLUSMALLINT second;
+ SQLUINTEGER fraction;
+ };
+#pragma pack(pop)
+
+ // SQL_TIMESTAMP_STRUCT
+ //
+ struct datetime
+ {
+ SQLSMALLINT year;
+ SQLUSMALLINT month;
+ SQLUSMALLINT day;
+ SQLUSMALLINT hour;
+ SQLUSMALLINT minute;
+ SQLUSMALLINT second;
+ SQLUINTEGER fraction;
+ };
+
+ // SQL_SS_TIMESTAMPOFFSET_STRUCT
+ //
+#pragma pack(push,8)
+ struct datetimeoffset
+ {
+ SQLSMALLINT year;
+ SQLUSMALLINT month;
+ SQLUSMALLINT day;
+ SQLUSMALLINT hour;
+ SQLUSMALLINT minute;
+ SQLUSMALLINT second;
+ SQLUINTEGER fraction;
+ SQLSMALLINT timezone_hour;
+ SQLSMALLINT timezone_minute;
+ };
+#pragma pack(pop)
+
+ // SQLGUID
+ //
+ struct uniqueidentifier
+ {
+ unsigned int data1;
+ unsigned short data2;
+ unsigned short data3;
+ unsigned char data4[8];
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_MSSQL_FWD_HXX
diff --git a/libodb-mssql/odb/mssql/mssql-types.hxx b/libodb-mssql/odb/mssql/mssql-types.hxx
new file mode 100644
index 0000000..b07aeb6
--- /dev/null
+++ b/libodb-mssql/odb/mssql/mssql-types.hxx
@@ -0,0 +1,161 @@
+// file : odb/mssql/mssql-types.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_MSSQL_TYPES_HXX
+#define ODB_MSSQL_MSSQL_TYPES_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/version.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ enum chunk_type
+ {
+ chunk_null,
+ chunk_one,
+ chunk_first,
+ chunk_next,
+ chunk_last,
+ };
+
+ typedef void (*param_callback_type) (
+ const void* context, // User context.
+ std::size_t* position, // Position context. An implementation is free
+ // to use this to track position information. It
+ // is initialized to zero before the first call.
+ const void** buffer, // [in/out] Buffer contaning the data. On the
+ // the first call it contains a pointer to the
+ // long_callback struct (used for redirections).
+ std::size_t* size, // [out] Data size.
+ chunk_type*, // [out] The position of this chunk of data.
+ void* temp_buffer, // A temporary buffer that may be used by the
+ // implementation.
+ std::size_t capacity); // Capacity of the temporary buffer.
+
+ typedef void (*result_callback_type) (
+ void* context, // User context.
+ std::size_t* position, // Position context. An implementation is free
+ // to use this to track position information. It
+ // is initialized to zero before the first call.
+ void** buffer, // [in/out] Buffer to copy the data to. On the
+ // the first call it contains a pointer to the
+ // long_callback struct (used for redirections).
+ std::size_t* size, // [in/out] In: amount of data copied into the
+ // buffer after the previous call. Out: capacity
+ // of the buffer.
+ chunk_type, // The position of this chunk; chunk_first means
+ // this is the first call, chunk_last means there
+ // is no more data, chunk_null means this value is
+ // NULL, and chunk_one means the value is empty.
+ std::size_t size_left, // Contains the amount of data left or 0 if this
+ // information is not available.
+ void* temp_buffer, // A temporary buffer that may be used by the
+ // implementation.
+ std::size_t capacity); // Capacity of the temporary buffer.
+
+ struct long_callback
+ {
+ union
+ {
+ param_callback_type param;
+ result_callback_type result;
+ } callback;
+
+ union
+ {
+ const void* param;
+ void* result;
+ } context;
+ };
+
+ struct bind
+ {
+ // This enumeration identifies the possible buffer types that can be
+ // bound to a parameter or result. In most cases, these map directly
+ // to SQL_XXX/SQL_C_XXX codes.
+ //
+ enum buffer_type
+ {
+ bit, // Buffer is a 1-byte integer.
+ tinyint, // Buffer is a 1-byte integer.
+ smallint, // Buffer is a 2-byte integer.
+ int_, // Buffer is a 4-byte integer.
+ bigint, // Buffer is an 8-byte integer.
+
+ decimal, // Buffer is a decimal struct (SQL_NUMERIC_STRUCT).
+
+ smallmoney, // Buffer is a smallmoney struct (DBMONEY4).
+ money, // Buffer is a money struct (DBMONEY).
+
+ float4, // Buffer is a float.
+ float8, // Buffer is a double.
+
+ string, // Buffer is a char array.
+ long_string, // Buffer is a long_callback.
+
+ nstring, // Buffer is a ucs2_char array.
+ long_nstring, // Buffer is a long_callback.
+
+ binary, // Buffer is a byte array.
+ long_binary, // Buffer is a long_callback.
+
+ date, // Buffer is a date struct (SQL_DATE_STRUCT).
+ time, // Buffer is a time struct (SQL_SS_TIME2_STRUCT).
+ datetime, // Buffer is a datetime struct
+ // (SQL_TIMESTAMP_STRUCT).
+ datetimeoffset, // Buffer is a datetimeoffset
+ // (SQL_SS_TIMESTAMPOFFSET_STRUCT).
+
+ uniqueidentifier, // Buffer is a uniqueidentifier struct (SQLGUID).
+ rowversion, // Buffer is an 8-byte array.
+
+ last // Used as an end of list marker.
+ };
+
+ buffer_type type; // The buffer type.
+ void* buffer; // The buffer. For long data this is a long_callback.
+ SQLLEN* size_ind; // Pointer to the size/inidicator variable. Size is
+ // ignored except for variable-size, [small]money, and
+ // rowversion types. Sepcial indicator values are
+ // SQL_NULL_DATA (value is NULL) and SQL_DATA_AT_EXEC
+ // (should be set for the long_* data types).
+ SQLLEN capacity; // Buffer capacity. Only used for variable-size
+ // types as well as to pass column/size precisions
+ // as follows: For string/binary parameters this
+ // value (minus one character for strings) is used
+ // as maximum column size. For decimal parameters
+ // it contains precision (p) and scale (s) encoded
+ // as (p * 100 + s). For float4 and float8 it
+ // contains precision. For time, datetime, and
+ // datatimeoffset it contains fractional seconds
+ // (scale). In case of datetime, the special
+ // value 8 indicates the SMALLDATETIME type
+ // which has no seconds.
+ };
+
+ // An instance of this structure specifies the function to invoke and
+ // the context to pass when the object/view image is about to be
+ // modified. This mechanism is used by the query machinery to save the
+ // image between result iteration and dereferencing if something gets
+ // executed between these two operations that would overwrite the
+ // image.
+ //
+ struct change_callback
+ {
+ change_callback (): callback (0), context (0) {};
+
+ void (*callback) (void*);
+ void* context;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_MSSQL_TYPES_HXX
diff --git a/libodb-mssql/odb/mssql/mssql.hxx b/libodb-mssql/odb/mssql/mssql.hxx
new file mode 100644
index 0000000..b0d2355
--- /dev/null
+++ b/libodb-mssql/odb/mssql/mssql.hxx
@@ -0,0 +1,69 @@
+// file : odb/mssql/mssql.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_MSSQL_HXX
+#define ODB_MSSQL_MSSQL_HXX
+
+#include <odb/pre.hxx>
+
+// This file should always be included before mssql-fwd.hxx.
+//
+#ifdef ODB_MSSQL_MSSQL_FWD_HXX
+# error odb/mssql/mssql-fwd.hxx included before odb/mssql/mssql.hxx
+#endif
+
+#ifdef _WIN32
+# include <odb/details/win32/windows.hxx>
+#endif
+
+#include <sqlext.h> // Standard ODBC.
+
+//#define _SQLNCLI_ODBC_
+//#include <sqlncli.h> // SQL Server Native Client driver specifics.
+
+// Instead of having a dependency on <sqlncli.h> (which, BTW, is not
+// currently available for the Linux version of the Native Client),
+// we are going to provide the few definitions that we need ourselves.
+//
+#ifndef SQL_SS_LENGTH_UNLIMITED
+# define SQL_SS_LENGTH_UNLIMITED 0
+#endif
+
+#ifndef SQL_COPT_SS_BASE
+# define SQL_COPT_SS_BASE 1200
+#endif
+
+#ifndef SQL_COPT_SS_MARS_ENABLED
+# define SQL_COPT_SS_MARS_ENABLED (SQL_COPT_SS_BASE + 24)
+#endif
+
+#ifndef SQL_MARS_ENABLED_NO
+# define SQL_MARS_ENABLED_NO 0L
+# define SQL_MARS_ENABLED_YES 1L
+#endif
+
+#ifndef SQL_COPT_SS_TXN_ISOLATION
+# define SQL_COPT_SS_TXN_ISOLATION (SQL_COPT_SS_BASE + 27)
+#endif
+
+#ifndef SQL_TXN_SS_SNAPSHOT
+# define SQL_TXN_SS_SNAPSHOT 0x00000020L
+#endif
+
+#ifndef SQL_SS_TIME2
+# define SQL_SS_TIME2 (-154)
+# define SQL_SS_TIMESTAMPOFFSET (-155)
+#endif
+
+// unixODBC doesn't define SQL_PARAM_DATA_AVAILABLE even though it
+// claims ODBC version 3.80.
+//
+#if ODBCVER >= 0x0380
+# ifndef SQL_PARAM_DATA_AVAILABLE
+# define SQL_PARAM_DATA_AVAILABLE 101
+# endif
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_MSSQL_HXX
diff --git a/libodb-mssql/odb/mssql/no-id-object-result.hxx b/libodb-mssql/odb/mssql/no-id-object-result.hxx
new file mode 100644
index 0000000..ad674bc
--- /dev/null
+++ b/libodb-mssql/odb/mssql/no-id-object-result.hxx
@@ -0,0 +1,85 @@
+// file : odb/mssql/no-id-object-result.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_NO_ID_OBJECT_RESULT_HXX
+#define ODB_MSSQL_NO_ID_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/no-id-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx> // query_base
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ class no_id_object_result_impl: public odb::no_id_object_result_impl<T>
+ {
+ public:
+ typedef odb::no_id_object_result_impl<T> base_type;
+
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_mssql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~no_id_object_result_impl ();
+
+ no_id_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ typedef mssql::change_callback change_callback_type;
+
+ static void
+ change_callback (void* context);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ bool can_load_;
+ bool use_copy_;
+ typename object_traits::image_type* image_copy_;
+ };
+ }
+}
+
+#include <odb/mssql/no-id-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_NO_ID_OBJECT_RESULT_HXX
diff --git a/libodb-mssql/odb/mssql/no-id-object-result.txx b/libodb-mssql/odb/mssql/no-id-object-result.txx
new file mode 100644
index 0000000..06b7d15
--- /dev/null
+++ b/libodb-mssql/odb/mssql/no-id-object-result.txx
@@ -0,0 +1,154 @@
+// file : odb/mssql/no-id-object-result.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/mssql/exceptions.hxx> // long_data_reload
+#include <odb/mssql/no-id-object-statements.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ no_id_object_result_impl<T>::
+ ~no_id_object_result_impl ()
+ {
+ invalidate ();
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ invalidate ()
+ {
+ change_callback_type& cc (statements_.image ().change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ delete image_copy_;
+ image_copy_ = 0;
+
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ no_id_object_result_impl<T>::
+ no_id_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ use_copy_ (false),
+ image_copy_ (0)
+ {
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ load (object_type& obj)
+ {
+ if (!can_load_)
+ throw long_data_reload ();
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+
+ tc_.init (obj,
+ use_copy_ ? *image_copy_ : statements_.image (),
+ &this->db_);
+
+ // If we are using a copy, make sure the callback information for
+ // long data also comes from the copy.
+ //
+ can_load_ = !statement_->stream_result (
+ use_copy_ ? &statements_.image () : 0,
+ use_copy_ ? image_copy_ : 0);
+
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ next ()
+ {
+ can_load_ = true;
+ this->current (pointer_type ());
+
+ typename object_traits::image_type& im (statements_.image ());
+ change_callback_type& cc (im.change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ use_copy_ = false;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+
+ if (statement_->fetch () == select_statement::no_data)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ else
+ {
+ cc.callback = &change_callback;
+ cc.context = this;
+ }
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t no_id_object_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ change_callback (void* c)
+ {
+ no_id_object_result_impl<T>* r (
+ static_cast<no_id_object_result_impl<T>*> (c));
+
+ typename object_traits::image_type im (r->statements_.image ());
+
+ if (r->image_copy_ == 0)
+ r->image_copy_ = new typename object_traits::image_type (im);
+ else
+ *r->image_copy_ = im;
+
+ im.change_callback_.callback = 0;
+ im.change_callback_.context = 0;
+
+ r->use_copy_ = true;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/no-id-object-statements.hxx b/libodb-mssql/odb/mssql/no-id-object-statements.hxx
new file mode 100644
index 0000000..8d49355
--- /dev/null
+++ b/libodb-mssql/odb/mssql/no-id-object-statements.hxx
@@ -0,0 +1,137 @@
+// file : odb/mssql/no-id-object-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_NO_ID_OBJECT_STATEMENTS_HXX
+#define ODB_MSSQL_NO_ID_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/mssql-types.hxx>
+#include <odb/mssql/binding.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/statements-base.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // Implementation for objects without object id.
+ //
+
+ template <typename T>
+ class no_id_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mssql> object_traits;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef mssql::insert_statement insert_statement_type;
+
+ public:
+ no_id_object_statements (connection_type&);
+
+ virtual
+ ~no_id_object_statements ();
+
+ // Object image.
+ //
+ image_type&
+ image (std::size_t i = 0)
+ {
+ return image_[i];
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Select binding (needed for query support).
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ false,
+ false,
+ 0,
+ false));
+
+ return *persist_;
+ }
+
+ public:
+ // select = total
+ // insert = total - inverse; inverse == 0 for object without id
+ //
+ static const std::size_t insert_column_count =
+ object_traits::column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count;
+
+ private:
+ no_id_object_statements (const no_id_object_statements&);
+ no_id_object_statements& operator= (const no_id_object_statements&);
+
+ private:
+ image_type image_[object_traits::batch];
+ SQLUSMALLINT status_[object_traits::batch];
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ bind select_image_bind_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ };
+ }
+}
+
+#include <odb/mssql/no-id-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_NO_ID_OBJECT_STATEMENTS_HXX
diff --git a/libodb-mssql/odb/mssql/no-id-object-statements.txx b/libodb-mssql/odb/mssql/no-id-object-statements.txx
new file mode 100644
index 0000000..30cc438
--- /dev/null
+++ b/libodb-mssql/odb/mssql/no-id-object-statements.txx
@@ -0,0 +1,39 @@
+// file : odb/mssql/no-id-object-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ no_id_object_statements<T>::
+ ~no_id_object_statements ()
+ {
+ }
+
+ template <typename T>
+ no_id_object_statements<T>::
+ no_id_object_statements (connection_type& conn)
+ : statements_base (conn),
+ select_image_binding_ (select_image_bind_, select_column_count),
+ insert_image_binding_ (insert_image_bind_,
+ insert_column_count,
+ object_traits::batch,
+ sizeof (image_type),
+ status_)
+ {
+ image_[0].version = 0; // Only version in the first element used.
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+
+ // SELECT statements only use the first element (no batches).
+ //
+ select_image_binding_.change_callback = image_[0].change_callback ();
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/polymorphic-object-result.hxx b/libodb-mssql/odb/mssql/polymorphic-object-result.hxx
new file mode 100644
index 0000000..5ee9642
--- /dev/null
+++ b/libodb-mssql/odb/mssql/polymorphic-object-result.hxx
@@ -0,0 +1,99 @@
+// file : odb/mssql/polymorphic-object-result.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_POLYMORPHIC_OBJECT_RESULT_HXX
+#define ODB_MSSQL_POLYMORPHIC_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/polymorphic-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx> // query_base
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ class polymorphic_object_result_impl:
+ public odb::polymorphic_object_result_impl<T>
+ {
+ public:
+ typedef odb::polymorphic_object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_mssql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename base_type::root_type root_type;
+ typedef typename base_type::discriminator_type discriminator_type;
+
+ typedef object_traits_impl<root_type, id_mssql> root_traits;
+
+ typedef typename object_traits::image_type image_type;
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~polymorphic_object_result_impl ();
+
+ polymorphic_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type*, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual discriminator_type
+ load_discriminator ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ typedef mssql::change_callback change_callback_type;
+
+ static void
+ change_callback (void* context);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ bool can_load_;
+ bool use_copy_;
+ image_type* image_copy_;
+ };
+ }
+}
+
+#include <odb/mssql/polymorphic-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_POLYMORPHIC_OBJECT_RESULT_HXX
diff --git a/libodb-mssql/odb/mssql/polymorphic-object-result.txx b/libodb-mssql/odb/mssql/polymorphic-object-result.txx
new file mode 100644
index 0000000..159010f
--- /dev/null
+++ b/libodb-mssql/odb/mssql/polymorphic-object-result.txx
@@ -0,0 +1,325 @@
+// file : odb/mssql/polymorphic-object-result.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/mssql/exceptions.hxx> // long_data_reload
+#include <odb/mssql/polymorphic-object-statements.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ ~polymorphic_object_result_impl ()
+ {
+ invalidate ();
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ invalidate ()
+ {
+ change_callback_type& cc (
+ statements_.root_statements ().image ().change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.context = 0;
+ cc.callback = 0;
+ }
+
+ if (image_copy_ != 0)
+ {
+ object_traits::free_image (image_copy_);
+ image_copy_ = 0;
+ }
+
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ polymorphic_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> st,
+ statements_type& sts,
+ const schema_version_migration* svm)
+ : base_type (sts.connection ()),
+ statement_ (st),
+ statements_ (sts),
+ tc_ (svm),
+ use_copy_ (false),
+ image_copy_ (0)
+ {
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ load (object_type* pobj, bool)
+ {
+ if (!can_load_)
+ throw long_data_reload ();
+
+ typename statements_type::root_statements_type& rsts (
+ statements_.root_statements ());
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!rsts.locked ());
+ typename statements_type::auto_lock l (rsts);
+
+ image_type& i (use_copy_ ? *image_copy_ : statements_.image ());
+ typename root_traits::image_type& ri (
+ use_copy_ ? object_traits::root_image (i) : rsts.image ());
+
+ id_type id (root_traits::id (ri));
+
+ // Determine this object's dynamic type.
+ //
+ typedef typename root_traits::info_type info_type;
+ discriminator_type d (root_traits::discriminator (ri));
+
+ // Use the polymorphic_info() helper to get concrete_info if
+ // object_type is concrete and NULL if it is abstract.
+ //
+ const info_type* spi (polymorphic_info (object_traits::info));
+ const info_type& pi (
+ spi != 0 && spi->discriminator == d
+ ? *spi
+ : root_traits::map->find (d));
+
+ typedef typename root_traits::pointer_type root_pointer_type;
+ typedef typename root_traits::pointer_traits root_pointer_traits;
+
+ typename object_traits::pointer_cache_traits::insert_guard ig;
+
+ if (pobj == 0)
+ {
+ // Need to create a new instance of the dynamic type.
+ //
+ root_pointer_type rp (pi.create ());
+ pointer_type p (
+ root_pointer_traits::template static_pointer_cast<object_type> (rp));
+
+ // Insert it as a root pointer (for non-unique pointers, rp should
+ // still be valid and for unique pointers this is a no-op).
+ //
+ ig.reset (
+ object_traits::pointer_cache_traits::insert (this->db_, id, rp));
+
+ pobj = &pointer_traits::get_ref (p);
+ current (p);
+ }
+ else
+ {
+ // We are loading into an existing instance. If the static and
+ // dynamic types differ, then make sure the instance is at least
+ // of the dynamic type.
+ //
+ if (&pi != &object_traits::info)
+ {
+ const info_type& dpi (root_traits::map->find (typeid (*pobj)));
+
+ if (&dpi != &pi && dpi.derived (pi))
+ throw object_not_persistent (); // @@ type_mismatch ?
+ }
+ }
+
+ callback_event ce (callback_event::pre_load);
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+
+ tc_.init (*pobj, i, &this->db_);
+
+ // If we are using a copy, make sure the callback information for
+ // long data also comes from the copy.
+ //
+ can_load_ = !statement_->stream_result (
+ use_copy_ ? &statements_.image () : 0,
+ use_copy_ ? image_copy_ : 0);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, dynamic part, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ root_traits::init (idi, id);
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, *pobj, false);
+
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ if (&pi != &object_traits::info)
+ {
+ std::size_t d (object_traits::depth);
+ pi.dispatch (info_type::call_load, this->db_, pobj, &d);
+ };
+
+ rsts.load_delayed (tc_.version ());
+ l.unlock ();
+
+ ce = callback_event::post_load;
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+ object_traits::pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::id_type
+ polymorphic_object_result_impl<T>::
+ load_id ()
+ {
+ typename root_traits::image_type& i (
+ use_copy_
+ ? object_traits::root_image (*image_copy_)
+ : statements_.root_statements ().image ());
+
+ return root_traits::id (i);
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::discriminator_type
+ polymorphic_object_result_impl<T>::
+ load_discriminator ()
+ {
+ typename root_traits::image_type& i (
+ use_copy_
+ ? object_traits::root_image (*image_copy_)
+ : statements_.root_statements ().image ());
+
+ return root_traits::discriminator (i);
+ }
+
+ template <typename T, typename R>
+ struct polymorphic_image_rebind
+ {
+ // Derived type version.
+ //
+ typedef object_traits_impl<T, id_mssql> traits;
+
+ static void
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (traits::check_version (sts.select_image_versions (), im))
+ {
+ binding& b (sts.select_image_binding (traits::depth));
+ object_traits_calls<T> tc (svm);
+ tc.bind (b.bind, 0, 0, im, statement_select);
+ traits::update_version (
+ sts.select_image_versions (), im, sts.select_image_bindings ());
+ }
+ }
+ };
+
+ template <typename R>
+ struct polymorphic_image_rebind<R, R>
+ {
+ // Root type version.
+ //
+ typedef object_traits_impl<R, id_mssql> traits;
+
+ static void
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (im.version != sts.select_image_version ())
+ {
+ binding& b (sts.select_image_binding ());
+ object_traits_calls<R> tc (svm);
+ tc.bind (b.bind, im, statement_select);
+ sts.select_image_version (im.version);
+ b.version++;
+ }
+ }
+ };
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ next ()
+ {
+ can_load_ = true;
+ this->current (pointer_type ());
+
+ change_callback_type& cc (
+ statements_.root_statements ().image ().change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ use_copy_ = false;
+ polymorphic_image_rebind<object_type, root_type>::rebind (
+ statements_, tc_.version ());
+
+ if (statement_->fetch () == select_statement::no_data)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ else
+ {
+ cc.callback = &change_callback;
+ cc.context = this;
+ }
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t polymorphic_object_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ change_callback (void* c)
+ {
+ polymorphic_object_result_impl<T>* r (
+ static_cast<polymorphic_object_result_impl<T>*> (c));
+ image_type& im (r->statements_.image ());
+
+ if (r->image_copy_ == 0)
+ r->image_copy_ = object_traits::clone_image (im);
+ else
+ object_traits::copy_image (*r->image_copy_, im);
+
+ typename root_traits::image_type& rim (
+ r->statements_.root_statements ().image ());
+
+ rim.change_callback_.callback = 0;
+ rim.change_callback_.context = 0;
+
+ r->use_copy_ = true;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/polymorphic-object-statements.hxx b/libodb-mssql/odb/mssql/polymorphic-object-statements.hxx
new file mode 100644
index 0000000..c115502
--- /dev/null
+++ b/libodb-mssql/odb/mssql/polymorphic-object-statements.hxx
@@ -0,0 +1,469 @@
+// file : odb/mssql/polymorphic-object-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+#define ODB_MSSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/mssql-types.hxx>
+#include <odb/mssql/binding.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/statements-base.hxx>
+#include <odb/mssql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // Implementation for polymorphic objects.
+ //
+
+ template <typename T>
+ class polymorphic_root_object_statements: public object_statements<T>
+ {
+ public:
+ typedef typename object_statements<T>::connection_type connection_type;
+ typedef typename object_statements<T>::object_traits object_traits;
+ typedef typename object_statements<T>::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::discriminator_image_type
+ discriminator_image_type;
+
+ typedef
+ typename object_statements<T>::select_statement_type
+ select_statement_type;
+
+ public:
+ // Interface compatibility with derived_object_statements.
+ //
+ typedef polymorphic_root_object_statements root_statements_type;
+
+ root_statements_type&
+ root_statements ()
+ {
+ return *this;
+ }
+
+ public:
+ // Discriminator binding.
+ //
+ discriminator_image_type&
+ discriminator_image () {return discriminator_image_;}
+
+ std::size_t
+ discriminator_image_version () const
+ {return discriminator_image_version_;}
+
+ void
+ discriminator_image_version (std::size_t v)
+ {discriminator_image_version_ = v;}
+
+ binding&
+ discriminator_image_binding () {return discriminator_image_binding_;}
+
+ // Id binding for discriminator retrieval.
+ //
+ id_image_type&
+ discriminator_id_image () {return discriminator_id_image_;}
+
+ std::size_t
+ discriminator_id_image_version () const
+ {return discriminator_id_image_version_;}
+
+ void
+ discriminator_id_image_version (std::size_t v)
+ {discriminator_id_image_version_ = v;}
+
+ binding&
+ discriminator_id_image_binding ()
+ {return discriminator_id_image_binding_;}
+
+ //
+ //
+ select_statement_type&
+ find_discriminator_statement ()
+ {
+ if (find_discriminator_ == 0)
+ find_discriminator_.reset (
+ new (details::shared) select_statement_type (
+ this->conn_,
+ object_traits::find_discriminator_statement,
+ false, // Doesn't need to be processed.
+ false, // Don't optimize.
+ discriminator_id_image_binding_,
+ discriminator_image_binding_,
+ false));
+
+ return *find_discriminator_;
+ }
+
+ public:
+ polymorphic_root_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_root_object_statements ();
+
+ // Static "override" (statements type).
+ //
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (this->locked ());
+
+ if (!this->delayed_.empty ())
+ this->template load_delayed_<polymorphic_root_object_statements> (
+ svm);
+ }
+
+ public:
+ static const std::size_t id_column_count =
+ object_statements<T>::id_column_count;
+
+ static const std::size_t discriminator_column_count =
+ object_traits::discriminator_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ private:
+ // Discriminator image.
+ //
+ discriminator_image_type discriminator_image_;
+ std::size_t discriminator_image_version_;
+ binding discriminator_image_binding_;
+ bind discriminator_image_bind_[discriminator_column_count +
+ managed_optimistic_column_count];
+
+ // Id image for discriminator retrieval (only used as a parameter).
+ //
+ id_image_type discriminator_id_image_;
+ std::size_t discriminator_id_image_version_;
+ binding discriminator_id_image_binding_;
+ bind discriminator_id_image_bind_[id_column_count];
+
+ details::shared_ptr<select_statement_type> find_discriminator_;
+ };
+
+ template <typename T>
+ class polymorphic_derived_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mssql> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::id_image_type id_image_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef typename object_traits::root_type root_type;
+ typedef
+ polymorphic_root_object_statements<root_type>
+ root_statements_type;
+
+ typedef typename object_traits::base_type base_type;
+ typedef
+ typename object_traits::base_traits::statements_type
+ base_statements_type;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef mssql::insert_statement insert_statement_type;
+ typedef mssql::select_statement select_statement_type;
+ typedef mssql::update_statement update_statement_type;
+ typedef mssql::delete_statement delete_statement_type;
+
+ typedef typename root_statements_type::auto_lock auto_lock;
+
+ public:
+ polymorphic_derived_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_derived_object_statements ();
+
+ public:
+ // Delayed loading.
+ //
+ static void
+ delayed_loader (odb::database&,
+ const id_type&,
+ root_type&,
+ const schema_version_migration*);
+
+ public:
+ // Root and immediate base statements.
+ //
+ root_statements_type&
+ root_statements ()
+ {
+ return root_statements_;
+ }
+
+ base_statements_type&
+ base_statements ()
+ {
+ return base_statements_;
+ }
+
+ public:
+ // Object image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ std::size_t
+ insert_id_binding_version () const { return insert_id_binding_version_;}
+
+ void
+ insert_id_binding_version (std::size_t v) {insert_id_binding_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t*
+ select_image_versions () { return select_image_versions_;}
+
+ binding*
+ select_image_bindings () {return select_image_bindings_;}
+
+ binding&
+ select_image_binding (std::size_t d)
+ {
+ return select_image_bindings_[object_traits::depth - d];
+ }
+
+ // Object id binding (comes from the root statements).
+ //
+ id_image_type&
+ id_image () {return root_statements_.id_image ();}
+
+ std::size_t
+ id_image_version () const {return root_statements_.id_image_version ();}
+
+ void
+ id_image_version (std::size_t v) {root_statements_.id_image_version (v);}
+
+ binding&
+ id_image_binding () {return root_statements_.id_image_binding ();}
+
+ binding&
+ optimistic_id_image_binding () {
+ return root_statements_.optimistic_id_image_binding ();}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ // Auto id and version are in the root.
+ //
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ false,
+ false,
+ 0));
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement (std::size_t d)
+ {
+ std::size_t i (object_traits::depth - d);
+ details::shared_ptr<select_statement_type>& p (find_[i]);
+
+ if (p == 0)
+ p.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statements[i],
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ root_statements_.id_image_binding (),
+ select_image_bindings_[i],
+ false));
+
+ return *p;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
+ update_image_binding_,
+ 0,
+ false));
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement,
+ root_statements_.id_image_binding (),
+ false));
+
+ return *erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ image_,
+ id_image (),
+ id_image_binding (),
+ &id_image_binding ()); // Note, not id+version.
+ }
+
+ public:
+ // select = total - id - separate_load + base::select
+ // insert = total - inverse
+ // update = total - inverse - id - readonly - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ id_column_count -
+ object_traits::separate_load_column_count +
+ base_statements_type::select_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count;
+
+ static const std::size_t update_column_count = insert_column_count -
+ object_traits::id_column_count -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ polymorphic_derived_object_statements (
+ const polymorphic_derived_object_statements&);
+
+ polymorphic_derived_object_statements&
+ operator= (const polymorphic_derived_object_statements&);
+
+ private:
+ root_statements_type& root_statements_;
+ base_statements_type& base_statements_;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ image_type image_;
+
+ // Select binding. Here we are have an array of statements/bindings
+ // one for each depth. In other words, if we have classes root, base,
+ // and derived, then we have the following array of statements:
+ //
+ // [0] d + b + r
+ // [1] d + b
+ // [2] d
+ //
+ // Also, because we have a chain of images bound to these statements,
+ // we have an array of versions, one entry for each base plus one for
+ // our own image.
+ //
+ // A poly-abstract class only needs the first statement and in this
+ // case we have only one entry in the the bindings and statements
+ // arrays (but not versions; we still have a chain of images).
+ //
+ std::size_t select_image_versions_[object_traits::depth];
+ binding select_image_bindings_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ bind select_image_bind_[select_column_count];
+
+ // Insert binding. The id binding is copied from the hierarchy root.
+ //
+ std::size_t insert_image_version_;
+ std::size_t insert_id_binding_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+
+ // Update binding. The id suffix binding is copied from the hierarchy
+ // root.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+ binding update_image_binding_;
+ bind update_image_bind_[update_column_count + id_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+ };
+ }
+}
+
+#include <odb/mssql/polymorphic-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
diff --git a/libodb-mssql/odb/mssql/polymorphic-object-statements.txx b/libodb-mssql/odb/mssql/polymorphic-object-statements.txx
new file mode 100644
index 0000000..0ba437a
--- /dev/null
+++ b/libodb-mssql/odb/mssql/polymorphic-object-statements.txx
@@ -0,0 +1,140 @@
+// file : odb/mssql/polymorphic-object-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/transaction.hxx>
+#include <odb/mssql/statement-cache.hxx>
+#include <odb/mssql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // polymorphic_root_object_statements
+ //
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ ~polymorphic_root_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ polymorphic_root_object_statements (connection_type& conn)
+ : object_statements<T> (conn),
+ discriminator_image_binding_ (discriminator_image_bind_,
+ discriminator_column_count +
+ managed_optimistic_column_count),
+ discriminator_id_image_binding_ (discriminator_id_image_bind_,
+ id_column_count)
+ {
+ discriminator_image_.version = 0;
+ discriminator_id_image_.version = 0;
+
+ discriminator_image_version_ = 0;
+ discriminator_id_image_version_ = 0;
+
+ std::memset (
+ discriminator_image_bind_, 0, sizeof (discriminator_image_bind_));
+ std::memset (
+ discriminator_id_image_bind_, 0, sizeof (discriminator_id_image_bind_));
+ }
+
+ //
+ // polymorphic_derived_object_statements
+ //
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ ~polymorphic_derived_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ polymorphic_derived_object_statements (connection_type& conn)
+ : statements_base (conn),
+ root_statements_ (conn.statement_cache ().find_object<root_type> ()),
+ base_statements_ (conn.statement_cache ().find_object<base_type> ()),
+ insert_image_binding_ (insert_image_bind_, insert_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count)
+ {
+ image_.base = &base_statements_.image ();
+ image_.version = 0;
+
+ for (std::size_t i (0); i < object_traits::depth; ++i)
+ select_image_versions_[i] = 0;
+
+ for (std::size_t i (0);
+ i < (object_traits::abstract ? 1 : object_traits::depth);
+ ++i)
+ {
+ select_image_bindings_[i].bind = select_image_bind_;
+ select_image_bindings_[i].count = object_traits::find_column_counts[i];
+ select_image_bindings_[i].change_callback = 0;
+ }
+
+ // Statements other than the first one (which goes all the way to
+ // the root) can never override the image because they are used to
+ // load up the dynamic part of the object only after the static
+ // part has been loaded (and triggered the callback if necessary).
+ //
+ select_image_bindings_[0].change_callback =
+ root_statements_.image ().change_callback ();
+
+ insert_image_version_ = 0;
+ insert_id_binding_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ }
+
+ template <typename T>
+ void polymorphic_derived_object_statements<T>::
+ delayed_loader (odb::database& db,
+ const id_type& id,
+ root_type& robj,
+ const schema_version_migration* svm)
+ {
+ connection_type& conn (transaction::current ().connection (db));
+ polymorphic_derived_object_statements& sts (
+ conn.statement_cache ().find_object<object_type> ());
+ root_statements_type& rsts (sts.root_statements ());
+
+ object_type& obj (static_cast<object_type&> (robj));
+
+ // The same code as in object_statements::load_delayed_().
+ //
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (sts, &id))
+ throw object_not_persistent ();
+
+ auto_result ar (*sts.find_[0]);
+
+ object_traits::callback (db, obj, callback_event::pre_load);
+ tc.init (obj, sts.image (), &db);
+ sts.find_[0]->stream_result ();
+ ar.free ();
+ tc.load_ (sts, obj, false); // Load containers, etc.
+
+ rsts.load_delayed (svm);
+
+ {
+ typename root_statements_type::auto_unlock u (rsts);
+ object_traits::callback (db, obj, callback_event::post_load);
+ }
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/prepared-query.cxx b/libodb-mssql/odb/mssql/prepared-query.cxx
new file mode 100644
index 0000000..ae0389d
--- /dev/null
+++ b/libodb-mssql/odb/mssql/prepared-query.cxx
@@ -0,0 +1,15 @@
+// file : odb/mssql/prepared-query.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/prepared-query.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ prepared_query_impl::
+ ~prepared_query_impl ()
+ {
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/prepared-query.hxx b/libodb-mssql/odb/mssql/prepared-query.hxx
new file mode 100644
index 0000000..730f7ec
--- /dev/null
+++ b/libodb-mssql/odb/mssql/prepared-query.hxx
@@ -0,0 +1,34 @@
+// file : odb/mssql/prepared-query.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_PREPARED_QUERY_HXX
+#define ODB_MSSQL_PREPARED_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/prepared-query.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/query.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ struct LIBODB_MSSQL_EXPORT prepared_query_impl: odb::prepared_query_impl
+ {
+ virtual
+ ~prepared_query_impl ();
+
+ prepared_query_impl (odb::connection& c): odb::prepared_query_impl (c) {}
+
+ mssql::query_base query;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_PREPARED_QUERY_HXX
diff --git a/libodb-mssql/odb/mssql/query-const-expr.cxx b/libodb-mssql/odb/mssql/query-const-expr.cxx
new file mode 100644
index 0000000..4e28fad
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query-const-expr.cxx
@@ -0,0 +1,14 @@
+// file : odb/mssql/query-const-expr.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/query.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ // Sun CC cannot handle this in query.cxx.
+ //
+ const query_base query_base::true_expr (true);
+ }
+}
diff --git a/libodb-mssql/odb/mssql/query-dynamic.cxx b/libodb-mssql/odb/mssql/query-dynamic.cxx
new file mode 100644
index 0000000..fb49b2b
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query-dynamic.cxx
@@ -0,0 +1,157 @@
+// file : odb/mssql/query-dynamic.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+
+#include <odb/mssql/query-dynamic.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ static const char* logic_operators[] = {") AND (", ") OR ("};
+ static const char* comp_operators[] = {"=", "!=", "<", ">", "<=", ">="};
+
+ static void
+ translate (query_base& q, const odb::query_base& s, size_t p)
+ {
+ typedef odb::query_base::clause_part part;
+
+ const part& x (s.clause ()[p]);
+
+ switch (x.kind)
+ {
+ case part::kind_column:
+ {
+ const query_column_base* c (
+ static_cast<const query_column_base*> (
+ x.native_info[id_mssql].column));
+
+ q.append (c->table (), c->column ());
+ break;
+ }
+ case part::kind_param_val:
+ case part::kind_param_ref:
+ {
+ const query_column_base& qc (
+ *static_cast<const query_column_base*> (
+ x.native_info[id_mssql].column));
+
+ query_param_factory f (
+ reinterpret_cast<query_param_factory> (
+ x.native_info[id_mssql].param_factory));
+
+ const odb::query_param* p (
+ reinterpret_cast<const odb::query_param*> (x.data));
+
+ q.append (f (p->value, qc, x.kind == part::kind_param_ref),
+ qc.conversion ());
+ break;
+ }
+ case part::kind_native:
+ {
+ q.append (s.strings ()[x.data]);
+ break;
+ }
+ case part::kind_true:
+ case part::kind_false:
+ {
+ q.append (x.kind == part::kind_true);
+ break;
+ }
+ case part::op_add:
+ {
+ translate (q, s, x.data);
+ translate (q, s, p - 1);
+ break;
+ }
+ case part::op_and:
+ case part::op_or:
+ {
+ q += "(";
+ translate (q, s, x.data);
+ q += logic_operators[x.kind - part::op_and];
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_not:
+ {
+ q += "NOT (";
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_null:
+ case part::op_not_null:
+ {
+ translate (q, s, p - 1);
+ q += (x.kind == part::op_null ? "IS NULL" : "IS NOT NULL");
+ break;
+ }
+ case part::op_in:
+ {
+ if (x.data != 0)
+ {
+ size_t b (p - x.data);
+
+ translate (q, s, b - 1); // column
+ q += "IN (";
+
+ for (size_t i (b); i != p; ++i)
+ {
+ if (i != b)
+ q += ",";
+
+ translate (q, s, i);
+ }
+
+ q += ")";
+ }
+ else
+ q.append (false);
+
+ break;
+ }
+ case part::op_like:
+ {
+ translate (q, s, p - 2); // column
+ q += "LIKE";
+ translate (q, s, p - 1); // pattern
+ break;
+ }
+ case part::op_like_escape:
+ {
+ translate (q, s, p - 3); // column
+ q += "LIKE";
+ translate (q, s, p - 2); // pattern
+ q += "ESCAPE";
+ translate (q, s, p - 1); // escape
+ break;
+ }
+ case part::op_eq:
+ case part::op_ne:
+ case part::op_lt:
+ case part::op_gt:
+ case part::op_le:
+ case part::op_ge:
+ {
+ translate (q, s, x.data);
+ q += comp_operators[x.kind - part::op_eq];
+ translate (q, s, p - 1);
+ break;
+ }
+ }
+ }
+
+ query_base::
+ query_base (const odb::query_base& q)
+ : binding_ (0, 0)
+ {
+ if (!q.empty ())
+ translate (*this, q, q.clause ().size () - 1);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/query-dynamic.hxx b/libodb-mssql/odb/mssql/query-dynamic.hxx
new file mode 100644
index 0000000..ceb423b
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query-dynamic.hxx
@@ -0,0 +1,32 @@
+// file : odb/mssql/query-dynamic.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_QUERY_DYNAMIC_HXX
+#define ODB_MSSQL_QUERY_DYNAMIC_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/query.hxx>
+#include <odb/query-dynamic.hxx>
+
+#include <odb/mssql/query.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ typedef details::shared_ptr<query_param> (*query_param_factory) (
+ const void* val, const query_column_base&, bool by_ref);
+
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void*, const query_column_base&, bool);
+ }
+}
+
+#include <odb/mssql/query-dynamic.ixx>
+#include <odb/mssql/query-dynamic.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_QUERY_DYNAMIC_HXX
diff --git a/libodb-mssql/odb/mssql/query-dynamic.ixx b/libodb-mssql/odb/mssql/query-dynamic.ixx
new file mode 100644
index 0000000..005175f
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query-dynamic.ixx
@@ -0,0 +1,30 @@
+// file : odb/mssql/query-dynamic.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ //
+ template <typename T, database_type_id ID>
+ inline query_column<T, ID>::
+ query_column (odb::query_column<T>& qc,
+ const char* table,
+ const char* column,
+ const char* conv,
+ unsigned short prec,
+ unsigned short scale)
+ : query_column_base (table, column, conv, prec, scale)
+ {
+ native_column_info& ci (qc.native_info[id_mssql]);
+ ci.column = static_cast<query_column_base*> (this);
+
+ // For some reason GCC needs this statically-typed pointer in
+ // order to instantiate the functions.
+ //
+ query_param_factory f (&query_param_factory_impl<T, ID>);
+ ci.param_factory = reinterpret_cast<void*> (f);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/query-dynamic.txx b/libodb-mssql/odb/mssql/query-dynamic.txx
new file mode 100644
index 0000000..8683c0f
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query-dynamic.txx
@@ -0,0 +1,25 @@
+// file : odb/mssql/query-dynamic.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void* val,
+ const query_column_base& qc,
+ bool by_ref)
+ {
+ const T& v (*static_cast<const T*> (val));
+
+ unsigned short p (qc.prec ());
+ unsigned short s (qc.scale ());
+
+ return details::shared_ptr<query_param> (
+ by_ref
+ ? new (details::shared) query_param_impl<T, ID> (ref_bind<T> (v, p, s))
+ : new (details::shared) query_param_impl<T, ID> (val_bind<T> (v, p, s)));
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/query.cxx b/libodb-mssql/odb/mssql/query.cxx
new file mode 100644
index 0000000..6dd87e7
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query.cxx
@@ -0,0 +1,406 @@
+// file : odb/mssql/query.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset, std::memcpy
+
+#include <odb/mssql/query.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ // query_param
+ //
+ query_param::
+ ~query_param ()
+ {
+ }
+
+ // query_base
+ //
+ query_base::
+ query_base (const query_base& q)
+ : clause_ (q.clause_),
+ parameters_ (q.parameters_),
+ bind_ (q.bind_),
+ binding_ (0, 0)
+ {
+ // Here and below we want to maintain up to date binding info so
+ // that the call to parameters_binding() below is an immutable
+ // operation, provided the query does not have any by-reference
+ // parameters. This way a by-value-only query can be shared
+ // between multiple threads without the need for synchronization.
+ //
+ if (size_t n = bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = n;
+ binding_.version++;
+ }
+ }
+
+ query_base& query_base::
+ operator= (const query_base& q)
+ {
+ if (this != &q)
+ {
+ clause_ = q.clause_;
+ parameters_ = q.parameters_;
+ bind_ = q.bind_;
+
+ size_t n (bind_.size ());
+ binding_.bind = n != 0 ? &bind_[0] : 0;
+ binding_.count = n;
+ binding_.version++;
+ }
+
+ return *this;
+ }
+
+ query_base& query_base::
+ operator+= (const query_base& q)
+ {
+ clause_.insert (clause_.end (), q.clause_.begin (), q.clause_.end ());
+
+ size_t n (bind_.size ());
+
+ parameters_.insert (
+ parameters_.end (), q.parameters_.begin (), q.parameters_.end ());
+
+ bind_.insert (
+ bind_.end (), q.bind_.begin (), q.bind_.end ());
+
+ if (n != bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+ }
+
+ return *this;
+ }
+
+ void query_base::
+ append (const string& q)
+ {
+ if (!clause_.empty () &&
+ clause_.back ().kind == clause_part::kind_native)
+ {
+ string& s (clause_.back ().part);
+
+ char first (!q.empty () ? q[0] : ' ');
+ char last (!s.empty () ? s[s.size () - 1] : ' ');
+
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ s += ' ';
+
+ s += q;
+ }
+ else
+ clause_.push_back (clause_part (clause_part::kind_native, q));
+ }
+
+ void query_base::
+ append (const char* table, const char* column)
+ {
+ string s (table);
+ s += '.';
+ s += column;
+
+ clause_.push_back (clause_part (clause_part::kind_column, s));
+ }
+
+ void query_base::
+ append (details::shared_ptr<query_param> p, const char* conv)
+ {
+ clause_.push_back (clause_part (clause_part::kind_param));
+
+ if (conv != 0)
+ clause_.back ().part = conv;
+
+ parameters_.push_back (p);
+ bind_.push_back (bind ());
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+
+ bind* b (&bind_.back ());
+ memset (b, 0, sizeof (bind));
+ p->bind (b);
+ }
+
+ void query_base::
+ init_parameters () const
+ {
+ bool inc_ver (false);
+
+ for (size_t i (0); i < parameters_.size (); ++i)
+ {
+ query_param& p (*parameters_[i]);
+
+ if (p.reference ())
+ {
+ if (p.init ())
+ {
+ p.bind (&bind_[i]);
+ inc_ver = true;
+ }
+ }
+ }
+
+ if (inc_ver)
+ binding_.version++;
+ }
+
+ static bool
+ check_prefix (const string& s)
+ {
+ string::size_type n;
+
+ // It is easier to compare to upper and lower-case versions
+ // rather than getting involved with the portable case-
+ // insensitive string comparison mess.
+ //
+ if (s.compare (0, (n = 5), "WHERE") == 0 ||
+ s.compare (0, (n = 5), "where") == 0 ||
+ s.compare (0, (n = 6), "SELECT") == 0 ||
+ s.compare (0, (n = 6), "select") == 0 ||
+ s.compare (0, (n = 8), "ORDER BY") == 0 ||
+ s.compare (0, (n = 8), "order by") == 0 ||
+ s.compare (0, (n = 8), "GROUP BY") == 0 ||
+ s.compare (0, (n = 8), "group by") == 0 ||
+ s.compare (0, (n = 6), "HAVING") == 0 ||
+ s.compare (0, (n = 6), "having") == 0 ||
+ s.compare (0, (n = 4), "EXEC") == 0 ||
+ s.compare (0, (n = 4), "exec") == 0 ||
+ s.compare (0, (n = 7), "EXECUTE") == 0 ||
+ s.compare (0, (n = 7), "execute") == 0)
+ {
+ // It either has to be an exact match, or there should be
+ // a whitespace following the keyword.
+ //
+ if (s.size () == n || s[n] == ' ' || s[n] == '\n' || s[n] =='\t')
+ return true;
+ }
+
+ return false;
+ }
+
+ void query_base::
+ optimize ()
+ {
+ // Remove a single TRUE literal or one that is followe by one of
+ // the other clauses. This avoids useless WHERE clauses like
+ //
+ // WHERE TRUE GROUP BY foo
+ //
+ clause_type::iterator i (clause_.begin ()), e (clause_.end ());
+
+ if (i != e && i->kind == clause_part::kind_bool && i->bool_part)
+ {
+ clause_type::iterator j (i + 1);
+
+ if (j == e ||
+ (j->kind == clause_part::kind_native && check_prefix (j->part)))
+ clause_.erase (i);
+ }
+ }
+
+ const char* query_base::
+ clause_prefix () const
+ {
+ if (!clause_.empty ())
+ {
+ const clause_part& p (clause_.front ());
+
+ if (p.kind == clause_part::kind_native && check_prefix (p.part))
+ return "";
+
+ return "WHERE ";
+ }
+
+ return "";
+ }
+
+ string query_base::
+ clause () const
+ {
+ string r;
+
+ for (clause_type::const_iterator i (clause_.begin ()),
+ end (clause_.end ());
+ i != end;
+ ++i)
+ {
+ char last (!r.empty () ? r[r.size () - 1] : ' ');
+
+ switch (i->kind)
+ {
+ case clause_part::kind_column:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ r += i->part;
+ break;
+ }
+ case clause_part::kind_param:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ // Add the conversion expression, if any.
+ //
+ string::size_type p (0);
+ if (!i->part.empty ())
+ {
+ p = i->part.find ("(?)");
+ r.append (i->part, 0, p);
+ }
+
+ r += '?';
+
+ if (!i->part.empty ())
+ r.append (i->part, p + 3, string::npos);
+
+ break;
+ }
+ case clause_part::kind_native:
+ {
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ const string& p (i->part);
+ char first (!p.empty () ? p[0] : ' ');
+
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ r += ' ';
+
+ r += p;
+ break;
+ }
+ case clause_part::kind_bool:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ // SQL Server does not have "true" TRUE and FALSE boolean
+ // constants (there seem to be TRUE and FALSE literals but
+ // they are interpreted as integers). Boolean values seem
+ // to only be created as the result of boolean expressions.
+ //
+ r += i->bool_part ? "1 = 1" : "1 = 0";
+ break;
+ }
+ }
+ }
+
+ return clause_prefix () + r;
+ }
+
+ query_base
+ operator&& (const query_base& x, const query_base& y)
+ {
+ // Optimize cases where one or both sides are constant truth.
+ //
+ bool xt (x.const_true ()), yt (y.const_true ());
+
+ if (xt && yt)
+ return x;
+
+ if (xt)
+ return y;
+
+ if (yt)
+ return x;
+
+ query_base r ("(");
+ r += x;
+ r += ") AND (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator|| (const query_base& x, const query_base& y)
+ {
+ query_base r ("(");
+ r += x;
+ r += ") OR (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator! (const query_base& x)
+ {
+ query_base r ("NOT (");
+ r += x;
+ r += ")";
+ return r;
+ }
+
+ //
+ // long_query_param_impl
+ //
+
+ void long_query_param_impl::
+ copy ()
+ {
+ size_ = 0;
+
+ // The below code is the same as in statement.cxx.
+ //
+ const void* buf (&callback_);
+ char tmp_buf[1024];
+
+ size_t position (0);
+ for (;;)
+ {
+ size_t n;
+ chunk_type chunk;
+
+ callback_.callback.param (
+ callback_.context.param,
+ &position,
+ &buf,
+ &n,
+ &chunk,
+ tmp_buf,
+ sizeof (tmp_buf));
+
+ if (chunk != chunk_null)
+ {
+ if (buf_.capacity () < size_ + n)
+ buf_.capacity (size_ + n, size_);
+
+ memcpy (buf_.data () + size_, buf, n);
+ size_ += n;
+ }
+
+ if (chunk == chunk_one || chunk == chunk_last)
+ {
+ // Make sure the resulting buffer is not NULL (failed that, this
+ // parameter will be skipped by the query machinery).
+ //
+ if (size_ == 0)
+ buf_.capacity (1);
+
+ break;
+ }
+ else if (chunk == chunk_null)
+ break;
+ }
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/query.hxx b/libodb-mssql/odb/mssql/query.hxx
new file mode 100644
index 0000000..18db752
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query.hxx
@@ -0,0 +1,2711 @@
+// file : odb/mssql/query.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_QUERY_HXX
+#define ODB_MSSQL_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx> // odb::query_column
+#include <odb/query.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/traits.hxx>
+#include <odb/mssql/binding.hxx>
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/mssql-types.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/details/export.hxx>
+#include <odb/mssql/details/conversion.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ // For precision, 0 is invalid so we can use it as a special value
+ // to indicate that the precision has not been specified. For scale,
+ // however, 0 is a valid value and for some types (e.g., TIME) if
+ // the scale is not specified, it defaults to something other than
+ // 0. As a result, for scale, the not specific special value will
+ // be 0xFFFF (USHORT_MAX).
+ //
+ template <typename T>
+ struct val_bind
+ {
+ typedef const T& type;
+
+ explicit
+ val_bind (type v, unsigned short p = 0, unsigned short s = 0xFFFF)
+ : val (v), prec (p), scale (s) {}
+
+ type val;
+
+ unsigned short prec;
+ unsigned short scale;
+ };
+
+ template <typename T, std::size_t N>
+ struct val_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ val_bind (type v, unsigned short p = 0, unsigned short s = 0xFFFF)
+ : val (v), prec (p), scale (s) {}
+
+ type val;
+
+ unsigned short prec;
+ unsigned short scale;
+ };
+
+ template <typename T>
+ struct ref_bind
+ {
+ typedef const T& type;
+
+ explicit
+ ref_bind (type r, unsigned short p = 0, unsigned short s = 0xFFFF)
+ : ref (r), prec (p), scale (s) {}
+
+ const void*
+ ptr () const {return &ref;}
+
+ type ref;
+
+ unsigned short prec;
+ unsigned short scale;
+ };
+
+ template <typename T, std::size_t N>
+ struct ref_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ ref_bind (type r, unsigned short p = 0, unsigned short s = 0xFFFF)
+ : ref (r), prec (p), scale (s) {}
+
+ // Allow implicit conversion from decayed ref_bind's.
+ //
+ ref_bind (ref_bind<T*> r): ref (r.ref), prec (r.prec), scale (r.scale) {}
+ ref_bind (ref_bind<const T*> r)
+ : ref (r.ref), prec (r.prec), scale (r.scale) {}
+
+ const void*
+ ptr () const {return ref;}
+
+ type ref;
+
+ unsigned short prec;
+ unsigned short scale;
+ };
+
+ template <typename T, database_type_id ID>
+ struct val_bind_typed: val_bind<T>
+ {
+ explicit
+ val_bind_typed (typename val_bind<T>::type v,
+ unsigned short p = 0,
+ unsigned short s = 0xFFFF): val_bind<T> (v, p, s) {}
+ };
+
+ template <typename T, database_type_id ID>
+ struct ref_bind_typed: ref_bind<T>
+ {
+ explicit
+ ref_bind_typed (typename ref_bind<T>::type r,
+ unsigned short p = 0,
+ unsigned short s = 0xFFFF): ref_bind<T> (r, p, s) {}
+ };
+
+ struct LIBODB_MSSQL_EXPORT query_param: details::shared_base
+ {
+ typedef mssql::bind bind_type;
+
+ virtual
+ ~query_param ();
+
+ bool
+ reference () const
+ {
+ return value_ != 0;
+ }
+
+ virtual bool
+ init () = 0;
+
+ virtual void
+ bind (bind_type*) = 0;
+
+ protected:
+ query_param (const void* value)
+ : value_ (value)
+ {
+ }
+
+ protected:
+ const void* value_;
+ };
+
+ //
+ //
+ template <typename T, database_type_id ID>
+ struct query_column;
+
+ class LIBODB_MSSQL_EXPORT query_base
+ {
+ public:
+ struct clause_part
+ {
+ enum kind_type
+ {
+ kind_column,
+ kind_param,
+ kind_native,
+ kind_bool
+ };
+
+ clause_part (kind_type k): kind (k), bool_part (false) {}
+ clause_part (kind_type k, const std::string& p)
+ : kind (k), part (p), bool_part (false) {}
+ clause_part (bool p): kind (kind_bool), bool_part (p) {}
+
+ kind_type kind;
+ std::string part; // If kind is param, then part is conversion expr.
+ bool bool_part;
+ };
+
+ query_base ()
+ : binding_ (0, 0)
+ {
+ }
+
+ // True or false literal.
+ //
+ explicit
+ query_base (bool v)
+ : binding_ (0, 0)
+ {
+ append (v);
+ }
+
+ explicit
+ query_base (const char* native)
+ : binding_ (0, 0)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ explicit
+ query_base (const std::string& native)
+ : binding_ (0, 0)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ query_base (const char* table, const char* column)
+ : binding_ (0, 0)
+ {
+ append (table, column);
+ }
+
+ template <typename T>
+ explicit
+ query_base (val_bind<T> v)
+ : binding_ (0, 0)
+ {
+ *this += v;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (val_bind_typed<T, ID> v)
+ : binding_ (0, 0)
+ {
+ *this += v;
+ }
+
+ template <typename T>
+ explicit
+ query_base (ref_bind<T> r)
+ : binding_ (0, 0)
+ {
+ *this += r;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (ref_bind_typed<T, ID> r)
+ : binding_ (0, 0)
+ {
+ *this += r;
+ }
+
+ template <database_type_id ID>
+ query_base (const query_column<bool, ID>&);
+
+ // Translate common query representation to SQL Server native. Defined
+ // in query-dynamic.cxx
+ //
+ query_base (const odb::query_base&);
+
+ // Copy c-tor and assignment.
+ //
+ query_base (const query_base&);
+
+ query_base&
+ operator= (const query_base&);
+
+ public:
+ std::string
+ clause () const;
+
+ const char*
+ clause_prefix () const;
+
+ // Initialize the by-reference parameters from bound variables.
+ //
+ void
+ init_parameters () const;
+
+ binding&
+ parameters_binding () const;
+
+ public:
+ bool
+ empty () const
+ {
+ return clause_.empty ();
+ }
+
+ static const query_base true_expr;
+
+ bool
+ const_true () const
+ {
+ return clause_.size () == 1 &&
+ clause_.front ().kind == clause_part::kind_bool &&
+ clause_.front ().bool_part;
+ }
+
+ void
+ optimize ();
+
+ public:
+ template <typename T>
+ static val_bind<T>
+ _val (const T& x, unsigned short prec = 0, unsigned short scale = 0xFFFF)
+ {
+ return val_bind<T> (x, prec, scale);
+ }
+
+ template <database_type_id ID, typename T>
+ static val_bind_typed<T, ID>
+ _val (const T& x, unsigned short prec = 0, unsigned short scale = 0xFFFF)
+ {
+ return val_bind_typed<T, ID> (x, prec, scale);
+ }
+
+ template <typename T>
+ static ref_bind<T>
+ _ref (const T& x, unsigned short prec = 0, unsigned short scale = 0xFFFF)
+ {
+ return ref_bind<T> (x, prec, scale);
+ }
+
+ template <database_type_id ID, typename T>
+ static ref_bind_typed<T, ID>
+ _ref (const T& x, unsigned short prec = 0, unsigned short scale = 0xFFFF)
+ {
+ return ref_bind_typed<T, ID> (x, prec, scale);
+ }
+
+ // Some compilers (notably VC++), when deducing const T& from const
+ // array do not strip const from the array type. As a result, in the
+ // above signatures we get, for example, T = const char[4] instead
+ // of T = char[4], which is what we want. So to "fix" such compilers,
+ // we will have to provide the following specializations of the above
+ // functions.
+ //
+ template <typename T, std::size_t N>
+ static val_bind<T[N]>
+ _val (const T (&x) [N], unsigned short p = 0, unsigned short s = 0xFFFF)
+ {
+ return val_bind<T[N]> (x, p, s);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static val_bind_typed<T[N], ID>
+ _val (const T (&x) [N], unsigned short p = 0, unsigned short s = 0xFFFF)
+ {
+ return val_bind_typed<T[N], ID> (x, p, s);
+ }
+
+ template <typename T, std::size_t N>
+ static ref_bind<T[N]>
+ _ref (const T (&x) [N], unsigned short p = 0, unsigned short s = 0xFFFF)
+ {
+ return ref_bind<T[N]> (x, p, s);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static ref_bind_typed<T[N], ID>
+ _ref (const T (&x) [N], unsigned short p = 0, unsigned short s = 0xFFFF)
+ {
+ return ref_bind_typed<T[N], ID> (x, p, s);
+ }
+
+ public:
+ query_base&
+ operator+= (const query_base&);
+
+ query_base&
+ operator+= (const std::string& q)
+ {
+ append (q);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (val_bind<T> v)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ v, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (val_bind_typed<T, ID> v)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (v, 0);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (ref_bind<T> r)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ r, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (ref_bind_typed<T, ID> r)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (r, 0);
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (const query_column<T, ID>&);
+
+ // Implementation details.
+ //
+ public:
+ template <typename T, database_type_id ID>
+ void
+ append (val_bind<T>, const char* conv);
+
+ template <typename T, database_type_id ID>
+ void
+ append (ref_bind<T>, const char* conv);
+
+ void
+ append (details::shared_ptr<query_param>, const char* conv);
+
+ void
+ append (bool v)
+ {
+ clause_.push_back (clause_part (v));
+ }
+
+ void
+ append (const std::string& native);
+
+ void
+ append (const char* native) // Clashes with append(bool).
+ {
+ append (std::string (native));
+ }
+
+ void
+ append (const char* table, const char* column);
+
+ private:
+ typedef std::vector<clause_part> clause_type;
+ typedef std::vector<details::shared_ptr<query_param> > parameters_type;
+
+ clause_type clause_;
+ parameters_type parameters_;
+ mutable std::vector<bind> bind_;
+ mutable binding binding_;
+ };
+
+ inline query_base
+ operator+ (const query_base& x, const query_base& y)
+ {
+ query_base r (x);
+ r += y;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, val_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, val_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, ref_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, ref_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const query_base& q, const std::string& s)
+ {
+ query_base r (q);
+ r += s;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const std::string& s, const query_base& q)
+ {
+ query_base r (s);
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, val_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, val_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, ref_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, ref_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ LIBODB_MSSQL_EXPORT query_base
+ operator&& (const query_base& x, const query_base& y);
+
+ LIBODB_MSSQL_EXPORT query_base
+ operator|| (const query_base& x, const query_base& y);
+
+ LIBODB_MSSQL_EXPORT query_base
+ operator! (const query_base& x);
+
+ // query_column
+ //
+ struct LIBODB_MSSQL_EXPORT query_column_base
+ {
+ // Note that we keep shallow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column_base (const char* table,
+ const char* column,
+ const char* conv,
+ unsigned short prec,
+ unsigned short scale)
+ : table_ (table), column_ (column), conversion_ (conv),
+ prec_ (prec), scale_ (scale)
+ {
+ }
+
+ const char*
+ table () const
+ {
+ return table_;
+ }
+
+ const char*
+ column () const
+ {
+ return column_;
+ }
+
+ // Can be NULL.
+ //
+ const char*
+ conversion () const
+ {
+ return conversion_;
+ }
+
+ unsigned short
+ prec () const
+ {
+ return prec_;
+ }
+
+ unsigned short
+ scale () const
+ {
+ return scale_;
+ }
+
+ protected:
+ const char* table_;
+ const char* column_;
+ const char* conversion_;
+
+ unsigned short prec_;
+ unsigned short scale_;
+ };
+
+ template <typename T, database_type_id ID>
+ struct query_column: query_column_base
+ {
+ typedef typename decay_traits<T>::type decayed_type;
+
+ // Note that we keep shalow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column (const char* table,
+ const char* column,
+ const char* conv,
+ unsigned short prec = 0,
+ unsigned short scale = 0xFFFF)
+ : query_column_base (table, column, conv, prec, scale) {}
+
+ // Implementation is in query-dynamic.ixx.
+ //
+ query_column (odb::query_column<T>&,
+ const char* table,
+ const char* column,
+ const char* conv,
+ unsigned short prec = 0,
+ unsigned short scale = 0xFFFF);
+
+ // is_null, is_not_null
+ //
+ public:
+ query_base
+ is_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NULL";
+ return q;
+ }
+
+ query_base
+ is_not_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NOT NULL";
+ return q;
+ }
+
+ // in
+ //
+ public:
+ query_base
+ in (decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type,
+ decayed_type) const;
+
+ template <typename I>
+ query_base
+ in_range (I begin, I end) const;
+
+ // like
+ //
+ public:
+ query_base
+ like (decayed_type pattern) const
+ {
+ return like (val_bind<T> (pattern));
+ }
+
+ query_base
+ like (val_bind<T> pattern) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)));
+ }
+
+ query_base
+ like (ref_bind<T> pattern) const;
+
+ query_base
+ like (decayed_type pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (pattern), escape);
+ }
+
+ query_base
+ like (val_bind<T> pattern, decayed_type escape) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)), escape);
+ }
+
+ query_base
+ like (ref_bind<T> pattern, decayed_type escape) const;
+
+ // =
+ //
+ public:
+ query_base
+ equal (decayed_type v) const
+ {
+ return equal (val_bind<T> (v));
+ }
+
+ query_base
+ equal (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ equal (val_bind<T2> v) const
+ {
+ return equal (val_bind<T> (decayed_type (v.val)));;
+ }
+
+ query_base
+ equal (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator== (const query_column& c, decayed_type v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (decayed_type v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, val_bind<T> v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (val_bind<T> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (const query_column& c, val_bind<T2> v)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (val_bind<T2> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, ref_bind<T> r)
+ {
+ return c.equal (r);
+ }
+
+ friend query_base
+ operator== (ref_bind<T> r, const query_column& c)
+ {
+ return c.equal (r);
+ }
+
+ // !=
+ //
+ public:
+ query_base
+ unequal (decayed_type v) const
+ {
+ return unequal (val_bind<T> (v));
+ }
+
+ query_base
+ unequal (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ unequal (val_bind<T2> v) const
+ {
+ return unequal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ unequal (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator!= (const query_column& c, decayed_type v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (decayed_type v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, val_bind<T> v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (val_bind<T> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (const query_column& c, val_bind<T2> v)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (val_bind<T2> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, ref_bind<T> r)
+ {
+ return c.unequal (r);
+ }
+
+ friend query_base
+ operator!= (ref_bind<T> r, const query_column& c)
+ {
+ return c.unequal (r);
+ }
+
+ // <
+ //
+ public:
+ query_base
+ less (decayed_type v) const
+ {
+ return less (val_bind<T> (v));
+ }
+
+ query_base
+ less (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less (val_bind<T2> v) const
+ {
+ return less (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator< (const query_column& c, decayed_type v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (decayed_type v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, val_bind<T> v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (val_bind<T> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (const query_column& c, val_bind<T2> v)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, ref_bind<T> r)
+ {
+ return c.less (r);
+ }
+
+ friend query_base
+ operator< (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater (r);
+ }
+
+ // >
+ //
+ public:
+ query_base
+ greater (decayed_type v) const
+ {
+ return greater (val_bind<T> (v));
+ }
+
+ query_base
+ greater (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater (val_bind<T2> v) const
+ {
+ return greater (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator> (const query_column& c, decayed_type v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (decayed_type v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, val_bind<T> v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (val_bind<T> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (val_bind<T2> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater (r);
+ }
+
+ friend query_base
+ operator> (ref_bind<T> r, const query_column& c)
+ {
+ return c.less (r);
+ }
+
+ // <=
+ //
+ public:
+ query_base
+ less_equal (decayed_type v) const
+ {
+ return less_equal (val_bind<T> (v));
+ }
+
+ query_base
+ less_equal (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less_equal (val_bind<T2> v) const
+ {
+ return less_equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less_equal (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator<= (const query_column& c, decayed_type v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (decayed_type v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, val_bind<T> v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (val_bind<T> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (const query_column& c, val_bind<T2> v)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, ref_bind<T> r)
+ {
+ return c.less_equal (r);
+ }
+
+ friend query_base
+ operator<= (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater_equal (r);
+ }
+
+ // >=
+ //
+ public:
+ query_base
+ greater_equal (decayed_type v) const
+ {
+ return greater_equal (val_bind<T> (v));
+ }
+
+ query_base
+ greater_equal (val_bind<T> v) const
+ {
+ v.prec = prec_;
+ v.scale = scale_;
+
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater_equal (val_bind<T2> v) const
+ {
+ return greater_equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater_equal (ref_bind<T> r) const
+ {
+ r.prec = prec_;
+ r.scale = scale_;
+
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator>= (const query_column& c, decayed_type v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (decayed_type v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, val_bind<T> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (val_bind<T> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (val_bind<T2> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater_equal (r);
+ }
+
+ friend query_base
+ operator>= (ref_bind<T> r, const query_column& c)
+ {
+ return c.less_equal (r);
+ }
+
+ // Column comparison.
+ //
+ public:
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator== (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () ==
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator!= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () !=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "!=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator< (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator> (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator<= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator>= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+ };
+
+ // Provide operator+() for using columns to construct native
+ // query fragments (e.g., ORDER BY).
+ //
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const std::string& s)
+ {
+ query_base q (c.table (), c.column ());
+ q += s;
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, const query_column<T, ID>& c)
+ {
+ query_base q (s);
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const query_base& q)
+ {
+ query_base r (c.table (), c.column ());
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, const query_column<T, ID>& c)
+ {
+ query_base r (q);
+ r.append (c.table (), c.column ());
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base& query_base::
+ operator+= (const query_column<T, ID>& c)
+ {
+ append (c.table (), c.column ());
+ return *this;
+ }
+
+ //
+ //
+ template <typename T, database_type_id>
+ struct query_param_impl;
+
+ // id_bit.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_bit>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::bit;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_bit>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ unsigned char image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_tinyint.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_tinyint>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::tinyint;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_tinyint>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ unsigned char image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_smallint.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_smallint>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::smallint;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_smallint>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ short image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_int.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_int>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::int_;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_int>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ int image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_bigint.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_bigint>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::bigint;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_bigint>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ long long image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_decimal.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_decimal>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ prec_ (r.prec != 0 ? r.prec : 18), // Default is 18.
+ scale_ (r.scale != 0xFFFF ? r.scale : 0) // Default is 0.
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ prec_ (v.prec != 0 ? v.prec : 18), // Default is 18.
+ scale_ (v.scale)
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::decimal;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ // Encode precision (p) and scale (s) as (p * 100 + s).
+ //
+ b->capacity = static_cast<SQLLEN> (prec_ * 100 + scale_);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_decimal>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ unsigned short prec_;
+ unsigned short scale_;
+
+ decimal image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_smallmoney.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_smallmoney>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::smallmoney;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_smallmoney>::set_image (image_, is_null, v);
+ size_ind_ = 4;
+ }
+
+ private:
+ smallmoney image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_money.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_money>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::money;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_money>::set_image (image_, is_null, v);
+ size_ind_ = 8;
+ }
+
+ private:
+ money image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_float4.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_float4>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ prec_ (r.prec != 0 ? r.prec : 24)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ prec_ (v.prec != 0 ? v.prec : 24)
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::float4;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (prec_);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_float4>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ unsigned short prec_;
+ float image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_float8.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_float8>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ prec_ (r.prec != 0 ? r.prec : 53)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ prec_ (v.prec != 0 ? v.prec : 53)
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::float8;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (prec_);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_float8>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ unsigned short prec_;
+ double image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_string.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_string>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ // Default to short data max (1024).
+ buf_ (r.prec != 0 ? r.prec : 1024)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ // Default to short data max (1024).
+ buf_ (v.prec != 0 ? v.prec : 1024)
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::string;
+ b->buffer = buf_.data ();
+ b->size_ind = &size_ind_;
+ // Extra byte for the null terminator (convention).
+ //
+ b->capacity = static_cast<SQLLEN> (buf_.capacity () + 1);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0);
+ value_traits<T, id_string>::set_image (
+ buf_.data (), buf_.capacity (), size, is_null, v);
+ size_ind_ = static_cast<SQLLEN> (size);
+ }
+
+ private:
+ details::buffer buf_;
+ SQLLEN size_ind_;
+ };
+
+ // id_nstring.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_nstring>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ // Precision is in 2-byte chars. Default to short data max (1024).
+ buf_ (r.prec != 0 ? r.prec * 2 : 1024)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ // Precision is in 2-byte chars. Default to short data max (1024).
+ buf_ (v.prec != 0 ? v.prec * 2 : 1024)
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::nstring;
+ b->buffer = buf_.data ();
+ b->size_ind = &size_ind_;
+ // Extra two bytes for the null terminator (convention).
+ //
+ b->capacity = static_cast<SQLLEN> (buf_.capacity () + 2);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0);
+ value_traits<T, id_nstring>::set_image (
+ reinterpret_cast<ucs2_char*> (buf_.data ()),
+ buf_.capacity () / 2,
+ size,
+ is_null,
+ v);
+ size_ind_ = static_cast<SQLLEN> (size * 2);
+ }
+
+ private:
+ details::buffer buf_;
+ SQLLEN size_ind_;
+ };
+
+ // id_binary.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_binary>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ // Default to short data max (1024).
+ buf_ (r.prec != 0 ? r.prec : 1024)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ // Default to short data max (1024).
+ buf_ (v.prec != 0 ? v.prec : 1024)
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::binary;
+ b->buffer = buf_.data ();
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (buf_.capacity ());
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0);
+ value_traits<T, id_binary>::set_image (
+ buf_.data (), buf_.capacity (), size, is_null, v);
+ size_ind_ = static_cast<SQLLEN> (size);
+ }
+
+ private:
+ details::buffer buf_;
+ SQLLEN size_ind_;
+ };
+
+ // long_query_param_impl
+ //
+ // For long data we cannot assume that the by-value parameter will
+ // still be alive when we execute the query (and when the callback
+ // will be called to provide the data). So we have to make a private
+ // copy of the data and use that when the time comes.
+ //
+ class LIBODB_MSSQL_EXPORT long_query_param_impl
+ {
+ protected:
+ long_query_param_impl (): buf_ (0) {}
+
+ void
+ copy ();
+
+ protected:
+ details::buffer buf_;
+ std::size_t size_;
+ long_callback callback_;
+ };
+
+ // id_long_string.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_long_string>: query_param,
+ long_query_param_impl
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()), prec_ (r.prec)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0), prec_ (v.prec)
+ {
+ init (v.val);
+ copy ();
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ // If this is a by-value parameter then we already have the
+ // buffer containing all the data. So instead of using the
+ // callback mechanism, bind the buffer directly using the
+ // short data approach. SQLLEN (int on 32-bit platforms)
+ // can represent maximum size of 2^31 bytes which happens
+ // to be greater than the maximum size of VARCHAR(max) or
+ // TEXT (both 2^31-1 bytes).
+ //
+ if (reference ())
+ {
+ b->type = bind::long_string;
+ b->buffer = &this->long_query_param_impl::callback_;
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (prec_);
+ size_ind_ = SQL_DATA_AT_EXEC;
+ }
+ else
+ {
+ b->type = bind::string;
+ b->buffer = buf_.data ();
+ b->size_ind = &size_ind_;
+ // Extra byte for the null terminator (convention).
+ //
+ b->capacity = static_cast<SQLLEN> (prec_ != 0 ? prec_ + 1 : 0);
+ size_ind_ = static_cast<SQLLEN> (size_);
+ }
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_long_string>::set_image (
+ long_query_param_impl::callback_.callback.param,
+ long_query_param_impl::callback_.context.param,
+ is_null,
+ v);
+ }
+
+ private:
+ unsigned short prec_;
+ SQLLEN size_ind_;
+ };
+
+ // id_long_nstring.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_long_nstring>: query_param,
+ long_query_param_impl
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()), prec_ (r.prec)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0), prec_ (v.prec)
+ {
+ init (v.val);
+ copy ();
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ // If this is a by-value parameter then we already have the
+ // buffer containing all the data. So instead of using the
+ // callback mechanism, bind the buffer directly using the
+ // short data approach. SQLLEN (int on 32-bit platforms)
+ // can represent maximum size of 2^31 bytes which happens
+ // to be greater than the maximum size of NVARCHAR(max) or
+ // NTEXT (both 2^31-1 bytes).
+ //
+ if (reference ())
+ {
+ b->type = bind::long_nstring;
+ b->buffer = &this->long_query_param_impl::callback_;
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (prec_ * 2); // In bytes.
+ size_ind_ = SQL_DATA_AT_EXEC;
+ }
+ else
+ {
+ b->type = bind::nstring;
+ b->buffer = buf_.data ();
+ b->size_ind = &size_ind_;
+ // In bytes, extra character for the null terminator (convention).
+ //
+ b->capacity = static_cast<SQLLEN> (prec_ != 0 ? (prec_ + 1) * 2 : 0);
+ size_ind_ = static_cast<SQLLEN> (size_);
+ }
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_long_nstring>::set_image (
+ long_query_param_impl::callback_.callback.param,
+ long_query_param_impl::callback_.context.param,
+ is_null,
+ v);
+ }
+
+ private:
+ unsigned short prec_;
+ SQLLEN size_ind_;
+ };
+
+ // id_long_binary.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_long_binary>: query_param,
+ long_query_param_impl
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()), prec_ (r.prec)
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0), prec_ (v.prec)
+ {
+ init (v.val);
+ copy ();
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ // If this is a by-value parameter then we already have the
+ // buffer containing all the data. So instead of using the
+ // callback mechanism, bind the buffer directly using the
+ // short data approach. SQLLEN (int on 32-bit platforms)
+ // can represent maximum size of 2^31 bytes which happens
+ // to be greater than the maximum size of VARBINARY(max)
+ // or IMAGE (both 2^31-1 bytes).
+ //
+ if (reference ())
+ {
+ b->type = bind::long_binary;
+ b->buffer = &this->long_query_param_impl::callback_;
+ b->size_ind = &size_ind_;
+ size_ind_ = SQL_DATA_AT_EXEC;
+ }
+ else
+ {
+ b->type = bind::binary;
+ b->buffer = buf_.data ();
+ b->size_ind = &size_ind_;
+ size_ind_ = static_cast<SQLLEN> (size_);
+ }
+
+ b->capacity = static_cast<SQLLEN> (prec_);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_long_binary>::set_image (
+ long_query_param_impl::callback_.callback.param,
+ long_query_param_impl::callback_.context.param,
+ is_null,
+ v);
+ }
+
+ private:
+ unsigned short prec_;
+ SQLLEN size_ind_;
+ };
+
+ // id_date.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_date>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::date;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_date>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ date image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_time.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_time>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ scale_ (r.scale != 0xFFFF ? r.scale : 7) // Default is 7.
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ scale_ (v.scale != 0xFFFF ? v.scale : 7) // Default is 7.
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::time;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (scale_);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_time>::set_image (image_, scale_, is_null, v);
+ size_ind_ = static_cast<SQLLEN> (sizeof (image_));
+ }
+
+ private:
+ unsigned short scale_;
+ time image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_datetime.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_datetime>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ scale_ (r.scale != 0xFFFF ? r.scale : 7) // Default to datetime2/7.
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ scale_ (v.scale != 0xFFFF ? v.scale : 7) // Default to datetime2/7.
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::datetime;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (scale_);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_datetime>::set_image (image_, scale_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ unsigned short scale_;
+ datetime image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_datetimeoffset.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_datetimeoffset>: query_param
+ {
+ query_param_impl (ref_bind<T> r)
+ : query_param (r.ptr ()),
+ scale_ (r.scale != 0xFFFF ? r.scale : 7) // Default is 7.
+ {
+ }
+
+ query_param_impl (val_bind<T> v)
+ : query_param (0),
+ scale_ (v.scale != 0xFFFF ? v.scale : 7) // Default is 7.
+ {
+ init (v.val);
+ }
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::datetimeoffset;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ b->capacity = static_cast<SQLLEN> (scale_);
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_datetimeoffset>::set_image (
+ image_, scale_, is_null, v);
+ size_ind_ = static_cast<SQLLEN> (sizeof (image_));
+ }
+
+ private:
+ unsigned short scale_;
+ datetimeoffset image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_uniqueidentifier.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_uniqueidentifier>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::uniqueidentifier;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_uniqueidentifier>::set_image (image_, is_null, v);
+ size_ind_ = 0;
+ }
+
+ private:
+ uniqueidentifier image_;
+ SQLLEN size_ind_;
+ };
+
+ // id_rowversion.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_rowversion>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::rowversion;
+ b->buffer = &image_;
+ b->size_ind = &size_ind_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_rowversion>::set_image (image_, is_null, v);
+ size_ind_ = 8;
+ }
+
+ private:
+ unsigned char image_[8];
+ SQLLEN size_ind_;
+ };
+ }
+}
+
+// odb::mssql::query and odb::query specialization for SQL Server.
+//
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ class query: public query_base,
+ public query_selector<T, id_mssql>::columns_type
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : query_base (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : query_base (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : query_base (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (val_bind<T2> v)
+ : query_base (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (ref_bind<T2> r)
+ : query_base (r)
+ {
+ }
+
+ query (const query_base& q)
+ : query_base (q)
+ {
+ }
+
+ template <database_type_id ID>
+ query (const query_column<bool, ID>& qc)
+ : query_base (qc)
+ {
+ }
+
+ query (const odb::query_base& q)
+ : query_base (q)
+ {
+ }
+ };
+
+ namespace core
+ {
+ using mssql::query;
+ }
+ }
+
+ // Derive odb::query from odb::mssql::query so that it can be
+ // implicitly converted in mssql::database::query() calls.
+ //
+ template <typename T>
+ class query<T, mssql::query_base>: public mssql::query<T>
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : mssql::query<T> (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : mssql::query<T> (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : mssql::query<T> (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (mssql::val_bind<T2> v)
+ : mssql::query<T> (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (mssql::ref_bind<T2> r)
+ : mssql::query<T> (r)
+ {
+ }
+
+ query (const mssql::query_base& q)
+ : mssql::query<T> (q)
+ {
+ }
+
+ template <mssql::database_type_id ID>
+ query (const mssql::query_column<bool, ID>& qc)
+ : mssql::query<T> (qc)
+ {
+ }
+ };
+}
+
+#include <odb/mssql/query.ixx>
+#include <odb/mssql/query.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_QUERY_HXX
diff --git a/libodb-mssql/odb/mssql/query.ixx b/libodb-mssql/odb/mssql/query.ixx
new file mode 100644
index 0000000..0467667
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query.ixx
@@ -0,0 +1,34 @@
+// file : odb/mssql/query.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mssql
+ {
+ inline binding& query_base::
+ parameters_binding () const
+ {
+ return binding_;
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (val_bind<T> v, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (v)),
+ conv);
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (ref_bind<T> r, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (r)),
+ conv);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/query.txx b/libodb-mssql/odb/mssql/query.txx
new file mode 100644
index 0000000..d01747c
--- /dev/null
+++ b/libodb-mssql/odb/mssql/query.txx
@@ -0,0 +1,169 @@
+// file : odb/mssql/query.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // query_base
+ //
+
+ template <database_type_id ID>
+ query_base::
+ query_base (const query_column<bool, ID>& c)
+ : binding_ (0, 0)
+ {
+ // Cannot use IS TRUE here since database type can be a non-
+ // integral type.
+ //
+ append (c.table (), c.column ());
+ append ("=");
+ append<bool, ID> (val_bind<bool> (true, c.prec (), c.scale ()),
+ c.conversion ());
+ }
+
+ //
+ // query_column
+ //
+
+ // in
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3, prec_, scale_), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3,
+ decayed_type v4) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4, prec_, scale_), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3, decayed_type v4,
+ decayed_type v5) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4, prec_, scale_), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v5, prec_, scale_), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ template <typename I>
+ query_base query_column<T, ID>::
+ in_range (I begin, I end) const
+ {
+ if (begin != end)
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+
+ for (I i (begin); i != end; ++i)
+ {
+ if (i != begin)
+ q += ",";
+
+ q.append<T, ID> (val_bind<T> (*i, prec_, scale_), conversion_);
+ }
+
+ q += ")";
+ return q;
+ }
+ else
+ return query_base (false);
+ }
+
+ // like
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/section-statements.hxx b/libodb-mssql/odb/mssql/section-statements.hxx
new file mode 100644
index 0000000..0f3500d
--- /dev/null
+++ b/libodb-mssql/odb/mssql/section-statements.hxx
@@ -0,0 +1,201 @@
+// file : odb/mssql/section-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_SECTION_STATEMENTS_HXX
+#define ODB_MSSQL_SECTION_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/mssql-types.hxx>
+#include <odb/mssql/binding.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class connection;
+
+ // Template argument is the section traits type.
+ //
+ template <typename T, typename ST>
+ class section_statements
+ {
+ public:
+ typedef ST traits;
+
+ typedef typename traits::image_type image_type;
+ typedef typename traits::id_image_type id_image_type;
+
+ typedef mssql::select_statement select_statement_type;
+ typedef mssql::update_statement update_statement_type;
+
+ typedef mssql::connection connection_type;
+
+ section_statements (connection_type&,
+ image_type&, id_image_type&,
+ binding& id, binding& idv);
+
+ connection_type&
+ connection () {return conn_;}
+
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ image_type&
+ image () {return image_;}
+
+ id_image_type&
+ id_image () {return id_image_;}
+
+ const binding&
+ id_binding () {return id_binding_;}
+
+ // Id and optimistic concurrency version (if any).
+ //
+ const binding&
+ idv_binding () {return idv_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ //
+ // Statements.
+ //
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ traits::select_statement,
+ traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_binding_,
+ select_image_binding_,
+ false));
+
+ return *select_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ traits::update_statement,
+ traits::versioned, // Process if versioned.
+ update_image_binding_,
+ (traits::rowversion ? &idv_binding_ : 0),
+ false));
+
+ return *update_;
+ }
+
+ public:
+ static const std::size_t id_column_count = traits::id_column_count;
+ static const std::size_t managed_optimistic_load_column_count =
+ traits::managed_optimistic_load_column_count;
+ static const std::size_t managed_optimistic_update_column_count =
+ traits::managed_optimistic_update_column_count;
+ static const std::size_t select_column_count = traits::load_column_count;
+ static const std::size_t update_column_count =
+ traits::update_column_count;
+
+ private:
+ section_statements (const section_statements&);
+ section_statements& operator= (const section_statements&);
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+
+ // These come from object_statements.
+ //
+ image_type& image_;
+ id_image_type& id_image_;
+ binding& id_binding_;
+ binding& idv_binding_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+
+ static const std::size_t select_bind_count =
+ select_column_count != 0 || managed_optimistic_load_column_count != 0
+ ? select_column_count + managed_optimistic_load_column_count
+ : 1;
+
+ binding select_image_binding_;
+ bind select_image_bind_[select_bind_count];
+
+ // Update binding.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+
+ static const std::size_t update_bind_count =
+ update_column_count != 0 || managed_optimistic_update_column_count != 0
+ ? update_column_count + id_column_count +
+ managed_optimistic_update_column_count
+ : 1;
+
+ binding update_image_binding_;
+ bind update_image_bind_[update_bind_count];
+
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<update_statement_type> update_;
+ };
+ }
+}
+
+#include <odb/mssql/section-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_SECTION_STATEMENTS_HXX
diff --git a/libodb-mssql/odb/mssql/section-statements.txx b/libodb-mssql/odb/mssql/section-statements.txx
new file mode 100644
index 0000000..7389d82
--- /dev/null
+++ b/libodb-mssql/odb/mssql/section-statements.txx
@@ -0,0 +1,50 @@
+// file : odb/mssql/section-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T, typename ST>
+ section_statements<T, ST>::
+ section_statements (connection_type& conn,
+ image_type& im, id_image_type& idim,
+ binding& id, binding& idv)
+ : conn_ (conn),
+ svm_ (0),
+ image_ (im),
+ id_image_ (idim),
+ id_binding_ (id),
+ idv_binding_ (idv),
+ select_image_binding_ (select_image_bind_,
+ select_column_count +
+ managed_optimistic_load_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_update_column_count)
+ {
+ select_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ // If we are using optimistic concurrency, then the select statement
+ // will override the version member in the object image.
+ //
+ if (managed_optimistic_load_column_count != 0)
+ {
+ // Getting to the root image in polymorphic case is tricky.
+ //
+ typedef object_traits_impl<T, id_mssql> object_traits;
+
+ select_image_binding_.change_callback =
+ root_image<object_traits, object_traits::polymorphic>::get (
+ image_).change_callback ();
+ }
+
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/simple-object-result.hxx b/libodb-mssql/odb/mssql/simple-object-result.hxx
new file mode 100644
index 0000000..dcf8243
--- /dev/null
+++ b/libodb-mssql/odb/mssql/simple-object-result.hxx
@@ -0,0 +1,89 @@
+// file : odb/mssql/simple-object-result.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_SIMPLE_OBJECT_RESULT_HXX
+#define ODB_MSSQL_SIMPLE_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/simple-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx> // query_base
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ class object_result_impl: public odb::object_result_impl<T>
+ {
+ public:
+ typedef odb::object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_mssql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~object_result_impl ();
+
+ object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ typedef mssql::change_callback change_callback_type;
+
+ static void
+ change_callback (void* context);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ bool can_load_;
+ bool use_copy_;
+ typename object_traits::image_type* image_copy_;
+ };
+ }
+}
+
+#include <odb/mssql/simple-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_SIMPLE_OBJECT_RESULT_HXX
diff --git a/libodb-mssql/odb/mssql/simple-object-result.txx b/libodb-mssql/odb/mssql/simple-object-result.txx
new file mode 100644
index 0000000..3a0e984
--- /dev/null
+++ b/libodb-mssql/odb/mssql/simple-object-result.txx
@@ -0,0 +1,186 @@
+// file : odb/mssql/simple-object-result.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/mssql/exceptions.hxx> // long_data_reload
+#include <odb/mssql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ object_result_impl<T>::
+ ~object_result_impl ()
+ {
+ invalidate ();
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ invalidate ()
+ {
+ change_callback_type& cc (statements_.image ().change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ delete image_copy_;
+ image_copy_ = 0;
+
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ object_result_impl<T>::
+ object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ use_copy_ (false),
+ image_copy_ (0)
+ {
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ load (object_type& obj, bool)
+ {
+ if (!can_load_)
+ throw long_data_reload ();
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!statements_.locked ());
+ typename statements_type::auto_lock l (statements_);
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+
+ typename object_traits::image_type& i (
+ use_copy_ ? *image_copy_ : statements_.image ());
+
+ tc_.init (obj, i, &this->db_);
+
+ // If we are using a copy, make sure the callback information for
+ // long data also comes from the copy.
+ //
+ can_load_ = !statement_->stream_result (
+ use_copy_ ? &statements_.image () : 0,
+ use_copy_ ? image_copy_ : 0);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ object_traits::init (idi, object_traits::id (i));
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, obj, false);
+ statements_.load_delayed (tc_.version ());
+ l.unlock ();
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ typename object_result_impl<T>::id_type
+ object_result_impl<T>::
+ load_id ()
+ {
+ return object_traits::id (
+ use_copy_ ? *image_copy_ : statements_.image ());
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ next ()
+ {
+ can_load_ = true;
+ this->current (pointer_type ());
+
+ typename object_traits::image_type& im (statements_.image ());
+ change_callback_type& cc (im.change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ use_copy_ = false;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+
+ if (statement_->fetch () == select_statement::no_data)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ else
+ {
+ cc.callback = &change_callback;
+ cc.context = this;
+ }
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t object_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ change_callback (void* c)
+ {
+ object_result_impl<T>* r (static_cast<object_result_impl<T>*> (c));
+ typename object_traits::image_type& im (r->statements_.image ());
+
+ if (r->image_copy_ == 0)
+ r->image_copy_ = new typename object_traits::image_type (im);
+ else
+ *r->image_copy_ = im;
+
+ im.change_callback_.callback = 0;
+ im.change_callback_.context = 0;
+
+ r->use_copy_ = true;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/simple-object-statements.cxx b/libodb-mssql/odb/mssql/simple-object-statements.cxx
new file mode 100644
index 0000000..5d7e8b6
--- /dev/null
+++ b/libodb-mssql/odb/mssql/simple-object-statements.cxx
@@ -0,0 +1,15 @@
+// file : odb/mssql/simple-object-statements.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ object_statements_base::
+ ~object_statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/simple-object-statements.hxx b/libodb-mssql/odb/mssql/simple-object-statements.hxx
new file mode 100644
index 0000000..9cece2c
--- /dev/null
+++ b/libodb-mssql/odb/mssql/simple-object-statements.hxx
@@ -0,0 +1,606 @@
+// file : odb/mssql/simple-object-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_SIMPLE_OBJECT_STATEMENTS_HXX
+#define ODB_MSSQL_SIMPLE_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cassert>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/mssql-types.hxx>
+#include <odb/mssql/binding.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/statements-base.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ // The extra_statement_cache class is only defined (and used) in
+ // the generated source file. However, object_statements may be
+ // referenced from another source file in the case of a polymorphic
+ // hierarchy (though in this case the extra statement cache is
+ // not used). As a result, we cannot have a by-value member and
+ // instead will store a pointer and lazily allocate the cache if
+ // and when needed. We will also need to store a pointer to the
+ // deleter function which will be initialized during allocation
+ // (at that point we know that the cache class is defined).
+ //
+ template <typename T, typename I, typename ID>
+ struct extra_statement_cache_ptr
+ {
+ typedef I image_type;
+ typedef ID id_image_type;
+ typedef mssql::connection connection_type;
+
+ extra_statement_cache_ptr (): p_ (0) {}
+ ~extra_statement_cache_ptr ()
+ {
+ if (p_ != 0)
+ (this->*deleter_) (0, 0, 0, 0, 0);
+ }
+
+ T&
+ get (connection_type& c,
+ image_type& im, id_image_type& idim,
+ binding& id, binding* idv)
+ {
+ if (p_ == 0)
+ allocate (&c, &im, &idim, &id, (idv != 0 ? idv : &id));
+
+ return *p_;
+ }
+
+ private:
+ void
+ allocate (connection_type*,
+ image_type*, id_image_type*,
+ binding*, binding*);
+
+ private:
+ T* p_;
+ void (extra_statement_cache_ptr::*deleter_) (
+ connection_type*, image_type*, id_image_type*, binding*, binding*);
+ };
+
+ template <typename T, typename I, typename ID>
+ void extra_statement_cache_ptr<T, I, ID>::
+ allocate (connection_type* c,
+ image_type* im, id_image_type* idim,
+ binding* id, binding* idv)
+ {
+ // To reduce object code size, this function acts as both allocator
+ // and deleter.
+ //
+ if (p_ == 0)
+ {
+ p_ = new T (*c, *im, *idim, *id, *idv);
+ deleter_ = &extra_statement_cache_ptr<T, I, ID>::allocate;
+ }
+ else
+ delete p_;
+ }
+
+ //
+ // Implementation for objects with object id.
+ //
+
+ class LIBODB_MSSQL_EXPORT object_statements_base: public statements_base
+ {
+ // Locking.
+ //
+ public:
+ void
+ lock ()
+ {
+ assert (!locked_);
+ locked_ = true;
+ }
+
+ void
+ unlock ()
+ {
+ assert (locked_);
+ locked_ = false;
+ }
+
+ bool
+ locked () const
+ {
+ return locked_;
+ }
+
+ struct auto_unlock
+ {
+ // Unlocks the statement on construction and re-locks it on
+ // destruction.
+ //
+ auto_unlock (object_statements_base&);
+ ~auto_unlock ();
+
+ private:
+ auto_unlock (const auto_unlock&);
+ auto_unlock& operator= (const auto_unlock&);
+
+ private:
+ object_statements_base& s_;
+ };
+
+ public:
+ virtual
+ ~object_statements_base ();
+
+ protected:
+ object_statements_base (connection_type& conn)
+ : statements_base (conn), locked_ (false)
+ {
+ }
+
+ protected:
+ bool locked_;
+ };
+
+ template <typename T, bool optimistic>
+ struct optimistic_data;
+
+ template <typename T>
+ struct optimistic_data<T, true>
+ {
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mssql> object_traits;
+
+ optimistic_data (bind*, std::size_t skip, SQLUSMALLINT* status);
+
+ binding*
+ id_image_binding () {return &id_image_binding_;}
+
+ // The id + optimistic column binding.
+ //
+ binding id_image_binding_;
+
+ details::shared_ptr<delete_statement> erase_;
+ };
+
+ template <typename T>
+ struct optimistic_data<T, false>
+ {
+ optimistic_data (bind*, std::size_t, SQLUSMALLINT*) {}
+
+ binding*
+ id_image_binding () {return 0;}
+ };
+
+ template <typename T>
+ class object_statements: public object_statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mssql> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+ typedef typename object_traits::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::pointer_cache_traits
+ pointer_cache_traits;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef mssql::insert_statement insert_statement_type;
+ typedef mssql::select_statement select_statement_type;
+ typedef mssql::update_statement update_statement_type;
+ typedef mssql::delete_statement delete_statement_type;
+
+ // Automatic lock.
+ //
+ struct auto_lock
+ {
+ // Lock the statements unless they are already locked in which
+ // case subsequent calls to locked() will return false.
+ //
+ auto_lock (object_statements&);
+
+ // Unlock the statements if we are holding the lock and clear
+ // the delayed loads. This should only happen in case an
+ // exception is thrown. In normal circumstances, the user
+ // should call unlock() explicitly.
+ //
+ ~auto_lock ();
+
+ // Return true if this auto_lock instance holds the lock.
+ //
+ bool
+ locked () const;
+
+ // Unlock the statements.
+ //
+ void
+ unlock ();
+
+ private:
+ auto_lock (const auto_lock&);
+ auto_lock& operator= (const auto_lock&);
+
+ private:
+ object_statements& s_;
+ bool locked_;
+ };
+
+ public:
+ object_statements (connection_type&);
+
+ virtual
+ ~object_statements ();
+
+ // Delayed loading.
+ //
+ typedef void (*loader_function) (odb::database&,
+ const id_type&,
+ object_type&,
+ const schema_version_migration*);
+
+ void
+ delay_load (const id_type& id,
+ object_type& obj,
+ const typename pointer_cache_traits::position_type& p,
+ loader_function l = 0)
+ {
+ delayed_.push_back (delayed_load (id, obj, p, l));
+ }
+
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (locked ());
+
+ if (!delayed_.empty ())
+ load_delayed_<object_statements> (svm);
+ }
+
+ void
+ clear_delayed ()
+ {
+ if (!delayed_.empty ())
+ clear_delayed_ ();
+ }
+
+ // Object image.
+ //
+ image_type&
+ image (std::size_t i = 0)
+ {
+ return images_[i].obj;
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_image_version () const { return update_id_image_version_;}
+
+ void
+ update_id_image_version (std::size_t v) {update_id_image_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ // Object id image and binding.
+ //
+ id_image_type&
+ id_image (std::size_t i = 0) {return images_[i].id;}
+
+ std::size_t
+ id_image_version () const {return id_image_version_;}
+
+ void
+ id_image_version (std::size_t v) {id_image_version_ = v;}
+
+ binding&
+ id_image_binding () {return id_image_binding_;}
+
+ // Optimistic id + managed column image binding. It points to
+ // the same suffix as id binding and they are always updated
+ // at the same time.
+ //
+ binding&
+ optimistic_id_image_binding () {return *od_.id_image_binding ();}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ object_traits::auto_id,
+ object_traits::rowversion,
+ (object_traits::rowversion
+ ? &optimistic_id_image_binding ()
+ : (object_traits::auto_id ? &id_image_binding () : 0)),
+ false));
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement ()
+ {
+ if (find_ == 0)
+ find_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statement,
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_image_binding_,
+ select_image_binding_,
+ false));
+
+ return *find_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement,
+ true, // Unique (0 or 1).
+ object_traits::versioned, // Process if versioned.
+ update_image_binding_,
+ object_traits::rowversion ? &optimistic_id_image_binding () : 0,
+ false));
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement,
+ true, // Unique (0 or 1 affected rows).
+ id_image_binding_,
+ false));
+
+ return *erase_;
+ }
+
+ delete_statement_type&
+ optimistic_erase_statement ()
+ {
+ if (od_.erase_ == 0)
+ {
+ od_.erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::optimistic_erase_statement,
+ true, // Unique (0 or 1 affected rows).
+ od_.id_image_binding_,
+ false));
+ }
+
+ return *od_.erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ images_[0].obj, images_[0].id,
+ id_image_binding_, od_.id_image_binding ());
+ }
+
+ public:
+ // select = total - separate_load
+ // insert = total - inverse - managed_optimistic - auto_id
+ // update = total - inverse - managed_optimistic - id - readonly
+ // - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ object_traits::separate_load_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count -
+ object_traits::managed_optimistic_column_count -
+ (object_traits::auto_id ? id_column_count : 0);
+
+ static const std::size_t update_column_count =
+ insert_column_count -
+ (object_traits::auto_id ? 0 : id_column_count) -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ object_statements (const object_statements&);
+ object_statements& operator= (const object_statements&);
+
+ protected:
+ template <typename STS>
+ void
+ load_delayed_ (const schema_version_migration*);
+
+ void
+ clear_delayed_ ();
+
+ protected:
+ template <typename T1>
+ friend class polymorphic_derived_object_statements;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ // The UPDATE statement uses both the object and id image. Keep
+ // them next to each other so that the same skip distance can
+ // be used in batch binding.
+ //
+ struct images
+ {
+ image_type obj;
+ id_image_type id;
+ };
+
+ images images_[object_traits::batch];
+ SQLUSMALLINT status_[object_traits::batch];
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ bind select_image_bind_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[
+ insert_column_count != 0 ? insert_column_count : 1];
+
+ // Update binding. Note that the id suffix is bound to id_image_
+ // below instead of image_ which makes this binding effectively
+ // bound to two images. As a result, we have to track versions
+ // for both of them. If this object uses optimistic concurrency,
+ // then the binding for the managed column (version, timestamp,
+ // etc) comes after the id and the image for such a column is
+ // stored as part of the id image.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_image_version_;
+ binding update_image_binding_;
+ bind update_image_bind_[update_column_count + id_column_count +
+ managed_optimistic_column_count];
+
+ // Id image binding (only used as a parameter or in OUTPUT for
+ // auto id and version). Uses the suffix in the update bind.
+ //
+ std::size_t id_image_version_;
+ binding id_image_binding_;
+
+ // Extra data for objects with optimistic concurrency support.
+ //
+ optimistic_data<T, managed_optimistic_column_count != 0> od_;
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_;
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+
+ // Delayed loading.
+ //
+ struct delayed_load
+ {
+ typedef typename pointer_cache_traits::position_type position_type;
+
+ delayed_load () {}
+ delayed_load (const id_type& i,
+ object_type& o,
+ const position_type& p,
+ loader_function l)
+ : id (i), obj (&o), pos (p), loader (l)
+ {
+ }
+
+ id_type id;
+ object_type* obj;
+ position_type pos;
+ loader_function loader;
+ };
+
+ typedef std::vector<delayed_load> delayed_loads;
+ delayed_loads delayed_;
+
+ // Delayed vectors swap guard. See the load_delayed_() function for
+ // details.
+ //
+ struct swap_guard
+ {
+ swap_guard (object_statements& os, delayed_loads& dl)
+ : os_ (os), dl_ (dl)
+ {
+ dl_.swap (os_.delayed_);
+ }
+
+ ~swap_guard ()
+ {
+ os_.clear_delayed ();
+ dl_.swap (os_.delayed_);
+ }
+
+ private:
+ object_statements& os_;
+ delayed_loads& dl_;
+ };
+ };
+ }
+}
+
+#include <odb/mssql/simple-object-statements.ixx>
+#include <odb/mssql/simple-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_SIMPLE_OBJECT_STATEMENTS_HXX
diff --git a/libodb-mssql/odb/mssql/simple-object-statements.ixx b/libodb-mssql/odb/mssql/simple-object-statements.ixx
new file mode 100644
index 0000000..1066850
--- /dev/null
+++ b/libodb-mssql/odb/mssql/simple-object-statements.ixx
@@ -0,0 +1,68 @@
+// file : odb/mssql/simple-object-statements.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // auto_unlock
+ //
+ inline object_statements_base::auto_unlock::
+ auto_unlock (object_statements_base& s)
+ : s_ (s)
+ {
+ s_.unlock ();
+ }
+
+ inline object_statements_base::auto_unlock::
+ ~auto_unlock ()
+ {
+ s_.lock ();
+ }
+
+ //
+ // auto_lock
+ //
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ auto_lock (object_statements& s)
+ : s_ (s)
+ {
+ if (!s_.locked ())
+ {
+ s_.lock ();
+ locked_ = true;
+ }
+ else
+ locked_ = false;
+ }
+
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ ~auto_lock ()
+ {
+ if (locked_)
+ {
+ s_.unlock ();
+ s_.clear_delayed ();
+ }
+ }
+
+ template <typename T>
+ inline bool object_statements<T>::auto_lock::
+ locked () const
+ {
+ return locked_;
+ }
+
+ template <typename T>
+ inline void object_statements<T>::auto_lock::
+ unlock ()
+ {
+ assert (locked_);
+ s_.unlock ();
+ locked_ = false;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/simple-object-statements.txx b/libodb-mssql/odb/mssql/simple-object-statements.txx
new file mode 100644
index 0000000..06a0651
--- /dev/null
+++ b/libodb-mssql/odb/mssql/simple-object-statements.txx
@@ -0,0 +1,173 @@
+// file : odb/mssql/simple-object-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // optimistic_data
+ //
+
+ template <typename T>
+ optimistic_data<T, true>::
+ optimistic_data (bind* b, std::size_t skip, SQLUSMALLINT* status)
+ : id_image_binding_ (
+ b,
+ object_traits::id_column_count +
+ object_traits::managed_optimistic_column_count,
+ object_traits::batch,
+ skip,
+ status)
+ {
+ }
+
+ //
+ // object_statements
+ //
+
+ template <typename T>
+ object_statements<T>::
+ ~object_statements ()
+ {
+ }
+
+ template <typename T>
+ object_statements<T>::
+ object_statements (connection_type& conn)
+ : object_statements_base (conn),
+ select_image_binding_ (select_image_bind_, select_column_count),
+ insert_image_binding_ (insert_image_bind_,
+ insert_column_count,
+ object_traits::batch,
+ sizeof (images),
+ status_),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_column_count,
+ // No support for bulk update and ROWVERSION.
+ //
+ (object_traits::rowversion
+ ? 1
+ : object_traits::batch),
+ sizeof (images),
+ status_),
+ id_image_binding_ (update_image_bind_ + update_column_count,
+ id_column_count,
+ object_traits::batch,
+ sizeof (images),
+ status_),
+ od_ (update_image_bind_ + update_column_count,
+ sizeof (images),
+ status_)
+ {
+ // Only versions in the first element used.
+ //
+ images_[0].obj.version = 0;
+ images_[0].id.version = 0;
+
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_image_version_ = 0;
+ id_image_version_ = 0;
+
+ // SELECT statements only use the first element (no batches).
+ //
+ select_image_binding_.change_callback =
+ images_[0].obj.change_callback ();
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ }
+
+ template <typename T>
+ template <typename STS>
+ void object_statements<T>::
+ load_delayed_ (const schema_version_migration* svm)
+ {
+ database& db (connection ().database ());
+
+ delayed_loads dls;
+ swap_guard sg (*this, dls);
+
+ while (!dls.empty ())
+ {
+ delayed_load l (dls.back ());
+ typename pointer_cache_traits::insert_guard ig (l.pos);
+ dls.pop_back ();
+
+ if (l.loader == 0)
+ {
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (static_cast<STS&> (*this), &l.id))
+ throw object_not_persistent ();
+
+ // Our find_() version delays result freeing.
+ //
+ auto_result ar (*find_);
+
+ object_traits::callback (db, *l.obj, callback_event::pre_load);
+
+ // Our calls to init/load below can result in additional delayed
+ // loads being added to the delayed_ vector. We need to process
+ // those before we call the post callback.
+ //
+ tc.init (*l.obj, image (), &db);
+ find_->stream_result ();
+ ar.free ();
+
+ // Load containers, etc.
+ //
+ tc.load_ (static_cast<STS&> (*this), *l.obj, false);
+
+ if (!delayed_.empty ())
+ load_delayed_<STS> (svm);
+
+ // Temporarily unlock the statement for the post_load call so that
+ // it can load objects of this type recursively. This is safe to do
+ // because we have completely loaded the current object. Also the
+ // delayed_ list is clear before the unlock and should be clear on
+ // re-lock (since a callback can only call public API functions
+ // which will make sure all the delayed loads are processed before
+ // returning).
+ //
+ {
+ auto_unlock u (*this);
+ object_traits::callback (db, *l.obj, callback_event::post_load);
+ }
+ }
+ else
+ l.loader (db, l.id, *l.obj, svm);
+
+ pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+ }
+
+ template <typename T>
+ void object_statements<T>::
+ clear_delayed_ ()
+ {
+ // Remove the objects from the session cache.
+ //
+ for (typename delayed_loads::iterator i (delayed_.begin ()),
+ e (delayed_.end ()); i != e; ++i)
+ {
+ pointer_cache_traits::erase (i->pos);
+ }
+
+ delayed_.clear ();
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/statement-cache.hxx b/libodb-mssql/odb/mssql/statement-cache.hxx
new file mode 100644
index 0000000..cf06b94
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statement-cache.hxx
@@ -0,0 +1,59 @@
+// file : odb/mssql/statement-cache.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_STATEMENT_CACHE_HXX
+#define ODB_MSSQL_STATEMENT_CACHE_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <typeinfo>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/statements-base.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/type-info.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class LIBODB_MSSQL_EXPORT statement_cache
+ {
+ public:
+ statement_cache (connection& conn)
+ : conn_ (conn),
+ version_seq_ (conn_.database ().schema_version_sequence ()) {}
+
+ template <typename T>
+ typename object_traits_impl<T, id_mssql>::statements_type&
+ find_object ();
+
+ template <typename T>
+ view_statements<T>&
+ find_view ();
+
+ private:
+ typedef std::map<const std::type_info*,
+ details::shared_ptr<statements_base>,
+ details::type_info_comparator> map;
+
+ connection& conn_;
+ unsigned int version_seq_;
+ map map_;
+ };
+ }
+}
+
+#include <odb/mssql/statement-cache.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_STATEMENT_CACHE_HXX
diff --git a/libodb-mssql/odb/mssql/statement-cache.txx b/libodb-mssql/odb/mssql/statement-cache.txx
new file mode 100644
index 0000000..84424e3
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statement-cache.txx
@@ -0,0 +1,60 @@
+// file : odb/mssql/statement-cache.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/database.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ typename object_traits_impl<T, id_mssql>::statements_type&
+ statement_cache::
+ find_object ()
+ {
+ typedef
+ typename object_traits_impl<T, id_mssql>::statements_type
+ statements_type;
+
+ // Clear the cache if the database version has changed. This
+ // makes sure we don't re-use statements that correspond to
+ // the old schema.
+ //
+ if (version_seq_ != conn_.database ().schema_version_sequence ())
+ {
+ map_.clear ();
+ version_seq_ = conn_.database ().schema_version_sequence ();
+ }
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<statements_type&> (*i->second);
+
+ details::shared_ptr<statements_type> p (
+ new (details::shared) statements_type (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+
+ template <typename T>
+ view_statements<T>& statement_cache::
+ find_view ()
+ {
+ // We don't cache any statements for views so no need to clear
+ // the cache.
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<view_statements<T>&> (*i->second);
+
+ details::shared_ptr<view_statements<T> > p (
+ new (details::shared) view_statements<T> (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/statement-processing.cxx b/libodb-mssql/odb/mssql/statement-processing.cxx
new file mode 100644
index 0000000..0c3072a
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statement-processing.cxx
@@ -0,0 +1,356 @@
+// file : odb/mssql/statement-processing.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/statement-processing-common.hxx>
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+# include <iostream>
+#endif
+
+#include <odb/mssql/statement.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ typedef bind bind_type;
+
+ void statement::
+ process_select (std::string& r,
+ const char* s,
+ const bind_type* bind,
+ std::size_t bind_size,
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ bool optimize
+#else
+ bool
+#endif
+ )
+ {
+ // This implementation is pretty much the same as the generic one
+ // except for two things:
+ //
+ // 1. When checking for the fast case, take into account long data
+ // columns which we may have to re-arrange.
+ //
+ // 2. Create the column list in two passes, ordinary columns first
+ // followed by the long data columns.
+ //
+
+ bool empty (true); // Empty case (if none present).
+ bool fast (true); // Fast case (if all present and none are long data).
+ for (size_t i (0); i != bind_size && (empty || fast); ++i)
+ {
+ const bind_type& b (bind[i]);
+
+ if (b.buffer != 0)
+ empty = false;
+ else
+ fast = false;
+
+ if (b.type == bind_type::long_string ||
+ b.type == bind_type::long_nstring ||
+ b.type == bind_type::long_binary)
+ fast = false;
+ }
+
+ // Empty.
+ //
+ if (empty)
+ {
+ r.clear ();
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (*s != '\0')
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+ return;
+ }
+
+ // Fast path: just remove the "structure".
+ //
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ if (fast && !optimize)
+ {
+ process_fast (s, r);
+ return;
+ }
+#endif
+
+ // Scan the statement and store the positions of various parts.
+ //
+ size_t n (traits::length (s));
+ const char* e (s + n);
+
+ // Header.
+ //
+ const char* p (find (s, e, '\n'));
+ assert (p != 0);
+ size_t header_size (p - s);
+ p++;
+
+ // Column list.
+ //
+ const char* columns_begin (p);
+ for (const char* ce (comma_begin (p, e)); ce != 0; comma_next (p, ce, e))
+ ;
+
+ // FROM.
+ assert (traits::compare (p, "FROM ", 5) == 0);
+ const char* from_begin (p);
+ p = find (p, e, '\n'); // May not end with '\n'.
+ if (p == 0)
+ p = e;
+ size_t from_size (p - from_begin);
+ if (p != e)
+ p++;
+
+ // JOIN list.
+ //
+ const char* joins_begin (0), *joins_end (0);
+ if (e - p > 5 && fuzzy_prefix (p, e, "JOIN ", 5))
+ {
+ joins_begin = p;
+
+ // Find the end of the JOIN list.
+ //
+ for (const char* je (newline_begin (p, e));
+ je != 0; newline_next (p, je, e, "JOIN ", 5, true))
+ ;
+
+ joins_end = (p != e ? p - 1 : p);
+ }
+
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ if (fast && joins_begin == 0)
+ {
+ // No JOINs to optimize so can still take the fast path.
+ //
+ process_fast (s, r);
+ return;
+ }
+#endif
+
+ // Trailer (WHERE, ORDER BY, etc).
+ //
+ const char* trailer_begin (0);
+ size_t trailer_size (0);
+ if (e - p != 0)
+ {
+ trailer_begin = p;
+ trailer_size = e - p;
+ }
+
+ // Assume the same size as the original. It can only shrink, and in
+ // most cases only slightly. So this is a good approximation.
+ //
+ r.reserve (n);
+ r.assign (s, header_size);
+
+ // Column list.
+ //
+ {
+ r += ' ';
+
+ size_t i (0);
+ bool need_second (false);
+
+ // First pass: non-long data columns.
+ //
+ {
+ size_t bi (0);
+ for (const char *c (columns_begin), *ce (comma_begin (c, e));
+ ce != 0; comma_next (c, ce, e))
+ {
+ const bind_type& b (bind[bi++]);
+
+ // See if the column is present in the bind array and if it
+ // is of the right kind.
+ //
+ if (b.buffer == 0)
+ continue;
+
+ if (b.type == bind_type::long_string ||
+ b.type == bind_type::long_nstring ||
+ b.type == bind_type::long_binary)
+ {
+ need_second = true;
+ continue;
+ }
+
+ // Append the column.
+ //
+ if (i++ != 0)
+ r += ", "; // Add the space for consistency with the fast path.
+
+ r.append (c, ce - c);
+ }
+ }
+
+ // Second pass: long data columns.
+ //
+ if (need_second)
+ {
+ size_t bi (0);
+ for (const char *c (columns_begin), *ce (comma_begin (c, e));
+ ce != 0; comma_next (c, ce, e))
+ {
+ const bind_type& b (bind[bi++]);
+
+ // See if the column is present in the bind array and if it
+ // is of the right kind.
+ //
+ if (b.buffer == 0 ||
+ (b.type != bind_type::long_string &&
+ b.type != bind_type::long_nstring &&
+ b.type != bind_type::long_binary))
+ continue;
+
+ // Append the column.
+ //
+ if (i++ != 0)
+ r += ", "; // Add the space for consistency with the fast path.
+
+ r.append (c, ce - c);
+ }
+ }
+ }
+
+ // From.
+ //
+ r += ' ';
+ r.append (from_begin, from_size);
+
+ // JOIN list, pass 1.
+ //
+ size_t join_pos (0);
+ if (joins_begin != 0)
+ {
+ // Fill in the JOIN "area" with spaces.
+ //
+ r.resize (r.size () + joins_end - joins_begin + 1, ' ');
+ join_pos = r.size () + 1; // End of the last JOIN.
+ }
+
+ // Trailer.
+ //
+ if (trailer_size != 0)
+ {
+ r += ' ';
+ r.append (trailer_begin, trailer_size);
+ }
+
+ // JOIN list, pass 2.
+ //
+ if (joins_begin != 0)
+ {
+ // Splice the JOINs into the pre-allocated area.
+ //
+ for (const char* je (joins_end), *j (newline_rbegin (je, joins_begin));
+ j != 0; newline_rnext (j, je, joins_begin))
+ {
+ size_t n (je - j);
+
+ // Get the alias or, if none used, the table name.
+ //
+ p = find (j, je, "JOIN ", 5) + 5; // Skip past "JOIN ".
+ const char* table_begin (p);
+ p = find (p, je, ' '); // End of the table name.
+ const char* table_end (p);
+ p++; // Skip space.
+
+ // We may or may not have the AS keyword.
+ //
+ const char* alias_begin (0);
+ size_t alias_size (0);
+ if (p != je && // Not the end.
+ (je - p < 4 || traits::compare (p, "ON ", 3) != 0))
+ {
+ // Something other than "ON ", so got to be an alias.
+ //
+ p += 3;
+ alias_begin = p;
+ p = find (p, je, ' '); // There might be no ON (CROSS JOIN).
+ alias_size = (p != 0 ? p : je) - alias_begin;
+ }
+ else
+ {
+ // Just the table.
+ //
+ alias_begin = table_begin;
+ alias_size = table_end - alias_begin;
+ }
+
+ // The alias must be quoted.
+ //
+ assert (*alias_begin == '[' &&
+ *(alias_begin + alias_size - 1) == ']');
+
+ // We now need to see if the alias is used in either the SELECT
+ // list, the WHERE conditions, or the ON condition of any of the
+ // JOINs that we have already processed and decided to keep.
+ //
+ // Instead of re-parsing the whole thing again, we are going to
+ // take a shortcut and simply search for the alias in the statement
+ // we have constructed so far (that's why we have added the
+ // trailer before filling in the JOINs). To make it more robust,
+ // we are going to do a few extra sanity checks, specifically,
+ // that the alias is a top level identifier and is followed by
+ // only a single identifer (column). This will catch cases like
+ // [s].[t].[c] where [s] is also used as an alias or LEFT JOIN [t]
+ // where [t] is also used as an alias in another JOIN.
+ //
+ bool found (false);
+ for (size_t p (r.find (alias_begin, 0, alias_size));
+ p != string::npos;
+ p = r.find (alias_begin, p + alias_size, alias_size))
+ {
+ size_t e (p + alias_size);
+
+ // If we are not a top-level qualifier or not a bottom-level,
+ // then we are done (3 is for at least "[a]").
+ //
+ if ((p != 0 && r[p - 1] == '.') ||
+ (e + 3 >= r.size () || (r[e] != '.' || r[e + 1] != '[')))
+ continue;
+
+ // The only way to distinguish the [a].[c] from FROM [a].[c] or
+ // JOIN [a].[c] is by checking the prefix.
+ //
+ if ((p > 5 && r.compare (p - 5, 5, "FROM ") == 0) ||
+ (p > 5 && r.compare (p - 5, 5, "JOIN ") == 0))
+ continue;
+
+ // Check that we are followed by a single identifier.
+ //
+ e = r.find (']', e + 2);
+ if (e == string::npos || (e + 1 != r.size () && r[e + 1] == '.'))
+ continue;
+
+ found = true;
+ break;
+ }
+
+ join_pos -= n + 1; // Extra one for space.
+ if (found)
+ r.replace (join_pos, n, j, n);
+ else
+ r.erase (join_pos - 1, n + 1); // Extra one for space.
+ }
+ }
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (r.size () != n)
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/statement.cxx b/libodb-mssql/odb/mssql/statement.cxx
new file mode 100644
index 0000000..ede4bb6
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statement.cxx
@@ -0,0 +1,1740 @@
+// file : odb/mssql/statement.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::strlen, std::strstr, std::memset, std::memcpy
+#include <cassert>
+
+#include <odb/tracer.hxx>
+
+#include <odb/mssql/mssql.hxx>
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/exceptions.hxx>
+#include <odb/mssql/error.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ // Mapping of bind::buffer_type to SQL_* SQL types.
+ //
+ static const SQLSMALLINT sql_type_lookup [bind::last] =
+ {
+ SQL_BIT, // bind::bit
+ SQL_TINYINT, // bind::tinyint
+ SQL_SMALLINT, // bind::smallint
+ SQL_INTEGER, // bind::int_
+ SQL_BIGINT, // bind::bigint
+
+ SQL_DECIMAL, // bind::decimal
+ SQL_DECIMAL, // bind::smallmoney
+ SQL_DECIMAL, // bind::money
+
+ SQL_FLOAT, // bind::float4
+ SQL_FLOAT, // bind::float8
+
+ SQL_VARCHAR, // bind::string
+ SQL_VARCHAR, // bind::long_string
+
+ SQL_WVARCHAR, // bind::nstring
+ SQL_WVARCHAR, // bind::long_nstring
+
+ SQL_VARBINARY, // bind::binary
+ SQL_VARBINARY, // bind::long_binary
+
+ SQL_TYPE_DATE, // bind::date
+ SQL_SS_TIME2, // bind::time
+ SQL_TYPE_TIMESTAMP, // bind::datetime
+ SQL_SS_TIMESTAMPOFFSET, // bind::datetimeoffset
+
+ SQL_GUID, // bind::uniqueidentifier
+ SQL_BINARY // bind::rowversion
+ };
+
+ // Mapping of bind::buffer_type to SQL_C_* C types.
+ //
+ static const SQLSMALLINT c_type_lookup [bind::last] =
+ {
+ SQL_C_BIT, // bind::bit
+ SQL_C_UTINYINT, // bind::tinyint
+ SQL_C_SSHORT, // bind::smallint
+ SQL_C_SLONG, // bind::int_
+ SQL_C_SBIGINT, // bind::bigint
+
+ SQL_C_NUMERIC, // bind::decimal
+ SQL_C_BINARY, // bind::smallmoney
+ SQL_C_BINARY, // bind::money
+
+ SQL_C_FLOAT, // bind::float4
+ SQL_C_DOUBLE, // bind::float8
+
+ SQL_C_CHAR, // bind::string
+ SQL_C_CHAR, // bind::long_string
+
+ SQL_C_WCHAR, // bind::nstring
+ SQL_C_WCHAR, // bind::long_nstring
+
+ SQL_C_BINARY, // bind::binary
+ SQL_C_BINARY, // bind::long_binary
+
+ SQL_C_TYPE_DATE, // bind::date
+ SQL_C_BINARY, // bind::time
+ SQL_C_TYPE_TIMESTAMP, // bind::datetime
+ SQL_C_BINARY, // bind::datetimeoffset
+
+ SQL_C_GUID, // bind::uniqueidentifier
+ SQL_C_BINARY // bind::rowversion
+ };
+
+ // Mapping of bind::buffer_type to fixed buffer capacity values.
+ //
+ static const SQLLEN capacity_lookup [bind::last] =
+ {
+ 1, // bind::bit
+ 1, // bind::tinyint
+ 2, // bind::smallint
+ 4, // bind::int_
+ 8, // bind::bigint
+
+ sizeof (decimal), // bind::decimal
+ 4, // bind::smallmoney
+ 8, // bind::money
+
+ 4, // bind::float4
+ 8, // bind::float8
+
+ 0, // bind::string
+ 0, // bind::long_string
+
+ 0, // bind::nstring
+ 0, // bind::long_nstring
+
+ 0, // bind::binary
+ 0, // bind::long_binary
+
+ sizeof (date), // bind::date
+ sizeof (time), // bind::time
+ sizeof (datetime), // bind::datetime
+ sizeof (datetimeoffset), // bind::datetimeoffset
+
+ 16, // bind::uniqueidentifier
+ 8 // bind::rowversion
+ };
+
+ //
+ // statement
+ //
+
+ statement::
+ statement (connection_type& conn,
+ const string& text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize)
+ : conn_ (conn)
+ {
+ if (process == 0)
+ {
+ text_copy_ = text;
+ text_ = text_copy_.c_str ();
+ }
+ else
+ text_ = text.c_str (); // Temporary, see init().
+
+ init (text.size (), sk, process, optimize);
+ }
+
+ statement::
+ statement (connection_type& conn,
+ const char* text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize,
+ bool copy)
+ : conn_ (conn)
+ {
+ size_t n;
+
+ if (process == 0 && copy)
+ {
+ text_copy_ = text;
+ text_ = text_copy_.c_str ();
+ n = text_copy_.size ();
+ }
+ else
+ {
+ text_ = text;
+ n = strlen (text_); // Potentially temporary, see init().
+ }
+
+ init (n, sk, process, optimize);
+ }
+
+ void statement::
+ init (size_t text_size,
+ statement_kind sk,
+ const binding* proc,
+ bool optimize)
+ {
+ if (proc != 0)
+ {
+ switch (sk)
+ {
+ case statement_select:
+ process_select (text_copy_,
+ text_,
+ proc->bind, proc->count,
+ optimize);
+ break;
+ case statement_insert:
+ process_insert (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '?');
+ break;
+ case statement_update:
+ process_update (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '?');
+ break;
+ case statement_delete:
+ assert (false);
+ }
+
+ text_ = text_copy_.c_str ();
+ text_size = text_copy_.size ();
+ }
+
+ // Empty statement.
+ //
+ if (*text_ == '\0')
+ return;
+
+ SQLRETURN r;
+
+ // Allocate the handle.
+ //
+ {
+ SQLHANDLE h;
+ r = SQLAllocHandle (SQL_HANDLE_STMT, conn_.handle (), &h);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_);
+
+ stmt_.reset (h);
+ }
+
+ // Disable escape sequences.
+ //
+ r = SQLSetStmtAttr (stmt_,
+ SQL_ATTR_NOSCAN,
+ (SQLPOINTER) SQL_NOSCAN_OFF,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->prepare (conn_, *this);
+ }
+
+ // Prepare the statement.
+ //
+ r = SQLPrepareA (stmt_, (SQLCHAR*) text_, (SQLINTEGER) text_size);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ statement::
+ ~statement ()
+ {
+ if (stmt_ != 0)
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->deallocate (conn_, *this);
+ }
+ }
+
+ const char* statement::
+ text () const
+ {
+ return text_;
+ }
+
+ void statement::
+ bind_param (bind* b, size_t n)
+ {
+ SQLRETURN r;
+
+ SQLUSMALLINT i (0);
+ for (bind* end (b + n); b != end; ++b)
+ {
+ if (b->buffer == 0) // Skip NULL entries.
+ continue;
+
+ i++; // Column index is 1-based.
+
+ SQLULEN col_size (0);
+ SQLSMALLINT digits (0);
+ SQLPOINTER buf;
+
+ switch (b->type)
+ {
+ case bind::decimal:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = (SQLULEN) (b->capacity / 100); // precision
+ digits = (SQLSMALLINT) (b->capacity % 100); // scale
+ break;
+ }
+ case bind::smallmoney:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = 10;
+ digits = 4;
+ break;
+ }
+ case bind::money:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = 19;
+ digits = 4;
+ break;
+ }
+ case bind::float4:
+ case bind::float8:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = (SQLULEN) b->capacity; // precision
+ break;
+ }
+ case bind::long_string:
+ case bind::long_binary:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = b->capacity != 0
+ ? (SQLULEN) b->capacity
+ : SQL_SS_LENGTH_UNLIMITED;
+ break;
+ }
+ case bind::long_nstring:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = b->capacity != 0
+ ? (SQLULEN) b->capacity / 2 // In characters, not bytes.
+ : SQL_SS_LENGTH_UNLIMITED;
+ break;
+ }
+ case bind::string:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = b->capacity != 0
+ ? (SQLULEN) b->capacity - 1 // Sans the null-terminator.
+ : SQL_SS_LENGTH_UNLIMITED;
+ break;
+ }
+ case bind::binary:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = b->capacity != 0
+ ? (SQLULEN) b->capacity
+ : SQL_SS_LENGTH_UNLIMITED;
+ break;
+ }
+ case bind::nstring:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = b->capacity != 0
+ // In characters, not bytes, and sans the null-terminator.
+ ? (SQLULEN) (b->capacity / 2 - 1)
+ : SQL_SS_LENGTH_UNLIMITED;
+ break;
+ }
+ case bind::date:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ // Native Client 10.0 requires the correct precision.
+ //
+ col_size = 10;
+ break;
+ }
+ case bind::time:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ digits = (SQLSMALLINT) b->capacity;
+
+ // Native Client 10.0 requires the correct precision.
+ //
+ if (digits == 0)
+ col_size = 8;
+ else
+ col_size = (SQLULEN) (digits + 9);
+
+ break;
+ }
+ case bind::datetime:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ digits = (SQLSMALLINT) b->capacity;
+
+ // Native Client 10.0 requires the correct precision.
+ //
+ if (digits == 0)
+ col_size = 19;
+ else if (digits == 8)
+ {
+ // This is a SMALLDATETIME column which only has the minutes
+ // precision. Documentation indicates that the correct numeric
+ // precision value for this type is 16.
+ //
+ digits = 0;
+ col_size = 16;
+ }
+ else
+ col_size = (SQLULEN) (digits + 20);
+
+ break;
+ }
+ case bind::datetimeoffset:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ digits = (SQLSMALLINT) b->capacity;
+
+ // Native Client 10.0 requires the correct precision.
+ //
+ if (digits == 0)
+ col_size = 26;
+ else
+ col_size = (SQLULEN) (digits + 27);
+
+ break;
+ }
+ case bind::rowversion:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ col_size = 8;
+ break;
+ }
+ default:
+ {
+ buf = (SQLPOINTER) b->buffer;
+ break;
+ }
+ }
+
+ r = SQLBindParameter (
+ stmt_,
+ i,
+ SQL_PARAM_INPUT,
+ c_type_lookup[b->type],
+ sql_type_lookup[b->type],
+ col_size,
+ digits,
+ buf,
+ 0, // buffer capacity (shouldn't be needed for input parameters)
+ b->size_ind);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+ }
+
+ SQLUSMALLINT statement::
+ bind_result (bind* b, size_t n, SQLUSMALLINT& long_count)
+ {
+ long_count = 0;
+ SQLRETURN r;
+
+ SQLUSMALLINT i (0);
+ for (bind* end (b + n); b != end; ++b)
+ {
+ if (b->buffer == 0) // Skip NULL entries.
+ continue;
+
+ SQLLEN cap (capacity_lookup[b->type]);
+
+ switch (b->type)
+ {
+ case bind::string:
+ case bind::nstring:
+ case bind::binary:
+ {
+ cap = b->capacity;
+ break;
+ }
+ case bind::long_string:
+ case bind::long_nstring:
+ case bind::long_binary:
+ {
+ // Long data is not bound.
+ //
+ long_count++;
+ continue;
+ }
+ case bind::last:
+ {
+ assert (false);
+ break;
+ }
+ default:
+ break;
+ }
+
+ r = SQLBindCol (stmt_,
+ ++i, // Column index is 1-based.
+ c_type_lookup[b->type],
+ (SQLPOINTER) b->buffer,
+ cap,
+ b->size_ind);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ return i;
+ }
+
+ SQLRETURN statement::
+ execute ()
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ SQLRETURN r (SQLExecute (stmt_));
+
+ if (r == SQL_NEED_DATA)
+ {
+ details::buffer& tmp_buf (conn_.long_data_buffer ());
+
+ if (tmp_buf.capacity () == 0)
+ tmp_buf.capacity (4096);
+
+ long_callback* pcb;
+ for (;;)
+ {
+ // ODBC seems to already offset the returned pointer for us
+ // in case of a batch.
+ //
+ r = SQLParamData (stmt_, (SQLPOINTER*) &pcb);
+
+ // If we get anything other than SQL_NEED_DATA, then this is
+ // the return code of SQLExecute().
+ //
+ if (r != SQL_NEED_DATA)
+ break;
+
+ // Store the pointer to the long_callback struct in buf on the
+ // first call to the callback. This allows the callback to
+ // redirect further calls to some other callback.
+ //
+ long_callback cb (*pcb);
+ const void* buf (&cb);
+
+ size_t position (0);
+ for (;;)
+ {
+ size_t n;
+ chunk_type chunk;
+
+ cb.callback.param (
+ cb.context.param,
+ &position,
+ &buf,
+ &n,
+ &chunk,
+ tmp_buf.data (),
+ tmp_buf.capacity ());
+
+ r = SQLPutData (
+ stmt_,
+ (SQLPOINTER) (buf != 0 ? buf : &buf), // Always pass non-NULL.
+ chunk != chunk_null ? (SQLLEN) n : SQL_NULL_DATA);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ if (chunk == chunk_one ||
+ chunk == chunk_last ||
+ chunk == chunk_null)
+ break;
+ }
+ }
+ }
+
+ return r;
+ }
+
+ void statement::
+ stream_result (SQLUSMALLINT i, bind* b, size_t n, void* obase, void* nbase)
+ {
+ details::buffer& tmp_buf (conn_.long_data_buffer ());
+
+ if (tmp_buf.capacity () == 0)
+ tmp_buf.capacity (4096);
+
+ SQLRETURN r;
+
+ for (bind* end (b + n); b != end; ++b)
+ {
+ if (b->buffer == 0) // Skip NULL entries.
+ continue;
+
+ bool char_data;
+ switch (b->type)
+ {
+ case bind::long_string:
+ case bind::long_nstring:
+ {
+ char_data = true;
+ break;
+ }
+ case bind::long_binary:
+ {
+ char_data = false;
+ break;
+ }
+ default:
+ {
+ continue; // Not long data.
+ }
+ }
+
+ void* cbp;
+
+ if (obase == 0)
+ cbp = b->buffer;
+ else
+ {
+ // Re-base the pointer.
+ //
+ char* p (static_cast<char*> (b->buffer));
+ char* ob (static_cast<char*> (obase));
+ char* nb (static_cast<char*> (nbase));
+
+ assert (ob <= p);
+ cbp = nb + (p - ob);
+ }
+
+ long_callback cb (*static_cast<long_callback*> (cbp));
+
+ // First determine if the value is NULL as well as try to
+ // get the total data size.
+ //
+ SQLLEN si;
+ r = SQLGetData (stmt_,
+ ++i,
+ c_type_lookup[b->type],
+ tmp_buf.data (), // Dummy value.
+ 0,
+ &si);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ // Store the pointer to the long_callback struct in buf on the
+ // first call to the callback. This allows the callback to
+ // redirect further calls to some other callback.
+ //
+ void* buf (&cb);
+ size_t size (0);
+ size_t position (0);
+ size_t size_left (si == SQL_NO_TOTAL ? 0 : static_cast<size_t> (si));
+
+ chunk_type c (si == SQL_NULL_DATA
+ ? chunk_null
+ : (si == 0 ? chunk_one : chunk_first));
+
+ for (;;)
+ {
+ cb.callback.result (
+ cb.context.result,
+ &position,
+ &buf,
+ &size,
+ c,
+ size_left,
+ tmp_buf.data (),
+ tmp_buf.capacity ());
+
+ if (c == chunk_last || c == chunk_one || c == chunk_null)
+ break;
+
+ // SQLGetData() can keep returning SQL_SUCCESS_WITH_INFO (truncated)
+ // with SQL_NO_TOTAL for all the calls except the last one. For the
+ // last call we should get SQL_SUCCESS and the size_indicator should
+ // contain a valid value.
+ //
+ r = SQLGetData (stmt_,
+ i,
+ c_type_lookup[b->type],
+ (SQLPOINTER) buf,
+ (SQLLEN) size,
+ &si);
+
+ if (r == SQL_SUCCESS)
+ {
+ assert (si != SQL_NO_TOTAL);
+
+ // Actual amount of data copied to the buffer (appears not to
+ // include the NULL terminator).
+ //
+ size = static_cast<size_t> (si);
+ c = chunk_last;
+ }
+ else if (r == SQL_SUCCESS_WITH_INFO)
+ {
+ if (char_data)
+ size--; // NULL terminator.
+
+ c = chunk_next;
+ }
+ else
+ translate_error (r, conn_, stmt_);
+
+ // Update the total.
+ //
+ if (size_left != 0)
+ size_left -= size;
+ }
+ }
+ }
+
+ //
+ // bulk_statement
+ //
+ bulk_statement::
+ ~bulk_statement () {}
+
+ void bulk_statement::
+ init (size_t skip)
+ {
+ // Setup row-wise batch operation. We set the actual number of
+ // parameter sets in the batch in execute().
+ //
+ SQLRETURN r;
+
+ r = SQLSetStmtAttr (stmt_,
+ SQL_ATTR_PARAM_BIND_TYPE,
+ (SQLPOINTER) skip,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ r = SQLSetStmtAttr (stmt_,
+ SQL_ATTR_PARAMS_PROCESSED_PTR,
+ (SQLPOINTER) &processed_,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ r = SQLSetStmtAttr (stmt_,
+ SQL_ATTR_PARAM_STATUS_PTR,
+ (SQLPOINTER) status_,
+ 0);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ SQLRETURN bulk_statement::
+ execute (size_t n, multiple_exceptions* mex)
+ {
+ mex_ = mex;
+
+ if (status_ != 0)
+ {
+ SQLRETURN r (SQLSetStmtAttr (stmt_,
+ SQL_ATTR_PARAMSET_SIZE,
+ (SQLPOINTER) n,
+ 0));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ // Some SQL* functions would only update the status in case of
+ // an error.
+ //
+ memset (status_, 0, sizeof (status_[0]) * n);
+ }
+
+ processed_ = 0;
+ SQLRETURN r (statement::execute ());
+ bool ok (SQL_SUCCEEDED (r) || r == SQL_NO_DATA);
+
+ // If we have a batch of 1 parameter set, SQL Server ODBC driver
+ // returns the error via SQLExecute() rather than via the status
+ // array even if we set all the attributes necessary for row-wise
+ // binding. So what we are going to do here is convert this case
+ // to the batch way of reporting errors (not that we also check
+ // processed_ so that we only do this is the parameter set was
+ // actually attempted).
+ //
+ if (!ok && status_ != 0 && n == 1 && processed_ == 1)
+ {
+ status_[0] = SQL_PARAM_ERROR;
+ r = SQL_SUCCESS;
+ ok = true;
+ }
+
+ // If the statement failed as a whole, assume no parameter sets
+ // were attempted in case of a batch. Otherwise, the documentation
+ // says that the native client driver keeps processing remaining
+ // sets even in case of an error.
+ //
+ i_ = 0;
+ n_ = (ok ? n : (status_ == 0 ? 1 : 0));
+
+ if (mex_ != 0)
+ {
+ mex_->current (i_);
+ mex_->attempted (processed_);
+ }
+
+ if (!ok)
+ {
+ if (mex_ != 0)
+ mex_->fatal (true); // An incomplete batch is always fatal.
+
+ return r;
+ }
+
+ return r;
+ }
+
+ size_t bulk_statement::
+ extract_errors ()
+ {
+ size_t e (0);
+
+ for (size_t i (0); i != n_; ++i)
+ {
+ if (status_[i] != SQL_PARAM_SUCCESS &&
+ status_[i] != SQL_PARAM_SUCCESS_WITH_INFO)
+ {
+ translate_error (SQL_ERROR, conn_, stmt_, i, mex_);
+ e++;
+ }
+ }
+
+ return e;
+ }
+
+ unsigned long long bulk_statement::
+ affected (SQLRETURN r, size_t errors, bool unique)
+ {
+ unsigned long long rows (0);
+
+ // SQL_NO_DATA indicates that the statement hasn't affected any rows.
+ //
+ if (r != SQL_NO_DATA)
+ {
+ SQLLEN n;
+ r = SQLRowCount (stmt_, &n);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ // If all the parameter sets failed, then the returned count is -1,
+ // which means "not available" according to the documentation.
+ //
+ rows = (n != -1 ? static_cast<unsigned long long> (n) : 0);
+ }
+
+ if (n_ > 1) // Batch.
+ {
+ if (rows != 0) // Some rows did get affected.
+ {
+ // Subtract the parameter sets that failed since they haven't
+ // affected any rows.
+ //
+ size_t p (n_ - errors);
+
+ if (p > 1) // True batch.
+ {
+ if (unique) // Each can affect 0 or 1 row.
+ {
+ rows = (p == static_cast<size_t> (rows)
+ ? 1
+ : result_unknown);
+ }
+ else
+ rows = result_unknown;
+ }
+ }
+ }
+
+ return rows;
+ }
+
+ //
+ // select_statement
+ //
+ select_statement::
+ ~select_statement ()
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ bool optimize,
+ binding& param,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ result_ (result)
+ {
+ if (!empty ())
+ {
+ bind_param (param.bind, param.count);
+ result_count_ = bind_result (result.bind, result.count, long_count_);
+ }
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& param,
+ binding& result,
+ bool copy_text)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize,
+ copy_text),
+ result_ (result)
+ {
+ if (!empty ())
+ {
+ bind_param (param.bind, param.count);
+ result_count_ = bind_result (result.bind, result.count, long_count_);
+ }
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ bool optimize,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ result_ (result)
+ {
+ if (!empty ())
+ result_count_ = bind_result (result.bind, result.count, long_count_);
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& result,
+ bool copy_text)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize,
+ copy_text),
+ result_ (result)
+ {
+ if (!empty ())
+ result_count_ = bind_result (result.bind, result.count, long_count_);
+ }
+
+ void select_statement::
+ execute ()
+ {
+ SQLRETURN r (statement::execute ());
+
+ // Skip empty result sets that seem to be added as a result of
+ // executing DML statements in stored procedures (e.g., INSERT
+ // INTO EXEC).
+ //
+ if (r == SQL_NO_DATA)
+ {
+ r = SQLMoreResults (stmt_);
+
+ if (r == SQL_NO_DATA)
+ {
+ throw database_exception (
+ 0,
+ "?????",
+ "another result set expected after SQL_NO_DATA");
+ }
+ }
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ // Skip result sets that have no columns. These seem to be added
+ // by DML statements that don't produce any result (e.g., EXEC).
+ //
+ for (columns_ = 0; columns_ == 0;)
+ {
+ {
+ SQLSMALLINT c;
+ r = SQLNumResultCols (stmt_, &c);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ columns_ = static_cast<SQLUSMALLINT> (c);
+ }
+
+ if (columns_ == 0)
+ {
+ r = SQLMoreResults (stmt_);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+ }
+
+ // Make sure that the number of columns in the result returned by
+ // the database matches the number that we expect. A common cause
+ // of this assertion is a native view with a number of data members
+ // not matching the number of columns in the SELECT-list.
+ //
+ assert (columns_ == result_count_ + long_count_);
+ }
+
+ select_statement::result select_statement::
+ fetch ()
+ {
+ change_callback* cc (result_.change_callback);
+
+ if (cc != 0 && cc->callback != 0)
+ (cc->callback) (cc->context);
+
+ // Don't bother calling SQLFetch() if there are no columns.
+ //
+ if (columns_ == 0)
+ return no_data;
+ else
+ {
+ SQLRETURN r (SQLFetch (stmt_));
+
+ if (r == SQL_NO_DATA)
+ return no_data;
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ return success;
+ }
+ }
+
+ void select_statement::
+ free_result ()
+ {
+ // Use SQLFreeStmt(SQL_CLOSE) instead of SQLCloseCursor() to avoid an
+ // error if a cursor is already closed. This can happens, for example,
+ // if we are trying to close the cursor after the transaction has been
+ // committed (e.g., when destroying the query result) which also closes
+ // the cursor.
+ //
+ SQLRETURN r (SQLFreeStmt (stmt_, SQL_CLOSE));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ //
+ // insert_statement
+ //
+
+ insert_statement::
+ ~insert_statement ()
+ {
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ binding& param,
+ bool returning_id,
+ bool returning_version,
+ binding* returning)
+ : bulk_statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false,
+ param.batch, param.skip, param.status),
+ returning_id_ (returning_id),
+ returning_version_ (returning_version),
+ ret_ (returning)
+ {
+ bind_param (param.bind, param.count);
+
+ if (ret_ != 0)
+ init_result ();
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param,
+ bool returning_id,
+ bool returning_version,
+ binding* returning,
+ bool copy_text)
+ : bulk_statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false,
+ param.batch, param.skip, param.status,
+ copy_text),
+ returning_id_ (returning_id),
+ returning_version_ (returning_version),
+ ret_ (returning)
+ {
+ bind_param (param.bind, param.count);
+
+ if (ret_ != 0)
+ init_result ();
+ }
+
+ template <typename T>
+ static inline T*
+ offset (T* base, size_t count, size_t size)
+ {
+ return reinterpret_cast<T*> (
+ reinterpret_cast<char*> (base) + count * size);
+ }
+
+ void insert_statement::
+ init_result ()
+ {
+ // Figure out if we are using the OUTPUT clause or a batch of
+ // INSERT and SELECT statements. The latter is used to work
+ // around a bug in SQL Server 2005 that causes it to fail
+ // on an INSERT statement with the OUTPUT clause if data
+ // for one of the inserted columns is supplied at execution
+ // (long data).
+ //
+ text_batch_ = (strstr (text_, "OUTPUT INSERTED.") == 0 &&
+ strstr (text_, "output inserted.") == 0);
+
+ // It might seem logical to set up the array of results if this is a
+ // batch (i.e., the SQL_ATTR_ROW_BIND_TYPE, SQL_ATTR_ROW_ARRAY_SIZE).
+ // This won't work because what we are getting is multiple result
+ // sets (each containing a single row) and not multiple rows. As a
+ // result, the SQL Server ODBC driver will always store the data in
+ // the first element of our array. A bit counter-intuitive.
+ //
+ // At the same time it would be conceptually cleaner to have the
+ // returned data extracted into the batch array instead of always
+ // the first element. This is also how other database runtimes (e.g.,
+ // Oracle) behave. So what we are going to do here is emulate this
+ // by making the ODBC driver store the data into the last element
+ // of the batch array and then copying it into the right place
+ // after processing each result set (see fetch() below).
+ //
+ SQLRETURN r;
+ SQLUSMALLINT col (1);
+
+ size_t last (ret_->batch - 1);
+
+ if (returning_id_)
+ {
+ bind& b (ret_->bind[0]); // Auto id is the first element.
+
+ r = SQLBindCol (stmt_,
+ col++,
+ c_type_lookup[b.type],
+ (SQLPOINTER) offset (b.buffer, last, ret_->skip),
+ capacity_lookup[b.type],
+ offset (b.size_ind, last, ret_->skip));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ if (returning_version_)
+ {
+ bind& b (ret_->bind[ret_->count - 1]); // Version is the last element.
+
+ r = SQLBindCol (stmt_,
+ col++,
+ c_type_lookup[b.type],
+ (SQLPOINTER) offset (b.buffer, last, ret_->skip),
+ capacity_lookup[b.type],
+ offset (b.size_ind, last, ret_->skip));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+ }
+
+ size_t insert_statement::
+ execute (size_t n, multiple_exceptions* mex)
+ {
+ // The batch INSERT works in two different ways, depending on
+ // whether we have the OUTPUT clause. If there is no OUTPUT, then
+ // all the parameter sets are processed inside the SQLExecute()
+ // call. If, however, there is OUTPUT, then the sets are
+ // processed one at a time as we consume the results with
+ // the SQLMoreResults() call below. Thus we in effect have
+ // two counts: the "processed so far" as set by the API
+ // (SQL_ATTR_PARAMS_PROCESSED_PTR) and the "to be processed"
+ // (value in n_). Note that in the OUTPUT case if there is an
+ // error, the processed count seems to jump by 2 for some reason.
+ //
+ // The OUTPUT case can be handled in two different ways: we can
+ // "execute" (with SQLMoreResults()) each set as the user moves
+ // from one result to the next (result() call). The advantage of
+ // this approach is that the returned data ends up in the right
+ // place automatically. The potential drawback is that the total
+ // affected row count will only be available at the end. As a
+ // result, this approach probably won't work if we need to handle,
+ // say, UPDATE with OUTPUT (SQLRowCount() does not return an
+ // intermediate total, at least not for INSERT).
+ //
+ // The alternative implementation would call SQLMoreResults()
+ // inside execute() until all the parameter sets are executed.
+ // In this case we will have to copy the extracted data into
+ // the right place in the bindings (or update the binding before
+ // each call to SQLMoreResults()). It is also not clear whether
+ // the diagnostic records for the failed sets would accumulate.
+ // If not, those will have to be stashed into mex on each
+ // iteration.
+ //
+ SQLRETURN r (bulk_statement::execute (n, mex));
+
+ // Statement failed as a whole, assume no parameter sets were
+ // attempted in case of a batch.
+ //
+ if (!SQL_SUCCEEDED (r))
+ {
+ fetch (r);
+ return n_;
+ }
+
+ if (status_ == 0) // Non-batch case.
+ fetch (SQL_SUCCESS);
+ else
+ fetch (status_[i_] == SQL_PARAM_SUCCESS ||
+ status_[i_] == SQL_PARAM_SUCCESS_WITH_INFO
+ ? SQL_SUCCESS : SQL_ERROR);
+
+ return n_;
+ }
+
+ void insert_statement::
+ fetch (SQLRETURN r)
+ {
+ result_ = true;
+
+ if (!SQL_SUCCEEDED (r))
+ {
+ // An auto-assigned object id should never cause a duplicate primary
+ // key.
+ //
+ if (!returning_id_)
+ {
+ // Translate the integrity contraint violation (SQLSTATE 23000)
+ // to the false result value. This code is similar to that found
+ // in translate_error().
+ //
+ char sqlstate[SQL_SQLSTATE_SIZE + 1];
+ SQLINTEGER native_code;
+ SQLSMALLINT msg_size;
+
+ bool cv (false);
+
+ for (SQLSMALLINT i (1);; ++i)
+ {
+ SQLRETURN r;
+
+ // Filter based on row association.
+ //
+ if (mex_ != 0)
+ {
+ SQLLEN n;
+ r = SQLGetDiagField (SQL_HANDLE_STMT,
+ stmt_,
+ i,
+ SQL_DIAG_ROW_NUMBER,
+ &n,
+ 0,
+ 0);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (!SQL_SUCCEEDED (r))
+ continue;
+
+ if (n == SQL_NO_ROW_NUMBER ||
+ n == SQL_ROW_NUMBER_UNKNOWN ||
+ n != static_cast<SQLLEN> (i_ + 1)) // 1-based
+ continue;
+ }
+
+ r= SQLGetDiagRecA (SQL_HANDLE_STMT,
+ stmt_,
+ i,
+ (SQLCHAR*) sqlstate,
+ &native_code,
+ 0,
+ 0,
+ &msg_size);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (SQL_SUCCEEDED (r))
+ {
+ string s (sqlstate);
+
+ if (s == "23000") // Integrity contraint violation.
+ cv = true;
+ else if (s != "01000") // General warning.
+ {
+ // Some other code.
+ //
+ cv = false;
+ break;
+ }
+ }
+ else // SQLGetDiagRec() failure.
+ {
+ cv = false;
+ break;
+ }
+ }
+
+ if (cv)
+ result_ = false;
+ }
+
+ if (result_)
+ {
+ translate_error (r, conn_, stmt_, i_, mex_); // Can return.
+ result_ = false; // Prevent id/version extraction below or.
+ }
+ }
+
+ // Fetch the row containing the id/version if this statement is
+ // returning.
+ //
+ if (result_ && ret_ != 0)
+ {
+ if (text_batch_)
+ {
+ r = SQLMoreResults (stmt_);
+
+ if (r == SQL_NO_DATA)
+ {
+ throw database_exception (
+ 0,
+ "?????",
+ "multiple result sets expected from a batch of statements");
+ }
+ else if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ r = SQLFetch (stmt_);
+
+ if (r != SQL_NO_DATA && !SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ if (r == SQL_NO_DATA)
+ throw database_exception (
+ 0,
+ "?????",
+ "result set expected from a statement with the OUTPUT clause");
+
+ // See init_result() for details on what's going here.
+ //
+ size_t last (ret_->batch - 1);
+ if (i_ != last)
+ {
+ if (returning_id_)
+ {
+ bind& b (ret_->bind[0]); // Auto id is the first element.
+
+ memcpy (offset (b.buffer, i_, ret_->skip),
+ offset (b.buffer, last, ret_->skip),
+ capacity_lookup[b.type]);
+
+ memcpy (offset (b.size_ind, i_, ret_->skip),
+ offset (b.size_ind, last, ret_->skip),
+ sizeof (*b.size_ind));
+ }
+
+ if (returning_version_)
+ {
+ bind& b (ret_->bind[ret_->count - 1]); // Version is the last.
+
+ memcpy (offset (b.buffer, i_, ret_->skip),
+ offset (b.buffer, last, ret_->skip),
+ capacity_lookup[b.type]);
+
+ memcpy (offset (b.size_ind, i_, ret_->skip),
+ offset (b.size_ind, last, ret_->skip),
+ sizeof (*b.size_ind));
+ }
+ }
+ }
+ }
+
+ bool insert_statement::
+ result (size_t i)
+ {
+ assert ((i_ == i || i_ + 1 == i) && i < n_);
+
+ SQLRETURN r;
+
+ // Get to the next result set if necessary.
+ //
+ if (i != i_)
+ {
+ mex_->current (++i_); // mex cannot be NULL since this is a batch.
+
+ // Only in case of the OUTPUT clause do we have multiple result sets.
+ //
+ if (ret_ != 0)
+ {
+ r = SQLMoreResults (stmt_);
+
+ // The actually processed count could have changed (see execute()).
+ //
+ mex_->attempted (processed_);
+
+ if (r == SQL_NO_DATA)
+ {
+ throw database_exception (
+ 0,
+ "?????",
+ "multiple result sets expected from an array of parameters");
+ }
+ }
+
+ fetch (status_[i_] == SQL_PARAM_SUCCESS ||
+ status_[i_] == SQL_PARAM_SUCCESS_WITH_INFO
+ ? SQL_SUCCESS : SQL_ERROR);
+ }
+
+ // Close the cursor if we are done.
+ //
+ if (ret_ != 0 && i_ + 1 == n_)
+ {
+ // Use SQLFreeStmt(SQL_CLOSE) instead of SQLCloseCursor() to avoid
+ // an error if a cursor is not open. This seem to happen if the
+ // statement failure was translated to a parameter set failure in
+ // bulk_statement for batches of one.
+ //
+ r = SQLFreeStmt (stmt_, SQL_CLOSE);
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ return result_;
+ }
+
+ //
+ // update_statement
+ //
+
+ update_statement::
+ ~update_statement ()
+ {
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ binding& param,
+ binding* returning)
+ : bulk_statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false,
+ param.batch, param.skip, param.status),
+ unique_ (false),
+ returning_ (returning != 0)
+ {
+ assert (param.batch == 1); // Specify unique_hint explicitly.
+ init (param, returning);
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const string& text,
+ bool unique,
+ bool process,
+ binding& param,
+ binding* returning)
+ : bulk_statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false,
+ param.batch, param.skip, param.status),
+ unique_ (unique),
+ returning_ (returning != 0)
+ {
+ init (param, returning);
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param,
+ binding* returning,
+ bool copy_text)
+ : bulk_statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false,
+ param.batch, param.skip, param.status,
+ copy_text),
+ unique_ (false),
+ returning_ (returning != 0)
+ {
+ assert (param.batch == 1); // Specify unique_hint explicitly.
+ init (param, returning);
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const char* text,
+ bool unique,
+ bool process,
+ binding& param,
+ binding* returning,
+ bool copy_text)
+ : bulk_statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false,
+ param.batch, param.skip, param.status,
+ copy_text),
+ unique_ (unique),
+ returning_ (returning != 0)
+ {
+ init (param, returning);
+ }
+
+ void update_statement::
+ init (binding& param, binding* ret)
+ {
+ if (!empty ())
+ {
+ bind_param (param.bind, param.count);
+
+ if (ret != 0)
+ {
+ bind& b (ret->bind[ret->count - 1]); // Version is the last element.
+
+ SQLRETURN r (SQLBindCol (stmt_,
+ 1,
+ c_type_lookup[b.type],
+ (SQLPOINTER) b.buffer,
+ capacity_lookup[b.type],
+ b.size_ind));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+ }
+ }
+
+ size_t update_statement::
+ execute (size_t n, multiple_exceptions* mex)
+ {
+ // In batch UPDATE without the OUTPUT clause (which is the
+ // only kind we currently support) all the parameter sets
+ // are processed inside SQLExecute() and the total count of
+ // affected rows is available after it returns.
+ //
+ assert (!returning_ || status_ == 0);
+
+ SQLRETURN r (bulk_statement::execute (n, mex));
+
+ // Statement failed as a whole, assume no parameter sets were
+ // attempted in case of a batch.
+ //
+ if (!(SQL_SUCCEEDED (r) || r == SQL_NO_DATA))
+ {
+ translate_error (r, conn_, stmt_, 0, mex_);
+ return n_;
+ }
+
+ if (status_ == 0) // Non-batch case.
+ {
+ // Fetch the row containing the data if this statement is
+ // returning. We still need to close the cursor even if we
+ // haven't updated any rows.
+ //
+ if (returning_)
+ {
+ r = SQLFetch (stmt_);
+
+ if (r != SQL_NO_DATA && !SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+
+ // We have to get the result after fetching the OUTPUT data
+ // but before closing the cursor.
+ //
+ result_ = affected (SQL_SUCCESS, 0, unique_);
+
+ {
+ SQLRETURN r (SQLCloseCursor (stmt_)); // Don't overwrite r.
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, conn_, stmt_);
+ }
+
+ if (result_ != 0 && r == SQL_NO_DATA)
+ throw database_exception (
+ 0,
+ "?????",
+ "result set expected from a statement with the OUTPUT clause");
+ }
+ else
+ result_ = affected (r, 0, unique_);
+ }
+ else
+ {
+ // Extract error information for failed parameter sets. If we do
+ // this after calling SQLRowCount(), all the diagnostics records
+ // that we need will be gone.
+ //
+ size_t errors (extract_errors ());
+
+ // Figure out the affected row count.
+ //
+ result_ = affected (r, errors, unique_);
+ }
+
+ return n_;
+ }
+
+ //
+ // delete_statement
+ //
+
+ delete_statement::
+ ~delete_statement ()
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const string& text,
+ binding& param)
+ : bulk_statement (conn,
+ text, statement_delete,
+ 0, false,
+ param.batch, param.skip, param.status),
+ unique_ (false)
+ {
+ assert (param.batch == 1); // Specify unique_hint explicitly.
+ bind_param (param.bind, param.count);
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const string& text,
+ bool unique,
+ binding& param)
+ : bulk_statement (conn,
+ text, statement_delete,
+ 0, false,
+ param.batch, param.skip, param.status),
+ unique_ (unique)
+ {
+ bind_param (param.bind, param.count);
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const char* text,
+ binding& param,
+ bool copy_text)
+ : bulk_statement (conn,
+ text, statement_delete,
+ 0, false,
+ param.batch, param.skip, param.status,
+ copy_text),
+ unique_ (false)
+ {
+ assert (param.batch == 1); // Specify unique_hint explicitly.
+ bind_param (param.bind, param.count);
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const char* text,
+ bool unique,
+ binding& param,
+ bool copy_text)
+ : bulk_statement (conn,
+ text, statement_delete,
+ 0, false,
+ param.batch, param.skip, param.status,
+ copy_text),
+ unique_ (unique)
+ {
+ bind_param (param.bind, param.count);
+ }
+
+ size_t delete_statement::
+ execute (size_t n, multiple_exceptions* mex)
+ {
+ // In batch DELETE without the OUTPUT clause (which is the
+ // only kind we currently support) all the parameter sets
+ // are processed inside SQLExecute() and the total count of
+ // affected rows is available after it returns.
+ //
+
+ SQLRETURN r (bulk_statement::execute (n, mex));
+
+ // Statement failed as a whole, assume no parameter sets were
+ // attempted in case of a batch.
+ //
+ if (!(SQL_SUCCEEDED (r) || r == SQL_NO_DATA))
+ {
+ translate_error (r, conn_, stmt_, 0, mex_);
+ return n_;
+ }
+
+ // Extract error information for failed parameter sets. If we do
+ // this after calling SQLRowCount(), all the diagnostics records
+ // that we need will be gone.
+ //
+ size_t errors (status_ != 0 ? extract_errors () : 0);
+
+ // Figure out the affected row count.
+ //
+ result_ = affected (r, errors, unique_);
+
+ return n_;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/statement.hxx b/libodb-mssql/odb/mssql/statement.hxx
new file mode 100644
index 0000000..74326a0
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statement.hxx
@@ -0,0 +1,558 @@
+// file : odb/mssql/statement.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_STATEMENT_HXX
+#define ODB_MSSQL_STATEMENT_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+#include <odb/statement.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/mssql-fwd.hxx>
+#include <odb/mssql/binding.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/auto-handle.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class connection;
+
+ class LIBODB_MSSQL_EXPORT statement: public odb::statement
+ {
+ public:
+ typedef mssql::connection connection_type;
+
+ virtual
+ ~statement () = 0;
+
+ SQLHSTMT
+ handle () const
+ {
+ return stmt_;
+ }
+
+ virtual const char*
+ text () const;
+
+ virtual connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // A statement can be empty. This is used to handle situations
+ // where a SELECT or UPDATE statement ends up not having any
+ // columns after processing. An empty statement cannot be
+ // executed.
+ //
+ bool
+ empty () const
+ {
+ return stmt_ == 0;
+ }
+
+ protected:
+ // We keep two versions to take advantage of std::string COW.
+ //
+ statement (connection_type&,
+ const std::string& text,
+ statement_kind,
+ const binding* process,
+ bool optimize);
+
+ statement (connection_type&,
+ const char* text,
+ statement_kind,
+ const binding* process,
+ bool optimize,
+ bool copy_text);
+
+ private:
+ void
+ init (std::size_t text_size,
+ statement_kind,
+ const binding* process,
+ bool optimize);
+
+ // Custom implementation for SQL Server that also moves long data
+ // columns to the end.
+ //
+ static void
+ process_select (std::string& result,
+ const char* statement,
+ const bind*,
+ std::size_t bind_size,
+ bool optimize);
+
+ protected:
+ void
+ bind_param (bind*, std::size_t count);
+
+ // Return the actual number of columns bound.
+ //
+ SQLUSMALLINT
+ bind_result (bind*, std::size_t count, SQLUSMALLINT& long_count);
+
+ SQLRETURN
+ execute ();
+
+ // The old_base and new_base arguments can be used to "re-base"
+ // the long_callback struct pointer (stored in bind::buffer).
+ // This is used by the query machinery to cause stream_result()
+ // to use the callback information from a copy of the image
+ // instead of the bound image.
+ //
+ void
+ stream_result (SQLUSMALLINT start_col,
+ bind*,
+ std::size_t count,
+ void* old_base = 0,
+ void* new_base = 0);
+
+ protected:
+ connection_type& conn_;
+ std::string text_copy_;
+ const char* text_;
+ auto_handle<SQL_HANDLE_STMT> stmt_;
+ };
+
+ class LIBODB_MSSQL_EXPORT bulk_statement: public statement
+ {
+ public:
+ virtual
+ ~bulk_statement () = 0;
+
+ protected:
+ bulk_statement (connection_type&,
+ const std::string& text,
+ statement_kind,
+ const binding* process,
+ bool optimize,
+ std::size_t batch,
+ std::size_t skip,
+ SQLUSMALLINT* status);
+
+ bulk_statement (connection_type&,
+ const char* text,
+ statement_kind,
+ const binding* process,
+ bool optimize,
+ std::size_t batch,
+ std::size_t skip,
+ SQLUSMALLINT* status,
+ bool copy_text);
+
+ // Call SQLExecute() and set up the batch tracking variables (see
+ // below). Note that this function does not treat SQL_NO_DATA as
+ // an error since for DELETE and UPDATE statements this is a
+ // shortcut notation for zero rows affected.
+ //
+ SQLRETURN
+ execute (std::size_t n, multiple_exceptions*);
+
+ // Return the number of failed parameter sets.
+ //
+ std::size_t
+ extract_errors ();
+
+ static const unsigned long long result_unknown = ~0ULL;
+
+ unsigned long long
+ affected (SQLRETURN, std::size_t errors, bool unique);
+
+ private:
+ void
+ init (std::size_t skip);
+
+ protected:
+ SQLULEN processed_; // Number of parameter sets processed so far.
+ SQLUSMALLINT* status_; // Parameter sets status array.
+ std::size_t n_; // Actual batch size.
+ std::size_t i_; // Position in result.
+ multiple_exceptions* mex_;
+ };
+
+ class LIBODB_MSSQL_EXPORT select_statement: public statement
+ {
+ public:
+ virtual
+ ~select_statement ();
+
+ // While the long data columns can appear in any order in the
+ // result binding, they should appear last in the statement
+ // text.
+ //
+ select_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& param,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& param,
+ binding& result,
+ bool copy_text = true);
+
+ select_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& result,
+ bool copy_text = true);
+
+ enum result
+ {
+ success,
+ no_data
+ };
+
+ void
+ execute ();
+
+ result
+ fetch ();
+
+ // Return true if any long data was streamed.
+ //
+ bool
+ stream_result (void* old_base = 0, void* new_base = 0)
+ {
+ if (long_count_ != 0)
+ statement::stream_result (result_count_,
+ result_.bind,
+ result_.count,
+ old_base,
+ new_base);
+ return long_count_ != 0;
+ }
+
+ void
+ free_result ();
+
+ private:
+ select_statement (const select_statement&);
+ select_statement& operator= (const select_statement&);
+
+ private:
+ binding& result_;
+ SQLUSMALLINT result_count_; // Actual number of columns bound.
+ SQLUSMALLINT long_count_; // Number of long data columns.
+ SQLUSMALLINT columns_; // Number of columns in result set.
+ };
+
+ struct LIBODB_MSSQL_EXPORT auto_result
+ {
+ explicit auto_result (select_statement& s): s_ (&s) {}
+ ~auto_result () {free ();}
+
+ // Extended interface to support delayed freeing.
+ //
+ auto_result (): s_ (0) {}
+
+ void
+ set (select_statement& s) {s_ = &s;}
+
+ void
+ free ()
+ {
+ if (s_ != 0)
+ {
+ s_->free_result ();
+ s_ = 0;
+ }
+ }
+
+ void
+ release () {s_ = 0;}
+
+ private:
+ auto_result (const auto_result&);
+ auto_result& operator= (const auto_result&);
+
+ private:
+ select_statement* s_;
+ };
+
+ class LIBODB_MSSQL_EXPORT insert_statement: public bulk_statement
+ {
+ public:
+ virtual
+ ~insert_statement ();
+
+ insert_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ binding& param,
+ bool returning_id,
+ bool returning_version,
+ binding* returning);
+
+ insert_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ binding& param,
+ bool returning_id,
+ bool returning_version,
+ binding* returning,
+ bool copy_text = true);
+
+ // Return the number of parameter sets (out of n) that were attempted.
+ //
+ std::size_t
+ execute (std::size_t n, multiple_exceptions& mex)
+ {
+ return execute (n, &mex);
+ }
+
+ // Return true if successful and false if this row is a duplicate.
+ // All other errors are reported by throwing exceptions.
+ //
+ bool
+ result (std::size_t i);
+
+ bool
+ execute ()
+ {
+ execute (1, 0);
+ return result (0);
+ }
+
+ private:
+ insert_statement (const insert_statement&);
+ insert_statement& operator= (const insert_statement&);
+
+ private:
+ void
+ init_result ();
+
+ std::size_t
+ execute (std::size_t, multiple_exceptions*);
+
+ void
+ fetch (SQLRETURN);
+
+ private:
+ bool returning_id_;
+ bool returning_version_;
+ binding* ret_;
+ bool text_batch_;
+
+ bool result_;
+ };
+
+ class LIBODB_MSSQL_EXPORT update_statement: public bulk_statement
+ {
+ public:
+ virtual
+ ~update_statement ();
+
+ // SQL Server native client ODBC driver does not expose individual
+ // affected row counts for batch operations, even though it says it
+ // does (SQLGetInfo(SQL_PARAM_ARRAY_ROW_COUNTS) returns SQL_PARC_BATCH).
+ // Instead, it adds them all up and returns a single count. This is
+ // bad news for us.
+ //
+ // In case of updating by primary key (the affected row count is
+ // either 1 or 0), we can recognize the presumably successful case
+ // where the total affected row count is equal to the batch size
+ // (we can also recognize the "all unsuccessful" case where the
+ // total affected row count is 0). The unique_hint argument in the
+ // constructors below indicates whether this is a "0 or 1" UPDATE
+ // statement.
+ //
+ // In all other situations (provided this is a batch), the result()
+ // function below returns the special result_unknown value.
+ //
+ update_statement (connection_type& conn,
+ const std::string& text,
+ bool process,
+ binding& param,
+ binding* returning);
+
+ update_statement (connection_type& conn,
+ const std::string& text,
+ bool unique_hint,
+ bool process,
+ binding& param,
+ binding* returning);
+
+ update_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param,
+ binding* returning,
+ bool copy_text = true);
+
+ update_statement (connection_type& conn,
+ const char* text,
+ bool unique_hint,
+ bool process,
+ binding& param,
+ binding* returning,
+ bool copy_text = true);
+
+ // Return the number of parameter sets (out of n) that were attempted.
+ //
+ std::size_t
+ execute (std::size_t n, multiple_exceptions& mex)
+ {
+ return execute (n, &mex);
+ }
+
+ // Return the number of rows affected (updated) by the parameter
+ // set. If this is a batch (n > 1 in execute() call above) and it
+ // is impossible to determine the affected row count for each
+ // parameter set, then this function returns result_unknown. All
+ // other errors are reported by throwing exceptions.
+ //
+ using bulk_statement::result_unknown;
+
+ unsigned long long
+ result (std::size_t i)
+ {
+ if (i != i_)
+ mex_->current (++i_); // mex cannot be NULL since this is a batch.
+
+ return result_;
+ }
+
+ unsigned long long
+ execute ()
+ {
+ execute (1, 0);
+ return result (0);
+ }
+
+ private:
+ update_statement (const update_statement&);
+ update_statement& operator= (const update_statement&);
+
+ private:
+ void
+ init (binding& param, binding* ret);
+
+ std::size_t
+ execute (std::size_t, multiple_exceptions*);
+
+ private:
+ bool unique_;
+ bool returning_;
+
+ unsigned long long result_;
+ };
+
+ class LIBODB_MSSQL_EXPORT delete_statement: public bulk_statement
+ {
+ public:
+ virtual
+ ~delete_statement ();
+
+ // SQL Server native client ODBC driver does not expose individual
+ // affected row counts for batch operations, even though it says it
+ // does (SQLGetInfo(SQL_PARAM_ARRAY_ROW_COUNTS) returns SQL_PARC_BATCH).
+ // Instead, it adds them all up and returns a single count. This is
+ // bad news for us.
+ //
+ // In case of deleting by primary key (the affected row count is
+ // either 1 or 0), we can recognize the presumably successful case
+ // where the total affected row count is equal to the batch size
+ // (we can also recognize the "all unsuccessful" case where the
+ // total affected row count is 0). The unique_hint argument in the
+ // constructors below indicates whether this is a "0 or 1" DELETE
+ // statement.
+ //
+ // In all other situations (provided this is a batch), the result()
+ // function below returns the special result_unknown value.
+ //
+ delete_statement (connection_type& conn,
+ const std::string& text,
+ binding& param);
+
+ delete_statement (connection_type& conn,
+ const std::string& text,
+ bool unique_hint,
+ binding& param);
+
+ delete_statement (connection_type& conn,
+ const char* text,
+ binding& param,
+ bool copy_text = true);
+
+ delete_statement (connection_type& conn,
+ const char* text,
+ bool unique_hint,
+ binding& param,
+ bool copy_text = true);
+
+ // Return the number of parameter sets (out of n) that were attempted.
+ //
+ std::size_t
+ execute (std::size_t n, multiple_exceptions& mex)
+ {
+ return execute (n, &mex);
+ }
+
+ // Return the number of rows affected (deleted) by the parameter
+ // set. If this is a batch (n > 1 in execute() call above) and it
+ // is impossible to determine the affected row count for each
+ // parameter set, then this function returns result_unknown. All
+ // other errors are reported by throwing exceptions.
+ //
+ using bulk_statement::result_unknown;
+
+ unsigned long long
+ result (std::size_t i)
+ {
+ if (i != i_)
+ mex_->current (++i_); // mex cannot be NULL since this is a batch.
+
+ return result_;
+ }
+
+ unsigned long long
+ execute ()
+ {
+ execute (1, 0);
+ return result (0);
+ }
+
+ private:
+ delete_statement (const delete_statement&);
+ delete_statement& operator= (const delete_statement&);
+
+ private:
+ std::size_t
+ execute (std::size_t, multiple_exceptions*);
+
+ private:
+ bool unique_;
+ unsigned long long result_;
+ };
+ }
+}
+
+#include <odb/mssql/statement.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_STATEMENT_HXX
diff --git a/libodb-mssql/odb/mssql/statement.ixx b/libodb-mssql/odb/mssql/statement.ixx
new file mode 100644
index 0000000..12cce80
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statement.ixx
@@ -0,0 +1,41 @@
+// file : odb/mssql/statement.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mssql
+ {
+ inline bulk_statement::
+ bulk_statement (connection_type& c,
+ const std::string& text,
+ statement_kind k,
+ const binding* process,
+ bool optimize,
+ std::size_t batch,
+ std::size_t skip,
+ SQLUSMALLINT* status)
+ : statement (c, text, k, process, optimize),
+ status_ (batch == 1 ? 0 : status)
+ {
+ if (status_ != 0 && !empty ())
+ init (skip);
+ }
+
+ inline bulk_statement::
+ bulk_statement (connection_type& c,
+ const char* text,
+ statement_kind k,
+ const binding* process,
+ bool optimize,
+ std::size_t batch,
+ std::size_t skip,
+ SQLUSMALLINT* status,
+ bool copy_text)
+ : statement (c, text, k, process, optimize, copy_text),
+ status_ (batch == 1 ? 0 : status)
+ {
+ if (status_ != 0 && !empty ())
+ init (skip);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/statements-base.cxx b/libodb-mssql/odb/mssql/statements-base.cxx
new file mode 100644
index 0000000..9f302e3
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statements-base.cxx
@@ -0,0 +1,15 @@
+// file : odb/mssql/statements-base.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/statements-base.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ statements_base::
+ ~statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/statements-base.hxx b/libodb-mssql/odb/mssql/statements-base.hxx
new file mode 100644
index 0000000..4506628
--- /dev/null
+++ b/libodb-mssql/odb/mssql/statements-base.hxx
@@ -0,0 +1,63 @@
+// file : odb/mssql/statements-base.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_STATEMENTS_BASE_HXX
+#define ODB_MSSQL_STATEMENTS_BASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/schema-version.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/database.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class LIBODB_MSSQL_EXPORT statements_base: public details::shared_base
+ {
+ public:
+ typedef mssql::connection connection_type;
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Schema version. database::schema_version_migration() is thread-
+ // safe which means it is also slow. Cache the result in statements
+ // so we can avoid the mutex lock. This is thread-safe since if the
+ // version is updated, then the statements cache will be expired.
+ //
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ public:
+ virtual
+ ~statements_base ();
+
+ protected:
+ statements_base (connection_type& conn): conn_ (conn), svm_ (0) {}
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_STATEMENTS_BASE_HXX
diff --git a/libodb-mssql/odb/mssql/tracer.cxx b/libodb-mssql/odb/mssql/tracer.cxx
new file mode 100644
index 0000000..3fe62c9
--- /dev/null
+++ b/libodb-mssql/odb/mssql/tracer.cxx
@@ -0,0 +1,60 @@
+// file : odb/mssql/tracer.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/tracer.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/statement.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ tracer::
+ ~tracer ()
+ {
+ }
+
+ void tracer::
+ prepare (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ execute (connection& c, const statement& s)
+ {
+ execute (c, s.text ());
+ }
+
+ void tracer::
+ deallocate (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ prepare (odb::connection& c, const odb::statement& s)
+ {
+ prepare (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const odb::statement& s)
+ {
+ execute (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const char* s)
+ {
+ execute (static_cast<connection&> (c), s);
+ }
+
+ void tracer::
+ deallocate (odb::connection& c, const odb::statement& s)
+ {
+ deallocate (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/tracer.hxx b/libodb-mssql/odb/mssql/tracer.hxx
new file mode 100644
index 0000000..feaf5f0
--- /dev/null
+++ b/libodb-mssql/odb/mssql/tracer.hxx
@@ -0,0 +1,61 @@
+// file : odb/mssql/tracer.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_TRACER_HXX
+#define ODB_MSSQL_TRACER_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/tracer.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class LIBODB_MSSQL_EXPORT tracer: private odb::tracer
+ {
+ public:
+ virtual
+ ~tracer ();
+
+ virtual void
+ prepare (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const char* statement) = 0;
+
+ virtual void
+ deallocate (connection&, const statement&);
+
+ private:
+ // Allow these classes to convert mssql::tracer to odb::tracer.
+ //
+ friend class database;
+ friend class connection;
+ friend class transaction;
+
+ virtual void
+ prepare (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const char* statement);
+
+ virtual void
+ deallocate (odb::connection&, const odb::statement&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_TRACER_HXX
diff --git a/libodb-mssql/odb/mssql/traits-calls.hxx b/libodb-mssql/odb/mssql/traits-calls.hxx
new file mode 100644
index 0000000..6b20dbd
--- /dev/null
+++ b/libodb-mssql/odb/mssql/traits-calls.hxx
@@ -0,0 +1,190 @@
+// file : odb/mssql/traits-calls.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_TRAITS_CALLS_HXX
+#define ODB_MSSQL_TRAITS_CALLS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/mssql-types.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // object_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = object_traits_impl<T, id_mssql>::versioned>
+ struct object_traits_calls;
+
+ template <typename T>
+ struct object_traits_calls<T, false>
+ {
+ typedef object_traits_impl<T, id_mssql> traits;
+ typedef typename traits::image_type image_type;
+ typedef mssql::bind bind_type;
+
+ object_traits_calls (const schema_version_migration*) {}
+
+ const schema_version_migration*
+ version () const {return 0;}
+
+ static void
+ bind (bind_type* b, image_type& i, statement_kind sk)
+ {
+ traits::bind (b, i, sk);
+ }
+
+ // Poly-derived version.
+ //
+ static void
+ bind (bind_type* b,
+ const bind_type* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk)
+ {
+ traits::bind (b, id, id_size, i, sk);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+
+ static bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id)
+ {
+ return traits::find_ (sts, id);
+ }
+
+ static void
+ load_ (typename traits::statements_type& sts, T& o, bool reload)
+ {
+ return traits::load_ (sts, o, reload);
+ }
+ };
+
+ template <typename T>
+ struct object_traits_calls<T, true>
+ {
+ typedef object_traits_impl<T, id_mssql> traits;
+ typedef typename traits::image_type image_type;
+ typedef mssql::bind bind_type;
+
+ object_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ const schema_version_migration*
+ version () const {return &svm_;}
+
+ void
+ bind (bind_type* b, image_type& i, statement_kind sk) const
+ {
+ traits::bind (b, i, sk, svm_);
+ }
+
+ // Poly-derived version.
+ //
+ void
+ bind (bind_type* b,
+ const bind_type* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk) const
+ {
+ traits::bind (b, id, id_size, i, sk, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id) const
+ {
+ return traits::find_ (sts, id, svm_);
+ }
+
+ void
+ load_ (typename traits::statements_type& sts, T& o, bool reload) const
+ {
+ return traits::load_ (sts, o, reload, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+
+ //
+ // view_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = view_traits_impl<T, id_mssql>::versioned>
+ struct view_traits_calls;
+
+ template <typename T>
+ struct view_traits_calls<T, false>
+ {
+ typedef view_traits_impl<T, id_mssql> traits;
+ typedef typename traits::image_type image_type;
+ typedef mssql::bind bind_type;
+
+ view_traits_calls (const schema_version_migration*) {}
+
+ static void
+ bind (bind_type* b, image_type& i)
+ {
+ traits::bind (b, i);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+ };
+
+ template <typename T>
+ struct view_traits_calls<T, true>
+ {
+ typedef view_traits_impl<T, id_mssql> traits;
+ typedef typename traits::image_type image_type;
+ typedef mssql::bind bind_type;
+
+ view_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ void
+ bind (bind_type* b, image_type& i) const
+ {
+ traits::bind (b, i, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_TRAITS_CALLS_HXX
diff --git a/libodb-mssql/odb/mssql/traits.cxx b/libodb-mssql/odb/mssql/traits.cxx
new file mode 100644
index 0000000..9a9d4fa
--- /dev/null
+++ b/libodb-mssql/odb/mssql/traits.cxx
@@ -0,0 +1,616 @@
+// file : odb/mssql/traits.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/mssql/traits.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // c_array_value_traits_base
+ //
+ void c_array_value_traits_base::
+ set_value (char* const& v,
+ const char* b,
+ size_t n,
+ bool is_null,
+ size_t N)
+ {
+ if (!is_null)
+ {
+ n = n < N ? n : N;
+
+ if (n != 0)
+ memcpy (v, b, n);
+ }
+ else
+ n = 0;
+
+ if (n != N) // Append '\0' if there is space.
+ v[n] = '\0';
+ }
+
+ void c_array_value_traits_base::
+ set_image (char* b,
+ size_t c,
+ size_t& n,
+ bool& is_null,
+ const char* v,
+ size_t N)
+ {
+ is_null = false;
+
+ // Figure out the length. We cannot use strlen since it may
+ // not be 0-terminated (strnlen is not standard).
+ //
+ for (n = 0; n != N && v[n] != '\0'; ++n) ;
+
+ if (n > c)
+ n = c;
+
+ if (n != 0)
+ memcpy (b, v, n);
+ }
+
+ //
+ // default_value_traits<std::string, id_long_string>
+ //
+ void default_value_traits<string, id_long_string>::
+ param_callback (const void* context,
+ size_t*,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void*,
+ size_t)
+ {
+ const string& str (*static_cast<const string*> (context));
+
+ *buffer = str.c_str ();
+ *size = str.size ();
+ *chunk = chunk_one;
+ }
+
+ void default_value_traits<string, id_long_string>::
+ result_callback (void* context,
+ size_t* position,
+ void** buffer,
+ size_t* size,
+ chunk_type chunk,
+ size_t size_left,
+ void* tmp_buf,
+ size_t tmp_capacity)
+ {
+ string& str (*static_cast<string*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ str.clear ();
+ break;
+ }
+ case chunk_first:
+ {
+ // If the total size is available, then pre-allocate the string
+ // and copy the data directly into its buffer in one go. While
+ // this kind of direct modification of the std::string buffer
+ // is not sanctioned by the standard, this is known to work
+ // with all the implementations we care to support. We just
+ // need to make sure the underlying buffer is not shared with
+ // any other instance if the implementation uses COW.
+ //
+ if (size_left != 0)
+ {
+ size_left++; // One extra for the null terminator.
+
+ if (str.size () != size_left)
+ str.resize (size_left);
+ else
+ str[0] = '\0'; // Force copy in a COW implementation.
+
+ *buffer = const_cast<char*> (str.c_str ());
+ *size = size_left;
+ *position = 0; // Indicator.
+ }
+ else
+ {
+ // If the total size is not available, do the short string
+ // optimization by first returning a small temporary buffer.
+ // If the data fits, then we copy it over to the string and
+ // thus get the precise buffer allocation. If the data does
+ // not fit, then we resort to the exponential buffer growth.
+ //
+ *buffer = tmp_buf;
+ *size = tmp_capacity > 128 ? 128 : tmp_capacity;
+ *position = 1; // Indicator.
+ }
+
+ break;
+ }
+ case chunk_next:
+ {
+ // We should only end up here if the short string optimization
+ // didn't work out.
+ //
+ assert (*position != 0);
+
+ if (*position == 1)
+ {
+ // First chunk_next call. There is some data in the temp
+ // buffer which we need to copy over.
+ //
+ str.reserve (256);
+ str.assign (static_cast<char*> (tmp_buf), *size);
+ *position = *size; // Size of actual data in str, which got to be
+ // greater than 128, or we would have gotten
+ // chunk_last.
+ str.resize (256);
+ }
+ else
+ {
+ // Subsequent chunk_next call. Double the buffer and continue.
+ //
+ *position += *size;
+ str.resize (str.size () * 2);
+ }
+
+ *buffer = const_cast<char*> (str.c_str ()) + *position;
+ *size = str.size () - *position;
+ break;
+ }
+ case chunk_last:
+ {
+ if (*position == 0)
+ str.resize (*size);
+ else if (*position == 1)
+ // Short string optimization worked out. There is some data
+ // in the temp buffer which we need to copy over.
+ //
+ str.assign (static_cast<char*> (tmp_buf), *size);
+ else
+ str.resize (*position + *size);
+
+ break;
+ }
+ }
+ }
+
+ //
+ // c_long_string_value_traits
+ //
+ void c_string_long_value_traits::
+ param_callback (const void* context,
+ size_t*,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void*,
+ size_t)
+ {
+ *buffer = context;
+ *size = strlen (static_cast<const char*> (context));
+ *chunk = chunk_one;
+ }
+
+ //
+ // c_warray_value_traits_base
+ //
+ void c_warray_value_traits_base::
+ set_value (wchar_t* const& v,
+ const ucs2_char* b,
+ size_t n,
+ bool is_null,
+ size_t N)
+ {
+ if (!is_null)
+ {
+ n = n < N ? n : N;
+
+ if (n != 0)
+ functions::assign (v, b, n);
+ }
+ else
+ n = 0;
+
+ if (n != N) // Append '\0' if there is space.
+ v[n] = L'\0';
+ }
+
+ void c_warray_value_traits_base::
+ set_image (ucs2_char* b,
+ size_t c,
+ size_t& n,
+ bool& is_null,
+ const wchar_t* v,
+ size_t N)
+ {
+ is_null = false;
+
+ // Figure out the length. We cannot use wcslen since it may
+ // not be 0-terminated (wcsnlen is not standard).
+ //
+ for (n = 0; n != N && v[n] != L'\0'; ++n) ;
+
+ if (n > c)
+ n = c;
+
+ if (n != 0)
+ functions::assign (b, v, n);
+ }
+
+ //
+ // wstring_long_value_traits<2>
+ //
+ void wstring_long_value_traits<2>::
+ param_callback (const void* context,
+ size_t*,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void*,
+ size_t)
+ {
+ const wstring& str (*static_cast<const wstring*> (context));
+
+ *buffer = str.c_str ();
+ *size = str.size () * 2; // In bytes.
+ *chunk = chunk_one;
+ }
+
+ void wstring_long_value_traits<2>::
+ result_callback (void* context,
+ size_t*,
+ void** buffer,
+ size_t* size,
+ chunk_type chunk,
+ size_t size_left,
+ void*,
+ size_t)
+ {
+ wstring& str (*static_cast<wstring*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ str.clear ();
+ break;
+ }
+ case chunk_first:
+ {
+ // The Native Client ODBC driver seems to always be able to
+ // return the total size for national character strings. This
+ // makes things simple and efficient.
+ //
+ assert (size_left != 0);
+
+ size_left /= 2; // Convert to characters.
+ size_left++; // One extra for the null terminator.
+
+ if (str.size () != size_left)
+ str.resize (size_left);
+ else
+ str[0] = L'\0'; // Force copy in a COW implementation.
+
+ *buffer = const_cast<wchar_t*> (str.c_str ());
+ *size = size_left * 2; // In bytes.
+ break;
+ }
+ case chunk_next:
+ {
+ // We should never get here.
+ //
+ assert (false);
+ break;
+ }
+ case chunk_last:
+ {
+ str.resize (*size / 2); // Get rid of the null terminator.
+ break;
+ }
+ }
+ }
+
+ //
+ // wstring_long_value_traits<4>
+ //
+#ifndef _WIN32
+ void wstring_long_value_traits<4>::
+ param_callback (const void* context,
+ size_t* position,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void* tmp_buf,
+ size_t tmp_capacity)
+ {
+ const wstring& str (*static_cast<const wstring*> (context));
+
+ // Here we cannot just return the pointer to the underlying buffer
+ // since the character sizes are different. Instead we copy the
+ // data to the temporary buffer.
+ //
+ *buffer = tmp_buf;
+ *size = str.size () - *position; // In UCS-2 characters.
+
+ if (*size > tmp_capacity / 2)
+ {
+ *size = tmp_capacity / 2;
+ *chunk = chunk_next;
+ }
+ else
+ *chunk = chunk_last;
+
+ wstring_functions<>::assign (static_cast<ucs2_char*> (tmp_buf),
+ str.c_str () + *position,
+ *size);
+ if (*position == 0)
+ {
+ if (*chunk == chunk_last)
+ *chunk = chunk_one;
+ else
+ *chunk = chunk_first;
+ }
+
+ *position += *size;
+ *size *= 2; // Convert to bytes.
+ }
+
+ void wstring_long_value_traits<4>::
+ result_callback (void* context,
+ size_t*,
+ void** buffer,
+ size_t* size,
+ chunk_type chunk,
+ size_t size_left,
+ void* tmp_buf,
+ size_t tmp_capacity)
+ {
+ wstring& str (*static_cast<wstring*> (context));
+
+ // Again, we cannot do direct buffer copy and have to use the
+ // temporary buffer instead.
+ //
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ str.clear ();
+ break;
+ }
+ case chunk_first:
+ {
+ // The Native Client ODBC driver seems to always be able to
+ // return the total size for national character strings. Use
+ // this to reserve enough space in the string.
+ //
+ assert (size_left != 0);
+ str.reserve (size_left / 2);
+ break;
+ }
+ case chunk_next:
+ case chunk_last:
+ {
+ // Append the data from the temporary buffer.
+ //
+ ucs2_char* p (static_cast<ucs2_char*> (tmp_buf));
+ str.append (p, p + *size / 2);
+ break;
+ }
+ }
+
+ if (chunk == chunk_first || chunk == chunk_next)
+ {
+ *buffer = tmp_buf;
+ *size = tmp_capacity;
+ }
+ }
+#endif // _WIN32
+
+ //
+ // c_wstring_long_value_traits<2>
+ //
+ void c_wstring_long_value_traits<2>::
+ param_callback (const void* context,
+ size_t*,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void*,
+ size_t)
+ {
+ *buffer = context;
+ *size = wcslen (static_cast<const wchar_t*> (context)) * 2; // In bytes.
+ *chunk = chunk_one;
+ }
+
+ //
+ // c_wstring_long_value_traits<4>
+ //
+#ifndef _WIN32
+ void c_wstring_long_value_traits<4>::
+ param_callback (const void* context,
+ size_t* position,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void* tmp_buf,
+ size_t tmp_capacity)
+ {
+ const wchar_t* str (static_cast<const wchar_t*> (context));
+
+ // Here we cannot just return the pointer to the buffer since the
+ // character sizes are different. Instead we copy the data to the
+ // temporary buffer.
+ //
+ *buffer = tmp_buf;
+ *size = wcslen (str) - *position; // In UCS-2 characters.
+
+ if (*size > tmp_capacity / 2)
+ {
+ *size = tmp_capacity / 2;
+ *chunk = chunk_next;
+ }
+ else
+ *chunk = chunk_last;
+
+ wstring_functions<>::assign (static_cast<ucs2_char*> (tmp_buf),
+ str + *position,
+ *size);
+ if (*position == 0)
+ {
+ if (*chunk == chunk_last)
+ *chunk = chunk_one;
+ else
+ *chunk = chunk_first;
+ }
+
+ *position += *size;
+ *size *= 2; // Convert to bytes.
+ }
+#endif // _WIN32
+
+ //
+ // default_value_traits<std::vector<char>, id_long_binary>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<char>, id_long_binary>::
+ param_callback (const void* context,
+ size_t*,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void*,
+ size_t)
+ {
+ const value_type& v (*static_cast<const value_type*> (context));
+
+ *buffer = v.empty () ? 0 : &v.front ();
+ *size = v.size ();
+ *chunk = chunk_one;
+ }
+
+ void default_value_traits<std::vector<char>, id_long_binary>::
+ result_callback (void* context,
+ size_t*,
+ void** buffer,
+ size_t* size,
+ chunk_type chunk,
+ size_t size_left,
+ void*,
+ size_t)
+ {
+ value_type& v (*static_cast<value_type*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ v.clear ();
+ break;
+ }
+ case chunk_first:
+ {
+ // The Native Client ODBC driver seems to always be able to
+ // return the total size. This makes things simple and
+ // efficient.
+ //
+ assert (size_left != 0);
+
+ v.resize (size_left);
+ *buffer = &v.front ();
+ *size = size_left;
+ break;
+ }
+ case chunk_next:
+ {
+ // We should never get here.
+ //
+ assert (false);
+ break;
+ }
+ case chunk_last:
+ {
+ // Nothing to do here. The vector is already of the correct size
+ // and should contain the data.
+ break;
+ }
+ }
+ }
+
+ //
+ // default_value_traits<std::vector<unsigned char>, id_long_binary>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<unsigned char>, id_long_binary>::
+ param_callback (const void* context,
+ size_t*,
+ const void** buffer,
+ size_t* size,
+ chunk_type* chunk,
+ void*,
+ size_t)
+ {
+ const value_type& v (*static_cast<const value_type*> (context));
+
+ *buffer = v.empty () ? 0 : &v.front ();
+ *size = v.size ();
+ *chunk = chunk_one;
+ }
+
+ void default_value_traits<std::vector<unsigned char>, id_long_binary>::
+ result_callback (void* context,
+ size_t*,
+ void** buffer,
+ size_t* size,
+ chunk_type chunk,
+ size_t size_left,
+ void*,
+ size_t)
+ {
+ // The code is exactly the same as in the vector<char> specialization.
+ //
+ value_type& v (*static_cast<value_type*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ v.clear ();
+ break;
+ }
+ case chunk_first:
+ {
+ assert (size_left != 0);
+
+ v.resize (size_left);
+ *buffer = &v.front ();
+ *size = size_left;
+ break;
+ }
+ case chunk_next:
+ {
+ assert (false);
+ break;
+ }
+ case chunk_last:
+ {
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/traits.hxx b/libodb-mssql/odb/mssql/traits.hxx
new file mode 100644
index 0000000..f53e535
--- /dev/null
+++ b/libodb-mssql/odb/mssql/traits.hxx
@@ -0,0 +1,2176 @@
+// file : odb/mssql/traits.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_TRAITS_HXX
+#define ODB_MSSQL_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cstring> // std::memcpy, std::memset, std::strlen
+#include <cwchar> // std::wcslen
+
+#ifdef ODB_CXX11
+# include <array>
+#endif
+
+#ifdef _WIN32
+typedef struct _GUID GUID;
+#endif
+
+#include <odb/traits.hxx>
+#include <odb/wrapper-traits.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/mssql-types.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/wrapper-p.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ enum database_type_id
+ {
+ id_bit,
+ id_tinyint,
+ id_smallint,
+ id_int,
+ id_bigint,
+
+ id_decimal, // DECIMAL; NUMERIC
+
+ id_smallmoney,
+ id_money,
+
+ id_float4, // REAL; FLOAT(n) with n <= 24
+ id_float8, // FLOAT(n) with n > 24
+
+ id_string, // CHAR(n), VARCHAR(n) with n <= N
+ id_long_string, // CHAR(n), VARCHAR(n) with n > N; TEXT
+
+ id_nstring, // NCHAR(n), NVARCHAR(n) with 2*n <= N
+ id_long_nstring, // NCHAR(n), NVARCHAR(n) with 2*n > N; NTEXT
+
+ id_binary, // BINARY(n), VARBINARY(n) with n <= N
+ id_long_binary, // BINARY(n), VARBINARY(n) with n > N; IMAGE
+
+ id_date, // DATE
+ id_time, // TIME
+ id_datetime, // DATETIME; DATETIME2; SMALLDATETIME
+ id_datetimeoffset, // DATETIMEOFFSET
+
+ id_uniqueidentifier, // UNIQUEIDENTIFIER
+ id_rowversion // ROWVERSION; TIMESTAMP
+ };
+
+ //
+ // image_traits
+ //
+
+ template <database_type_id>
+ struct image_traits;
+
+ template <>
+ struct image_traits<id_bit> {typedef unsigned char image_type;};
+
+ template <>
+ struct image_traits<id_tinyint> {typedef unsigned char image_type;};
+
+ template <>
+ struct image_traits<id_smallint> {typedef short image_type;};
+
+ template <>
+ struct image_traits<id_int> {typedef int image_type;};
+
+ template <>
+ struct image_traits<id_bigint> {typedef long long image_type;};
+
+ template <>
+ struct image_traits<id_decimal> {typedef decimal image_type;};
+
+ template <>
+ struct image_traits<id_smallmoney> {typedef smallmoney image_type;};
+
+ template <>
+ struct image_traits<id_money> {typedef money image_type;};
+
+ template <>
+ struct image_traits<id_float4> {typedef float image_type;};
+
+ template <>
+ struct image_traits<id_float8> {typedef double image_type;};
+
+ template <>
+ struct image_traits<id_string> {typedef char* image_type;};
+
+ template <>
+ struct image_traits<id_long_string> {typedef long_callback image_type;};
+
+ template <>
+ struct image_traits<id_nstring> {typedef ucs2_char* image_type;};
+
+ template <>
+ struct image_traits<id_long_nstring> {typedef long_callback image_type;};
+
+ template <>
+ struct image_traits<id_binary> {typedef char* image_type;};
+
+ template <>
+ struct image_traits<id_long_binary> {typedef long_callback image_type;};
+
+ template <>
+ struct image_traits<id_date> {typedef date image_type;};
+
+ template <>
+ struct image_traits<id_time> {typedef time image_type;};
+
+ template <>
+ struct image_traits<id_datetime> {typedef datetime image_type;};
+
+ template <>
+ struct image_traits<id_datetimeoffset>
+ {
+ typedef datetimeoffset image_type;
+ };
+
+ template <>
+ struct image_traits<id_uniqueidentifier>
+ {
+ typedef uniqueidentifier image_type;
+ };
+
+ // Image is an 8-byte sequence.
+ //
+ template <>
+ struct image_traits<id_rowversion> {typedef unsigned char* image_type;};
+
+ //
+ // value_traits
+ //
+
+ template <typename W, database_type_id, bool null_handler>
+ struct wrapped_value_traits;
+
+ template <typename T, database_type_id>
+ struct default_value_traits;
+
+ template <typename T, database_type_id, bool w = details::wrapper_p<T>::r>
+ struct select_traits;
+
+ template <typename T, database_type_id ID>
+ struct select_traits<T, ID, false>
+ {
+ typedef default_value_traits<T, ID> type;
+ };
+
+ template <typename W, database_type_id ID>
+ struct select_traits<W, ID, true>
+ {
+ typedef
+ wrapped_value_traits<W, ID, wrapper_traits<W>::null_handler>
+ type;
+ };
+
+ template <typename T, database_type_id ID>
+ class value_traits: public select_traits<T, ID>::type
+ {
+ };
+
+ // The wrapped_value_traits specializations should be able to handle
+ // any value type which means we have to have every possible signature
+ // of the set_value() and set_image() functions.
+ //
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, false>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // string, binary.
+ //
+ static void
+ set_value (W& v, const char* i, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+
+ // nstring.
+ //
+ static void
+ set_value (W& v, const ucs2_char* i, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (ucs2_char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+
+ // long_string, long_nstring, long_binary.
+ //
+ static void
+ set_value (W& v, result_callback_type& cb, void*& context)
+ {
+ vtraits::set_value (wtraits::set_ref (v), cb, context);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const W& v)
+ {
+ vtraits::set_image (cb, context, is_null, wtraits::get_ref (v));
+ }
+
+ // time, datetime, datetimeoffset.
+ //
+ static void
+ set_image (image_type& i, unsigned short s, bool& is_null, const W& v)
+ {
+ vtraits::set_image (i, s, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, true>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // string, binary.
+ //
+ static void
+ set_value (W& v, const char* i, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+
+ // nstring.
+ //
+ static void
+ set_value (W& v, const ucs2_char* i, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (ucs2_char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+
+ // long_string, long_nstring, long_binary.
+ //
+ static void
+ set_value (W& v, result_callback_type& cb, void*& context)
+ {
+ // We have to use our own callback since the NULL information
+ // is only available during streaming.
+ //
+ cb = &result_callback;
+ context = &v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (cb, context, is_null, wtraits::get_ref (v));
+ }
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ // time, datetime, datetimeoffset.
+ //
+ static void
+ set_image (image_type& i, unsigned short s, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, s, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename T, database_type_id ID>
+ struct default_value_traits
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ static void
+ set_value (T& v, const image_type& i, bool is_null)
+ {
+ if (!is_null)
+ v = T (i);
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, T v)
+ {
+ is_null = false;
+ i = image_type (v);
+ }
+ };
+
+ // smallmoney as float/double.
+ //
+ template <typename T>
+ struct smallmoney_float_value_traits
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef smallmoney image_type;
+
+ static void
+ set_value (T& v, const smallmoney& i, bool is_null)
+ {
+ if (!is_null)
+ v = T (i.value / 10000) + T (i.value % 10000) / 10000;
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (smallmoney& i, bool& is_null, T v)
+ {
+ is_null = false;
+ i.value = static_cast<int> (v) * 10000 +
+ static_cast<int> (v * 10000) % 10000;
+ }
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<float, id_smallmoney>:
+ smallmoney_float_value_traits<float>
+ {
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<double, id_smallmoney>:
+ smallmoney_float_value_traits<double>
+ {
+ };
+
+ // smallmoney as integer.
+ //
+ template <typename T>
+ struct default_value_traits<T, id_smallmoney>
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef smallmoney image_type;
+
+ static void
+ set_value (T& v, const smallmoney& i, bool is_null)
+ {
+ if (!is_null)
+ v = static_cast<T> (i.value);
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (smallmoney& i, bool& is_null, T v)
+ {
+ is_null = false;
+ i.value = static_cast<int> (v);
+ }
+ };
+
+ // money as float/double.
+ //
+ template <typename T>
+ struct money_float_value_traits
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef money image_type;
+
+ static void
+ set_value (T& v, const money& i, bool is_null)
+ {
+ if (!is_null)
+ {
+ long long iv ((static_cast<long long> (i.high) << 32) | i.low);
+ v = T (iv / 10000) + T (iv % 10000) / 10000;
+ }
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (money& i, bool& is_null, T v)
+ {
+ is_null = false;
+ long long iv (static_cast<long long> (v) * 10000 +
+ static_cast<long long> (v * 10000) % 10000);
+ i.high = static_cast<int> (iv >> 32);
+ i.low = static_cast<unsigned int> (iv);
+ }
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<float, id_money>:
+ money_float_value_traits<float>
+ {
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<double, id_money>:
+ money_float_value_traits<double>
+ {
+ };
+
+ // money as integer.
+ //
+ template <typename T>
+ struct default_value_traits<T, id_money>
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef money image_type;
+
+ static void
+ set_value (T& v, const money& i, bool is_null)
+ {
+ if (!is_null)
+ {
+ long long iv ((static_cast<long long> (i.high) << 32) | i.low);
+ v = static_cast<T> (iv);
+ }
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (money& i, bool& is_null, T v)
+ {
+ is_null = false;
+ long long iv (static_cast<long long> (v));
+ i.high = static_cast<int> (iv >> 32);
+ i.low = static_cast<unsigned int> (iv);
+ }
+ };
+
+ // std::string specialization for string.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::string, id_string>
+ {
+ typedef std::string value_type;
+ typedef std::string query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (std::string& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ v.assign (b, n);
+ else
+ v.erase ();
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const std::string& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > c)
+ n = c;
+
+ if (n != 0)
+ std::memcpy (b, v.c_str (), n);
+ }
+ };
+
+ // char*/const char* specialization for string.
+ //
+ // Specialization for const char* which only supports initialization
+ // of an image from the value but not the other way around. This way
+ // we can pass such values to the queries.
+ //
+ class LIBODB_MSSQL_EXPORT c_string_value_traits
+ {
+ public:
+ typedef const char* value_type;
+ typedef char* image_type;
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ n = std::strlen (v);
+
+ if (n > c)
+ n = c;
+
+ if (n != 0)
+ std::memcpy (b, v, n);
+ }
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<char*, id_string>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<const char*, id_string>:
+ c_string_value_traits {};
+
+ // char[N] specialization.
+ //
+ struct LIBODB_MSSQL_EXPORT c_array_value_traits_base
+ {
+ static void
+ set_value (char* const& v,
+ const char* b,
+ std::size_t n,
+ bool is_null,
+ std::size_t N);
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const char* v,
+ std::size_t N);
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_string>
+ {
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char* const& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (v, b, n, is_null, N);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ c_array_value_traits_base::set_image (b, c, n, is_null, v, N);
+ }
+ };
+
+ // std::array<char, N> (string) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_string>
+ {
+ typedef std::array<char, N> value_type;
+ typedef std::array<char, N> query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (v.data (), b, n, is_null, N);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_array_value_traits_base::set_image (b, c, n, is_null, v.data (), N);
+ }
+ };
+#endif
+
+ // char specialization.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<char, id_string>
+ {
+ typedef char value_type;
+ typedef char query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char& v,
+ const char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (&v, b, n, is_null, 1);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ char v)
+ {
+ c_array_value_traits_base::set_image (b, c, n, is_null, &v, 1);
+ }
+ };
+
+ // std::string specialization for long_string.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::string,
+ id_long_string>
+ {
+ typedef std::string value_type;
+ typedef std::string query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (std::string& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = &v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const std::string& v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = &v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+ // char*/const char* specialization for long_string.
+ //
+ class LIBODB_MSSQL_EXPORT c_string_long_value_traits
+ {
+ public:
+ typedef const char* value_type;
+ typedef long_callback image_type;
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<
+ char*, id_long_string>: c_string_long_value_traits {};
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<
+ const char*, id_long_string>: c_string_long_value_traits {};
+
+ // char[N] specialization for long_string.
+ //
+ template <std::size_t N>
+ struct c_array_long_value_traits_base
+ {
+ static void
+ set_value (char* const& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_long_string>
+ {
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef long_callback image_type;
+
+ static void
+ set_value (char* const& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ c_array_long_value_traits_base<N>::set_value (v, cb, context);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const char* v)
+ {
+ c_array_long_value_traits_base<N>::set_image (cb, context, is_null, v);
+ }
+ };
+
+ // std::array<char, N> (long_string) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_long_string>
+ {
+ typedef std::array<char, N> value_type;
+ typedef std::array<char, N> query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ c_array_long_value_traits_base<N>::set_value (v.data (), cb, context);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_array_long_value_traits_base<N>::set_image (
+ cb, context, is_null, v.data ());
+ }
+ };
+#endif
+
+ // std::wstring specialization for nstring.
+ //
+ template <std::size_t = sizeof (wchar_t)>
+ struct wstring_functions
+ {
+ static void
+ assign (std::wstring& s, const ucs2_char* b, std::size_t n)
+ {
+ s.assign (b, b + n);
+ }
+
+ static void
+ assign (ucs2_char* b, const wchar_t* s, std::size_t n)
+ {
+ for (std::size_t i (0); i < n; ++i)
+ b[i] = static_cast<ucs2_char> (s[i]);
+ }
+
+ // Even though this is not used when ucs2_char == wchar_t, the
+ // compiler will still compile the signatures and complain.
+ //
+#ifndef _WIN32
+ static void
+ assign (wchar_t* s, const ucs2_char* b, std::size_t n)
+ {
+ for (std::size_t i (0); i < n; ++i)
+ s[i] = static_cast<wchar_t> (b[i]);
+ }
+#endif
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT wstring_functions<sizeof (ucs2_char)>
+ {
+ static void
+ assign (std::wstring& s, const ucs2_char* b, std::size_t n)
+ {
+ s.assign (reinterpret_cast<const wchar_t*> (b), n);
+ }
+
+ static void
+ assign (ucs2_char* b, const wchar_t* s, std::size_t n)
+ {
+ std::memcpy (b, s, n * sizeof (ucs2_char));
+ }
+
+ // On Windows ucs2_char is wchar_t which makes this function
+ // the same as the one above.
+ //
+#ifndef _WIN32
+ static void
+ assign (wchar_t* s, const ucs2_char* b, std::size_t n)
+ {
+ std::memcpy (s, b, n * sizeof (ucs2_char));
+ }
+#endif
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::wstring, id_nstring>
+ {
+ typedef std::wstring value_type;
+ typedef std::wstring query_type;
+ typedef ucs2_char* image_type;
+
+ typedef wstring_functions<> functions;
+
+ static void
+ set_value (std::wstring& v,
+ const ucs2_char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ functions::assign (v, b, n);
+ else
+ v.erase ();
+ }
+
+ static void
+ set_image (ucs2_char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const std::wstring& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > c)
+ n = c;
+
+ if (n != 0)
+ functions::assign (b, v.c_str (), n);
+ }
+ };
+
+ // wchar_t*/const wchar_t* specialization for nstring.
+ //
+ class LIBODB_MSSQL_EXPORT c_wstring_value_traits
+ {
+ public:
+ typedef const wchar_t* value_type;
+ typedef ucs2_char* image_type;
+
+ typedef wstring_functions<> functions;
+
+ static void
+ set_image (ucs2_char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ is_null = false;
+ n = std::wcslen (v);
+
+ if (n > c)
+ n = c;
+
+ if (n != 0)
+ functions::assign (b, v, n);
+ }
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<wchar_t*, id_nstring>:
+ c_wstring_value_traits {};
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<
+ const wchar_t*, id_nstring>: c_wstring_value_traits {};
+
+ // wchar_t[N] specialization.
+ //
+ struct LIBODB_MSSQL_EXPORT c_warray_value_traits_base
+ {
+ typedef wstring_functions<> functions;
+
+ static void
+ set_value (wchar_t* const& v,
+ const ucs2_char* b,
+ std::size_t n,
+ bool is_null,
+ std::size_t N);
+
+ static void
+ set_image (ucs2_char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const wchar_t* v,
+ std::size_t N);
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<wchar_t[N], id_nstring>
+ {
+ typedef wchar_t* value_type;
+ typedef wchar_t query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (wchar_t* const& v,
+ const ucs2_char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_warray_value_traits_base::set_value (v, b, n, is_null, N);
+ }
+
+ static void
+ set_image (ucs2_char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ c_warray_value_traits_base::set_image (b, c, n, is_null, v, N);
+ }
+ };
+
+ // std::array<wchar_t, N> (string) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_value_traits<std::array<wchar_t, N>, id_nstring>
+ {
+ typedef std::array<wchar_t, N> value_type;
+ typedef std::array<wchar_t, N> query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const ucs2_char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_warray_value_traits_base::set_value (v.data (), b, n, is_null, N);
+ }
+
+ static void
+ set_image (ucs2_char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_warray_value_traits_base::set_image (b, c, n, is_null, v.data (), N);
+ }
+ };
+#endif
+
+ // wchar_t specialization.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<wchar_t, id_nstring>
+ {
+ typedef wchar_t value_type;
+ typedef wchar_t query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (wchar_t& v,
+ const ucs2_char* b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_warray_value_traits_base::set_value (&v, b, n, is_null, 1);
+ }
+
+ static void
+ set_image (ucs2_char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ wchar_t v)
+ {
+ c_warray_value_traits_base::set_image (b, c, n, is_null, &v, 1);
+ }
+ };
+
+ // std::wstring specialization for long_nstring.
+ //
+ template <std::size_t = sizeof (wchar_t)>
+ struct wstring_long_value_traits;
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT wstring_long_value_traits<2>
+ {
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+#ifndef _WIN32
+ template <>
+ struct LIBODB_MSSQL_EXPORT wstring_long_value_traits<4>
+ {
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+#endif
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::wstring,
+ id_long_nstring>
+ {
+ typedef std::wstring value_type;
+ typedef std::wstring query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (std::wstring& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &wstring_long_value_traits<>::result_callback;
+ context = &v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const std::wstring& v)
+ {
+ is_null = false;
+ cb = &wstring_long_value_traits<>::param_callback;
+ context = &v;
+ }
+ };
+
+ // wchar_t*/const wchar_t* specialization for long_nstring.
+ //
+ template <std::size_t = sizeof (wchar_t)>
+ struct c_wstring_long_value_traits;
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT c_wstring_long_value_traits<2>
+ {
+ typedef const wchar_t* value_type;
+ typedef long_callback image_type;
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+#ifndef _WIN32
+ template <>
+ struct LIBODB_MSSQL_EXPORT c_wstring_long_value_traits<4>
+ {
+ typedef const wchar_t* value_type;
+ typedef long_callback image_type;
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+#endif
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<
+ wchar_t*, id_long_nstring>: c_wstring_long_value_traits<> {};
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<
+ const wchar_t*, id_long_nstring>: c_wstring_long_value_traits<> {};
+
+ // char[N] specialization for long_nstring.
+ //
+ template <std::size_t N, std::size_t = sizeof (wchar_t)>
+ struct c_warray_long_value_traits_base;
+
+ template <std::size_t N>
+ struct c_warray_long_value_traits_base<N, 2>
+ {
+ static void
+ set_value (wchar_t* const& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+#ifndef _WIN32
+ template <std::size_t N>
+ struct c_warray_long_value_traits_base<N, 4>
+ {
+ static void
+ set_value (wchar_t* const& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+#endif
+
+ template <std::size_t N>
+ struct default_value_traits<wchar_t[N], id_long_nstring>
+ {
+ typedef wchar_t* value_type;
+ typedef wchar_t query_type[N];
+ typedef long_callback image_type;
+
+ static void
+ set_value (wchar_t* const& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ c_warray_long_value_traits_base<N>::set_value (v, cb, context);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ c_warray_long_value_traits_base<N>::set_image (
+ cb, context, is_null, v);
+ }
+ };
+
+ // std::array<wchar_t, N> (long_nstring) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_value_traits<std::array<wchar_t, N>, id_long_nstring>
+ {
+ typedef std::array<wchar_t, N> value_type;
+ typedef std::array<wchar_t, N> query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ c_warray_long_value_traits_base<N>::set_value (
+ v.data (), cb, context);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_warray_long_value_traits_base<N>::set_image (
+ cb, context, is_null, v.data ());
+ }
+ };
+#endif
+
+ // std::vector (buffer) specialization for binary.
+ //
+ template <typename C>
+ struct vector_binary_value_traits
+ {
+ typedef std::vector<C> value_type;
+ typedef std::vector<C> query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (value_type& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (!is_null)
+ {
+ const C* cb (reinterpret_cast<const C*> (b));
+ v.assign (cb, cb + n);
+ }
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > c)
+ n = c;
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ std::memcpy (b, &v.front (), n);
+ }
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::vector<char>,
+ id_binary>:
+ vector_binary_value_traits<char>
+ {
+ };
+
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::vector<unsigned char>,
+ id_binary>:
+ vector_binary_value_traits<unsigned char>
+ {
+ };
+
+ // C array (buffer) specialization for binary.
+ //
+ template <typename C, std::size_t N>
+ struct c_array_binary_value_traits
+ {
+ typedef C* value_type;
+ typedef C query_type[N];
+ typedef char* image_type;
+
+ static void
+ set_value (C* const& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b, n < N ? n : N);
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const C* v)
+ {
+ is_null = false;
+ n = c > N ? N : c;
+ std::memcpy (b, v, n);
+ }
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_binary>:
+ c_array_binary_value_traits<char, N>
+ {
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<unsigned char[N], id_binary>:
+ c_array_binary_value_traits<unsigned char, N>
+ {
+ };
+
+#ifdef ODB_CXX11
+ // std::array (buffer) specialization for binary.
+ //
+ template <typename C, std::size_t N>
+ struct array_binary_value_traits
+ {
+ typedef std::array<C, N> value_type;
+ typedef value_type query_type;
+ typedef char* image_type;
+
+ static void
+ set_value (value_type& v, const char* b, std::size_t n, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b, n < N ? n : N);
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (char* b,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = c > N ? N : c;
+ std::memcpy (b, v.data (), n);
+ }
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_binary>:
+ array_binary_value_traits<char, N>
+ {
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<unsigned char, N>, id_binary>:
+ array_binary_value_traits<unsigned char, N>
+ {
+ };
+#endif
+
+ // std::vector<char> (buffer) specialization for long_binary.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::vector<char>,
+ id_long_binary>
+ {
+ typedef std::vector<char> value_type;
+ typedef std::vector<char> query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = &v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = &v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+ // std::vector<unsigned char> (buffer) specialization for long_binary.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<std::vector<unsigned char>,
+ id_long_binary>
+ {
+ typedef std::vector<unsigned char> value_type;
+ typedef std::vector<unsigned char> query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = &v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = &v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+ // C array (buffer) specialization for long_binary.
+ //
+ template <typename C, std::size_t N>
+ struct c_array_long_binary_value_traits
+ {
+ static void
+ set_value (C* const& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ cb = &result_callback;
+ context = v;
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const C* v)
+ {
+ is_null = false;
+ cb = &param_callback;
+ context = v;
+ }
+
+ static void
+ param_callback (const void* context,
+ std::size_t* position,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+
+ static void
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity);
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_long_binary>:
+ c_array_long_binary_value_traits<char, N>
+ {
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef long_callback image_type;
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<unsigned char[N], id_long_binary>:
+ c_array_long_binary_value_traits<unsigned char, N>
+ {
+ typedef unsigned char* value_type;
+ typedef unsigned char query_type[N];
+ typedef long_callback image_type;
+ };
+
+#ifdef ODB_CXX11
+ // std::array (buffer) specialization for long_binary.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_long_binary>
+ {
+ typedef std::array<char, N> value_type;
+ typedef value_type query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ c_array_long_binary_value_traits<char, N>::set_value (
+ v.data (), cb, context);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_array_long_binary_value_traits<char, N>::set_image (
+ cb, context, is_null, v.data ());
+ }
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<unsigned char, N>, id_long_binary>
+ {
+ typedef std::array<unsigned char, N> value_type;
+ typedef value_type query_type;
+ typedef long_callback image_type;
+
+ static void
+ set_value (value_type& v,
+ result_callback_type& cb,
+ void*& context)
+ {
+ c_array_long_binary_value_traits<unsigned char, N>::set_value (
+ v.data (), cb, context);
+ }
+
+ static void
+ set_image (param_callback_type& cb,
+ const void*& context,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_array_long_binary_value_traits<unsigned char, N>::set_image (
+ cb, context, is_null, v.data ());
+ }
+ };
+#endif
+
+ // GUID specialization for uniqueidentifier.
+ //
+#ifdef _WIN32
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<GUID, id_uniqueidentifier>
+ {
+ typedef GUID value_type;
+ typedef GUID query_type;
+ typedef uniqueidentifier image_type;
+
+ static void
+ set_value (GUID& v, const uniqueidentifier& i, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (&v, &i, 16);
+ else
+ std::memset (&v, 0, 16);
+ }
+
+ static void
+ set_image (uniqueidentifier& i, bool& is_null, const GUID& v)
+ {
+ is_null = false;
+ std::memcpy (&i, &v, 16);
+ }
+ };
+#endif
+
+ // char[16] specialization for uniqueidentifier.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<char[16],
+ id_uniqueidentifier>
+ {
+ typedef char* value_type;
+ typedef char query_type[16];
+ typedef uniqueidentifier image_type;
+
+ static void
+ set_value (char* const& v, const uniqueidentifier& i, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, &i, 16);
+ else
+ std::memset (v, 0, 16);
+ }
+
+ static void
+ set_image (uniqueidentifier& i, bool& is_null, const char* v)
+ {
+ is_null = false;
+ std::memcpy (&i, v, 16);
+ }
+ };
+
+ // unsigned long long specialization for rowversion.
+ //
+ template <>
+ struct LIBODB_MSSQL_EXPORT default_value_traits<unsigned long long,
+ id_rowversion>
+ {
+ typedef unsigned long long value_type;
+ typedef unsigned long long query_type;
+ typedef unsigned char* image_type;
+
+ static void
+ set_value (unsigned long long& v, const unsigned char* i, bool is_null)
+ {
+ if (!is_null)
+ {
+ // The value is in the big-endian format.
+ //
+ unsigned char* p (reinterpret_cast<unsigned char*> (&v));
+ p[0] = i[7];
+ p[1] = i[6];
+ p[2] = i[5];
+ p[3] = i[4];
+ p[4] = i[3];
+ p[5] = i[2];
+ p[6] = i[1];
+ p[7] = i[0];
+ }
+ else
+ v = 0;
+ }
+
+ static void
+ set_image (unsigned char* i, bool& is_null, unsigned long long v)
+ {
+ is_null = false;
+
+ // The value is in the big-endian format.
+ //
+ const unsigned char* p (reinterpret_cast<const unsigned char*> (&v));
+ i[0] = p[7];
+ i[1] = p[6];
+ i[2] = p[5];
+ i[3] = p[4];
+ i[4] = p[3];
+ i[5] = p[2];
+ i[6] = p[1];
+ i[7] = p[0];
+ }
+ };
+
+ //
+ // type_traits
+ //
+
+ template <typename T>
+ struct default_type_traits;
+
+ template <typename T>
+ class type_traits: public default_type_traits<T>
+ {
+ };
+
+ // Integral types.
+ //
+ template <>
+ struct default_type_traits<bool>
+ {
+ static const database_type_id db_type_id = id_bit;
+ };
+
+ template <>
+ struct default_type_traits<signed char>
+ {
+ static const database_type_id db_type_id = id_tinyint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned char>
+ {
+ static const database_type_id db_type_id = id_tinyint;
+ };
+
+ template <>
+ struct default_type_traits<short>
+ {
+ static const database_type_id db_type_id = id_smallint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned short>
+ {
+ static const database_type_id db_type_id = id_smallint;
+ };
+
+ template <>
+ struct default_type_traits<int>
+ {
+ static const database_type_id db_type_id = id_int;
+ };
+
+ template <>
+ struct default_type_traits<unsigned int>
+ {
+ static const database_type_id db_type_id = id_int;
+ };
+
+ template <>
+ struct default_type_traits<long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ template <>
+ struct default_type_traits<long long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ // Float types.
+ //
+ template <>
+ struct default_type_traits<float>
+ {
+ static const database_type_id db_type_id = id_float4;
+ };
+
+ template <>
+ struct default_type_traits<double>
+ {
+ static const database_type_id db_type_id = id_float8;
+ };
+
+ // String types.
+ //
+ template <>
+ struct default_type_traits<std::string>
+ {
+ static const database_type_id db_type_id = id_long_string;
+ };
+
+ template <>
+ struct default_type_traits<char*>
+ {
+ static const database_type_id db_type_id = id_long_string;
+ };
+
+ template <>
+ struct default_type_traits<const char*>
+ {
+ static const database_type_id db_type_id = id_long_string;
+ };
+
+ template <std::size_t N>
+ struct default_type_traits<char[N]>
+ {
+ // Use short string by default to minimize code bloat. Can
+ // always be overridden with _val()/_ref().
+ //
+ static const database_type_id db_type_id = id_string;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<char, N> >
+ {
+ // Ditto.
+ //
+ static const database_type_id db_type_id = id_string;
+ };
+#endif
+
+ template <>
+ struct default_type_traits<char>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ // Wide string types.
+ //
+ template <>
+ struct default_type_traits<std::wstring>
+ {
+ static const database_type_id db_type_id = id_long_nstring;
+ };
+
+ template <>
+ struct default_type_traits<wchar_t*>
+ {
+ static const database_type_id db_type_id = id_long_nstring;
+ };
+
+ template <>
+ struct default_type_traits<const wchar_t*>
+ {
+ static const database_type_id db_type_id = id_long_nstring;
+ };
+
+ template <std::size_t N>
+ struct default_type_traits<wchar_t[N]>
+ {
+ // Use short string by default to minimize code bloat. Can
+ // always be overridden with _val()/_ref().
+ //
+ static const database_type_id db_type_id = id_nstring;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<wchar_t, N> >
+ {
+ // Ditto.
+ //
+ static const database_type_id db_type_id = id_nstring;
+ };
+#endif
+
+ template <>
+ struct default_type_traits<wchar_t>
+ {
+ static const database_type_id db_type_id = id_nstring;
+ };
+
+ // Binary types.
+ //
+ template <std::size_t N>
+ struct default_type_traits<unsigned char[N]>
+ {
+ static const database_type_id db_type_id = id_long_binary;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<char> >
+ {
+ static const database_type_id db_type_id = id_long_binary;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<unsigned char> >
+ {
+ static const database_type_id db_type_id = id_long_binary;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<unsigned char, N> >
+ {
+ static const database_type_id db_type_id = id_long_binary;
+ };
+#endif
+
+ // GUID.
+ //
+#ifdef _WIN32
+ template <>
+ struct default_type_traits<GUID>
+ {
+ static const database_type_id db_type_id = id_uniqueidentifier;
+ };
+#endif
+ }
+}
+
+#include <odb/mssql/traits.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_TRAITS_HXX
diff --git a/libodb-mssql/odb/mssql/traits.txx b/libodb-mssql/odb/mssql/traits.txx
new file mode 100644
index 0000000..683054c
--- /dev/null
+++ b/libodb-mssql/odb/mssql/traits.txx
@@ -0,0 +1,399 @@
+// file : odb/mssql/traits.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+namespace odb
+{
+ namespace mssql
+ {
+ //
+ // wrapped_value_traits<W, ID, true>
+ //
+ template <typename W, database_type_id ID>
+ void wrapped_value_traits<W, ID, true>::
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buffer,
+ std::size_t tmp_capacity)
+ {
+ W& v (*static_cast<W*> (context));
+
+ if (chunk == chunk_null)
+ wtraits::set_null (v);
+ else
+ {
+ long_callback& c (*static_cast<long_callback*> (*buffer));
+
+ // Redirect all further calls.
+ //
+ vtraits::set_value (wtraits::set_ref (v),
+ c.callback.result,
+ c.context.result);
+
+ // Forward this call.
+ //
+ c.callback.result (
+ c.context.result,
+ position,
+ buffer,
+ size,
+ chunk,
+ size_left,
+ tmp_buffer,
+ tmp_capacity);
+ }
+ }
+
+ //
+ // c_array_long_value_traits_base
+ //
+ template <std::size_t N>
+ void c_array_long_value_traits_base<N>::
+ param_callback (const void* context,
+ std::size_t*,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void*,
+ std::size_t)
+ {
+ // Figure out the length. We cannot use strlen since it may
+ // not be 0-terminated (strnlen is not standard).
+ //
+ size_t n (0);
+ for (; n != N && static_cast<const char*> (context)[n] != '\0'; ++n);
+
+ *buffer = context;
+ *size = n;
+ *chunk = chunk_one;
+ }
+
+ template <std::size_t N>
+ void c_array_long_value_traits_base<N>::
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t,
+ void* tmp_buf,
+ std::size_t tmp_capacity)
+ {
+ char* p (static_cast<char*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ *p = '\0';
+ break;
+ }
+ case chunk_first:
+ {
+ *buffer = p;
+ *size = N;
+ break;
+ }
+ case chunk_next:
+ {
+ // ODBC insists on appending '\0' to each chunk it returns.
+ // As a result, we can get here if the last character did not
+ // fit into our buffer. There could also be more data but since
+ // there is no way to stop until we read all the data, dump
+ // the remainder into the temporary buffer.
+ //
+
+ // Use position to indicate whether this is the first "next
+ // chunk".
+ //
+ if (*position == 0)
+ *position = 1;
+ else if (*position == 1)
+ {
+ p[N - 1] = *static_cast<const char*> (tmp_buf);
+ *position = 2;
+ }
+
+ *buffer = tmp_buf;
+ *size = tmp_capacity;
+ break;
+ }
+ case chunk_last:
+ {
+ if (*position == 0)
+ {
+ if (*size < N) // Append '\0' if there is space.
+ p[*size] = '\0';
+ }
+ else if (*position == 1)
+ p[N - 1] = *static_cast<const char*> (tmp_buf);
+
+ break;
+ }
+ }
+ }
+
+ //
+ // c_warray_long_value_traits_base<2>
+ //
+ template <std::size_t N>
+ void c_warray_long_value_traits_base<N, 2>::
+ param_callback (const void* context,
+ std::size_t*,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void*,
+ std::size_t)
+ {
+ // Figure out the length. We cannot use wcslen since it may
+ // not be 0-terminated (wcsnlen is not standard).
+ //
+ size_t n (0);
+ for (; n != N && static_cast<const wchar_t*> (context)[n] != L'\0'; ++n);
+
+ *buffer = context;
+ *size = n * 2; // In bytes.
+ *chunk = chunk_one;
+ }
+
+ template <std::size_t N>
+ void c_warray_long_value_traits_base<N, 2>::
+ result_callback (void* context,
+ std::size_t* position,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t,
+ void* tmp_buf,
+ std::size_t tmp_capacity)
+ {
+ wchar_t* p (static_cast<wchar_t*> (context));
+
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ *p = L'\0';
+ break;
+ }
+ case chunk_first:
+ {
+ *buffer = p;
+ *size = N * 2; // In bytes
+ break;
+ }
+ case chunk_next:
+ {
+ // ODBC insists on appending '\0' to each chunk it returns.
+ // As a result, we can get here if the last character did not
+ // fit into our buffer. There could also be more data but since
+ // there is no way to stop until we read all the data, dump
+ // the remainder into the temporary buffer.
+ //
+
+ // Use position to indicate whether this is the first "next
+ // chunk".
+ //
+ if (*position == 0)
+ *position = 1;
+ else if (*position == 1)
+ {
+ p[N - 1] = *static_cast<const wchar_t*> (tmp_buf);
+ *position = 2;
+ }
+
+ *buffer = tmp_buf;
+ *size = tmp_capacity;
+ break;
+ }
+ case chunk_last:
+ {
+ if (*position == 0)
+ {
+ size_t n (*size / 2); // In wide characters.
+ if (n < N) // Append '\0' if there is space.
+ p[n] = L'\0';
+ }
+ else if (*position == 1)
+ p[N - 1] = *static_cast<const wchar_t*> (tmp_buf);
+
+ break;
+ }
+ }
+ }
+
+#ifndef _WIN32
+ //
+ // c_warray_long_value_traits_base<4>
+ //
+ template <std::size_t N>
+ void c_warray_long_value_traits_base<N, 4>::
+ param_callback (const void* context,
+ std::size_t* pos,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void* tmp_buf,
+ std::size_t tmp_capacity)
+ {
+ // Here we cannot just return the pointer to the underlying buffer
+ // since the character sizes are different. Instead we copy the
+ // data to the temporary buffer.
+ //
+ ucs2_char* d (static_cast<ucs2_char*> (tmp_buf));
+ const wchar_t* s (static_cast<const wchar_t*> (context) + *pos);
+
+ size_t n (0);
+ tmp_capacity /= 2; // In UCS-2 character.
+ for (const wchar_t* e (s + N);
+ s != e && *s != L'\0' && n != tmp_capacity;
+ ++n, ++s)
+ d[n] = static_cast<ucs2_char> (*s);
+
+ *buffer = d;
+ *size = n * 2; // In bytes.
+ *chunk = (n != tmp_capacity ? chunk_last : chunk_next);
+
+ if (*pos == 0)
+ {
+ if (*chunk == chunk_last)
+ *chunk = chunk_one;
+ else
+ *chunk = chunk_first;
+ }
+
+ *pos += n;
+ }
+
+ template <std::size_t N>
+ void c_warray_long_value_traits_base<N, 4>::
+ result_callback (void* context,
+ std::size_t* pos,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t,
+ void* tmp_buf,
+ std::size_t tmp_capacity)
+ {
+ wchar_t* d (static_cast<wchar_t*> (context));
+ const ucs2_char* s (static_cast<const ucs2_char*> (tmp_buf));
+
+ // Again, we cannot do direct buffer copy and have to use the
+ // temporary buffer instead.
+ //
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ *d = L'\0';
+ break;
+ }
+ case chunk_first:
+ {
+ break;
+ }
+ case chunk_next:
+ case chunk_last:
+ {
+ // Append the data from the temporary buffer.
+ //
+ if (*pos < N)
+ {
+ *size /= 2; // In UCS-2 characters.
+ size_t n (N - *pos);
+ n = *size < n ? *size : n;
+
+ wstring_functions<>::assign (d + *pos, s, n);
+ *pos += n;
+
+ if (*pos < N) // Append '\0' if there is space.
+ d[*pos] = L'\0';
+ }
+
+ break;
+ }
+ }
+
+ if (chunk == chunk_first || chunk == chunk_next)
+ {
+ *buffer = tmp_buf;
+ *size = tmp_capacity;
+ }
+ }
+#endif
+
+ //
+ // c_array_long_binary_value_traits
+ //
+ template <typename C, std::size_t N>
+ void c_array_long_binary_value_traits<C, N>::
+ param_callback (const void* context,
+ std::size_t*,
+ const void** buffer,
+ std::size_t* size,
+ chunk_type* chunk,
+ void*,
+ std::size_t)
+ {
+ *buffer = context;
+ *size = N;
+ *chunk = chunk_one;
+ }
+
+ template <typename C, std::size_t N>
+ void c_array_long_binary_value_traits<C, N>::
+ result_callback (void* context,
+ std::size_t*,
+ void** buffer,
+ std::size_t* size,
+ chunk_type chunk,
+ std::size_t size_left,
+ void* tmp_buf,
+ std::size_t tmp_capacity)
+ {
+ // The code is similar to the vector<char> specialization.
+ //
+ switch (chunk)
+ {
+ case chunk_null:
+ case chunk_one:
+ {
+ std::memset (context, 0, N);
+ break;
+ }
+ case chunk_first:
+ {
+ assert (size_left != 0);
+
+ *buffer = context;
+ *size = size_left < N ? size_left : N;
+ break;
+ }
+ case chunk_next:
+ {
+ // We can get here if total size is greater than N. There is
+ // no way to stop until we read all the data, so dump the
+ // remainder into the temporary buffer.
+ //
+ *buffer = tmp_buf;
+ *size = tmp_capacity;
+ break;
+ }
+ case chunk_last:
+ {
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/transaction-impl.cxx b/libodb-mssql/odb/mssql/transaction-impl.cxx
new file mode 100644
index 0000000..a44e83f
--- /dev/null
+++ b/libodb-mssql/odb/mssql/transaction-impl.cxx
@@ -0,0 +1,103 @@
+// file : odb/mssql/transaction-impl.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/tracer.hxx>
+
+#include <odb/mssql/mssql.hxx>
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/connection.hxx>
+#include <odb/mssql/transaction-impl.hxx>
+#include <odb/mssql/error.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ transaction_impl::
+ transaction_impl (database_type& db)
+ : odb::transaction_impl (db)
+ {
+ }
+
+ transaction_impl::
+ transaction_impl (connection_ptr c)
+ : odb::transaction_impl (c->database (), *c), connection_ (c)
+ {
+ }
+
+ transaction_impl::
+ ~transaction_impl ()
+ {
+ }
+
+ void transaction_impl::
+ start ()
+ {
+ // Grab a connection if we don't already have one.
+ //
+ if (connection_ == 0)
+ {
+ connection_ = static_cast<database_type&> (database_).connection ();
+ odb::transaction_impl::connection_ = connection_.get ();
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "BEGIN");
+ }
+
+ // In ODBC a transaction is started automatically before the first
+ // statement is executed.
+ //
+ }
+
+ void transaction_impl::
+ commit ()
+ {
+ // Invalidate query results.
+ //
+ connection_->invalidate_results ();
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "COMMIT");
+ }
+
+ SQLRETURN r (
+ SQLEndTran (SQL_HANDLE_DBC, connection_->handle (), SQL_COMMIT));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *connection_, true);
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+
+ void transaction_impl::
+ rollback ()
+ {
+ // Invalidate query results.
+ //
+ connection_->invalidate_results ();
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "ROLLBACK");
+ }
+
+ SQLRETURN r (
+ SQLEndTran (SQL_HANDLE_DBC, connection_->handle (), SQL_ROLLBACK));
+
+ if (!SQL_SUCCEEDED (r))
+ translate_error (r, *connection_, true);
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/transaction-impl.hxx b/libodb-mssql/odb/mssql/transaction-impl.hxx
new file mode 100644
index 0000000..f7189f2
--- /dev/null
+++ b/libodb-mssql/odb/mssql/transaction-impl.hxx
@@ -0,0 +1,49 @@
+// file : odb/mssql/transaction-impl.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_TRANSACTION_IMPL_HXX
+#define ODB_MSSQL_TRANSACTION_IMPL_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class LIBODB_MSSQL_EXPORT transaction_impl: public odb::transaction_impl
+ {
+ public:
+ typedef mssql::database database_type;
+ typedef mssql::connection connection_type;
+
+ transaction_impl (database_type&);
+ transaction_impl (connection_ptr);
+
+ virtual
+ ~transaction_impl ();
+
+ virtual void
+ start ();
+
+ virtual void
+ commit ();
+
+ virtual void
+ rollback ();
+
+ private:
+ connection_ptr connection_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_TRANSACTION_IMPL_HXX
diff --git a/libodb-mssql/odb/mssql/transaction.cxx b/libodb-mssql/odb/mssql/transaction.cxx
new file mode 100644
index 0000000..bb51697
--- /dev/null
+++ b/libodb-mssql/odb/mssql/transaction.cxx
@@ -0,0 +1,26 @@
+// file : odb/mssql/transaction.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/mssql/transaction.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ transaction& transaction::
+ current ()
+ {
+ // While the impl type can be of the concrete type, the transaction
+ // object can be created as either odb:: or odb::mssql:: type. To
+ // work around that we are going to hard-cast one to the other
+ // relying on the fact that they have the same representation and
+ // no virtual functions. The former is checked in the tests.
+ //
+ odb::transaction& b (odb::transaction::current ());
+ assert (dynamic_cast<transaction_impl*> (&b.implementation ()) != 0);
+ return reinterpret_cast<transaction&> (b);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/transaction.hxx b/libodb-mssql/odb/mssql/transaction.hxx
new file mode 100644
index 0000000..8c86515
--- /dev/null
+++ b/libodb-mssql/odb/mssql/transaction.hxx
@@ -0,0 +1,88 @@
+// file : odb/mssql/transaction.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_TRANSACTION_HXX
+#define ODB_MSSQL_TRANSACTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx>
+#include <odb/mssql/tracer.hxx>
+
+#include <odb/mssql/details/export.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ class transaction_impl;
+
+ class LIBODB_MSSQL_EXPORT transaction: public odb::transaction
+ {
+ public:
+ typedef mssql::database database_type;
+ typedef mssql::connection connection_type;
+
+ explicit
+ transaction (transaction_impl*, bool make_current = true);
+
+ transaction ();
+
+ // Return the database this transaction is on.
+ //
+ database_type&
+ database ();
+
+ // Return the underlying database connection for this transaction.
+ //
+ connection_type&
+ connection ();
+
+ connection_type&
+ connection (odb::database&);
+
+ // Return current transaction or throw if there is no transaction
+ // in effect.
+ //
+ static transaction&
+ current ();
+
+ // Set the current thread's transaction.
+ //
+ static void
+ current (transaction&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef mssql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ using odb::transaction::tracer;
+
+ public:
+ transaction_impl&
+ implementation ();
+ };
+ }
+}
+
+#include <odb/mssql/transaction.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_TRANSACTION_HXX
diff --git a/libodb-mssql/odb/mssql/transaction.ixx b/libodb-mssql/odb/mssql/transaction.ixx
new file mode 100644
index 0000000..ac819bc
--- /dev/null
+++ b/libodb-mssql/odb/mssql/transaction.ixx
@@ -0,0 +1,57 @@
+// file : odb/mssql/transaction.ixx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/transaction-impl.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ inline transaction::
+ transaction (transaction_impl* impl, bool make_current)
+ : odb::transaction (impl, make_current)
+ {
+ }
+
+ inline transaction::
+ transaction ()
+ : odb::transaction ()
+ {
+ }
+
+ inline transaction_impl& transaction::
+ implementation ()
+ {
+ // We can use static_cast here since we have an instance of
+ // mssql::transaction.
+ //
+ return static_cast<transaction_impl&> (
+ odb::transaction::implementation ());
+ }
+
+ inline transaction::database_type& transaction::
+ database ()
+ {
+ return static_cast<database_type&> (odb::transaction::database ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection ()
+ {
+ return static_cast<connection_type&> (odb::transaction::connection ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection (odb::database& db)
+ {
+ return static_cast<connection_type&> (odb::transaction::connection (db));
+ }
+
+ inline void transaction::
+ current (transaction& t)
+ {
+ odb::transaction::current (t);
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/version-build2-stub.hxx b/libodb-mssql/odb/mssql/version-build2-stub.hxx
new file mode 100644
index 0000000..969ab3c
--- /dev/null
+++ b/libodb-mssql/odb/mssql/version-build2-stub.hxx
@@ -0,0 +1,4 @@
+// file : odb/mssql/version-build2-stub.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/mssql/version.hxx>
diff --git a/libodb-mssql/odb/mssql/version-build2.hxx b/libodb-mssql/odb/mssql/version-build2.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libodb-mssql/odb/mssql/version-build2.hxx
diff --git a/libodb-mssql/odb/mssql/version-build2.hxx.in b/libodb-mssql/odb/mssql/version-build2.hxx.in
new file mode 100644
index 0000000..9f57dfe
--- /dev/null
+++ b/libodb-mssql/odb/mssql/version-build2.hxx.in
@@ -0,0 +1,42 @@
+// file : odb/mssql/version-build2.hxx.in
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef LIBODB_MSSQL_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_MSSQL_VERSION $libodb_mssql.version.project_number$ULL
+#define LIBODB_MSSQL_VERSION_STR "$libodb_mssql.version.project$"
+#define LIBODB_MSSQL_VERSION_ID "$libodb_mssql.version.project_id$"
+
+#define LIBODB_MSSQL_VERSION_MAJOR $libodb_mssql.version.major$
+#define LIBODB_MSSQL_VERSION_MINOR $libodb_mssql.version.minor$
+#define LIBODB_MSSQL_VERSION_PATCH $libodb_mssql.version.patch$
+
+#define LIBODB_MSSQL_PRE_RELEASE $libodb_mssql.version.pre_release$
+
+#define LIBODB_MSSQL_SNAPSHOT $libodb_mssql.version.snapshot_sn$ULL
+#define LIBODB_MSSQL_SNAPSHOT_ID "$libodb_mssql.version.snapshot_id$"
+
+#include <odb/version.hxx>
+
+$libodb.check(LIBODB_VERSION, LIBODB_SNAPSHOT)$
+
+#endif // LIBODB_MSSQL_VERSION
diff --git a/libodb-mssql/odb/mssql/version.hxx b/libodb-mssql/odb/mssql/version.hxx
new file mode 100644
index 0000000..5b6cc56
--- /dev/null
+++ b/libodb-mssql/odb/mssql/version.hxx
@@ -0,0 +1,48 @@
+// file : odb/mssql/version.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifdef LIBODB_MSSQL_BUILD2
+# include <odb/mssql/version-build2.hxx>
+#else
+
+#ifndef ODB_MSSQL_VERSION_HXX
+#define ODB_MSSQL_VERSION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/mssql/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-mssql version: odb interface version plus the bugfix
+// version.
+//
+#define LIBODB_MSSQL_VERSION 2049976
+#define LIBODB_MSSQL_VERSION_STR "2.5.0-b.26"
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_VERSION_HXX
+#endif // LIBODB_MSSQL_BUILD2
diff --git a/libodb-mssql/odb/mssql/view-result.hxx b/libodb-mssql/odb/mssql/view-result.hxx
new file mode 100644
index 0000000..41e5b8e
--- /dev/null
+++ b/libodb-mssql/odb/mssql/view-result.hxx
@@ -0,0 +1,85 @@
+// file : odb/mssql/view-result.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_VIEW_RESULT_HXX
+#define ODB_MSSQL_VIEW_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/view-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/forward.hxx> // query_base, view_statements
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ class view_result_impl: public odb::view_result_impl<T>
+ {
+ public:
+ typedef odb::view_result_impl<T> base_type;
+
+ typedef typename base_type::view_type view_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef view_traits_impl<view_type, id_mssql> view_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef view_statements<view_type> statements_type;
+
+ virtual
+ ~view_result_impl ();
+
+ view_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (view_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ typedef mssql::change_callback change_callback_type;
+
+ static void
+ change_callback (void* context);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ view_traits_calls<view_type> tc_;
+ bool can_load_;
+ bool use_copy_;
+ typename view_traits::image_type* image_copy_;
+ };
+ }
+}
+
+#include <odb/mssql/view-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_VIEW_RESULT_HXX
diff --git a/libodb-mssql/odb/mssql/view-result.txx b/libodb-mssql/odb/mssql/view-result.txx
new file mode 100644
index 0000000..7818d36
--- /dev/null
+++ b/libodb-mssql/odb/mssql/view-result.txx
@@ -0,0 +1,153 @@
+// file : odb/mssql/view-result.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/mssql/exceptions.hxx> // long_data_reload
+#include <odb/mssql/view-statements.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ view_result_impl<T>::
+ ~view_result_impl ()
+ {
+ invalidate ();
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ invalidate ()
+ {
+ change_callback_type& cc (statements_.image ().change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ delete image_copy_;
+ image_copy_ = 0;
+
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ view_result_impl<T>::
+ view_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ use_copy_ (false),
+ image_copy_ (0)
+ {
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ load (view_type& view)
+ {
+ if (!can_load_)
+ throw long_data_reload ();
+
+ view_traits::callback (this->db_, view, callback_event::pre_load);
+
+ tc_.init (view,
+ use_copy_ ? *image_copy_ : statements_.image (),
+ &this->db_);
+
+ // If we are using a copy, make sure the callback information for
+ // long data also comes from the copy.
+ //
+ can_load_ = !statement_->stream_result (
+ use_copy_ ? &statements_.image () : 0,
+ use_copy_ ? image_copy_ : 0);
+
+ view_traits::callback (this->db_, view, callback_event::post_load);
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ next ()
+ {
+ can_load_ = true;
+ this->current (pointer_type ());
+
+ typename view_traits::image_type& im (statements_.image ());
+ change_callback_type& cc (im.change_callback_);
+
+ if (cc.context == this)
+ {
+ cc.callback = 0;
+ cc.context = 0;
+ }
+
+ use_copy_ = false;
+
+ if (im.version != statements_.image_version ())
+ {
+ binding& b (statements_.image_binding ());
+ tc_.bind (b.bind, im);
+ statements_.image_version (im.version);
+ b.version++;
+ }
+
+ if (statement_->fetch () == select_statement::no_data)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ else
+ {
+ cc.callback = &change_callback;
+ cc.context = this;
+ }
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t view_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ change_callback (void* c)
+ {
+ view_result_impl<T>* r (static_cast<view_result_impl<T>*> (c));
+
+ typename view_traits::image_type& im (r->statements_.image ());
+
+ if (r->image_copy_ == 0)
+ r->image_copy_ = new typename view_traits::image_type (im);
+ else
+ *r->image_copy_ = im;
+
+ im.change_callback_.callback = 0;
+ im.change_callback_.context = 0;
+
+ r->use_copy_ = true;
+ }
+ }
+}
diff --git a/libodb-mssql/odb/mssql/view-statements.hxx b/libodb-mssql/odb/mssql/view-statements.hxx
new file mode 100644
index 0000000..1742cab
--- /dev/null
+++ b/libodb-mssql/odb/mssql/view-statements.hxx
@@ -0,0 +1,83 @@
+// file : odb/mssql/view-statements.hxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#ifndef ODB_MSSQL_VIEW_STATEMENTS_HXX
+#define ODB_MSSQL_VIEW_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mssql/mssql-types.hxx>
+#include <odb/mssql/version.hxx>
+#include <odb/mssql/statement.hxx>
+#include <odb/mssql/statements-base.hxx>
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ class view_statements: public statements_base
+ {
+ public:
+ typedef T view_type;
+ typedef view_traits_impl<view_type, id_mssql> view_traits;
+ typedef typename view_traits::pointer_type pointer_type;
+ typedef typename view_traits::image_type image_type;
+
+ public:
+ view_statements (connection_type&);
+
+ virtual
+ ~view_statements ();
+
+ // View image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ std::size_t
+ image_version () const
+ {
+ return image_version_;
+ }
+
+ void
+ image_version (std::size_t v)
+ {
+ image_version_ = v;
+ }
+
+ binding&
+ image_binding ()
+ {
+ return image_binding_;
+ }
+
+ private:
+ view_statements (const view_statements&);
+ view_statements& operator= (const view_statements&);
+
+ private:
+ image_type image_;
+ std::size_t image_version_;
+ binding image_binding_;
+ bind image_bind_[view_traits::column_count != 0
+ ? view_traits::column_count
+ : 1];
+ };
+ }
+}
+
+#include <odb/mssql/view-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MSSQL_VIEW_STATEMENTS_HXX
diff --git a/libodb-mssql/odb/mssql/view-statements.txx b/libodb-mssql/odb/mssql/view-statements.txx
new file mode 100644
index 0000000..e217523
--- /dev/null
+++ b/libodb-mssql/odb/mssql/view-statements.txx
@@ -0,0 +1,30 @@
+// file : odb/mssql/view-statements.txx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mssql
+ {
+ template <typename T>
+ view_statements<T>::
+ ~view_statements ()
+ {
+ }
+
+ template <typename T>
+ view_statements<T>::
+ view_statements (connection_type& conn)
+ : statements_base (conn),
+ image_binding_ (image_bind_, view_traits::column_count)
+ {
+ image_.version = 0;
+ image_version_ = 0;
+
+ image_binding_.change_callback = image_.change_callback ();
+
+ std::memset (image_bind_, 0, sizeof (image_bind_));
+ }
+ }
+}
diff --git a/libodb-mssql/tests/.gitignore b/libodb-mssql/tests/.gitignore
new file mode 100644
index 0000000..e54525b
--- /dev/null
+++ b/libodb-mssql/tests/.gitignore
@@ -0,0 +1 @@
+driver
diff --git a/libodb-mssql/tests/basics/buildfile b/libodb-mssql/tests/basics/buildfile
new file mode 100644
index 0000000..ee213f5
--- /dev/null
+++ b/libodb-mssql/tests/basics/buildfile
@@ -0,0 +1,6 @@
+# file : tests/basics/buildfile
+# license : ODB NCUEL; see accompanying LICENSE file
+
+import libs = libodb-mssql%lib{odb-mssql}
+
+exe{driver}: {hxx cxx}{*} $libs
diff --git a/libodb-mssql/tests/basics/driver.cxx b/libodb-mssql/tests/basics/driver.cxx
new file mode 100644
index 0000000..04d7231
--- /dev/null
+++ b/libodb-mssql/tests/basics/driver.cxx
@@ -0,0 +1,37 @@
+// file : tests/basics/driver.cxx
+// license : ODB NCUEL; see accompanying LICENSE file
+
+// Basic test to make sure the library is usable. Functionality testing
+// is done in the odb-tests package.
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/mssql/database.hxx>
+#include <odb/mssql/exceptions.hxx>
+#include <odb/mssql/transaction.hxx>
+
+using namespace odb::mssql;
+
+int
+main ()
+{
+ {
+ std::ostringstream os;
+ database::print_usage (os);
+ assert (!os.str ().empty ());
+ }
+
+ // We can't really do much here since that would require a database. We can
+ // create a fake database object as long as we don't expect to get a valid
+ // connection.
+ //
+ database db ("john", "secret", "dummy whammy");
+
+ try
+ {
+ transaction t (db.begin ());
+ assert (false);
+ }
+ catch (const database_exception&) {}
+}
diff --git a/libodb-mssql/tests/build/.gitignore b/libodb-mssql/tests/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-mssql/tests/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-mssql/tests/build/bootstrap.build b/libodb-mssql/tests/build/bootstrap.build
new file mode 100644
index 0000000..895126c
--- /dev/null
+++ b/libodb-mssql/tests/build/bootstrap.build
@@ -0,0 +1,8 @@
+# file : tests/build/bootstrap.build
+# license : ODB NCUEL; see accompanying LICENSE file
+
+project = # Unnamed subproject.
+
+using config
+using dist
+using test
diff --git a/libodb-mssql/tests/build/root.build b/libodb-mssql/tests/build/root.build
new file mode 100644
index 0000000..bbd3781
--- /dev/null
+++ b/libodb-mssql/tests/build/root.build
@@ -0,0 +1,23 @@
+# file : tests/build/root.build
+# license : ODB NCUEL; see accompanying LICENSE file
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+cxx{*}: extension = cxx
+
+if ($cxx.target.system == 'win32-msvc')
+ cxx.poptions += -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS
+
+if ($cxx.class == 'msvc')
+ cxx.coptions += /wd4251 /wd4275 /wd4800
+
+# Every exe{} in this subproject is by default a test.
+#
+exe{*}: test = true
+
+# Specify the test target for cross-testing.
+#
+test.target = $cxx.target
diff --git a/libodb-mssql/tests/buildfile b/libodb-mssql/tests/buildfile
new file mode 100644
index 0000000..fd73adc
--- /dev/null
+++ b/libodb-mssql/tests/buildfile
@@ -0,0 +1,4 @@
+# file : tests/buildfile
+# license : ODB NCUEL; see accompanying LICENSE file
+
+./: {*/ -build/}