summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2024-01-24 19:01:49 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2024-01-24 19:01:49 +0300
commitba80b1f8c8354103bb98d31e252c64254f288273 (patch)
tree6e0fe6e82a58ee0fa4f9817dfc764ac5285a2368
parentb650caf5661dec901eae39e374c2c2ebd625d152 (diff)
parente5d0186db99492a139237067bab841a5b83463af (diff)
Merge branch 'libodb-sqlite' into multi-package
-rw-r--r--libodb-sqlite/.gitignore25
-rw-r--r--libodb-sqlite/GPLv2340
-rw-r--r--libodb-sqlite/INSTALL6
-rw-r--r--libodb-sqlite/LICENSE20
-rw-r--r--libodb-sqlite/README20
-rw-r--r--libodb-sqlite/build/.gitignore3
-rw-r--r--libodb-sqlite/build/bootstrap.build10
-rw-r--r--libodb-sqlite/build/export.build9
-rw-r--r--libodb-sqlite/build/root.build19
-rw-r--r--libodb-sqlite/buildfile9
-rw-r--r--libodb-sqlite/manifest22
-rw-r--r--libodb-sqlite/odb/sqlite/auto-handle.hxx101
-rw-r--r--libodb-sqlite/odb/sqlite/binding.hxx44
-rw-r--r--libodb-sqlite/odb/sqlite/blob-stream.hxx31
-rw-r--r--libodb-sqlite/odb/sqlite/blob.hxx68
-rw-r--r--libodb-sqlite/odb/sqlite/buildfile155
-rw-r--r--libodb-sqlite/odb/sqlite/connection-factory.cxx417
-rw-r--r--libodb-sqlite/odb/sqlite/connection-factory.hxx273
-rw-r--r--libodb-sqlite/odb/sqlite/connection.cxx310
-rw-r--r--libodb-sqlite/odb/sqlite/connection.hxx356
-rw-r--r--libodb-sqlite/odb/sqlite/connection.ixx108
-rw-r--r--libodb-sqlite/odb/sqlite/container-statements.hxx355
-rw-r--r--libodb-sqlite/odb/sqlite/container-statements.txx107
-rw-r--r--libodb-sqlite/odb/sqlite/database.cxx306
-rw-r--r--libodb-sqlite/odb/sqlite/database.hxx567
-rw-r--r--libodb-sqlite/odb/sqlite/database.ixx622
-rw-r--r--libodb-sqlite/odb/sqlite/details/.gitignore1
-rw-r--r--libodb-sqlite/odb/sqlite/details/build2/config-stub.h5
-rw-r--r--libodb-sqlite/odb/sqlite/details/build2/config-vc-stub.h5
-rw-r--r--libodb-sqlite/odb/sqlite/details/build2/config-vc.h15
-rw-r--r--libodb-sqlite/odb/sqlite/details/build2/config.h17
-rw-r--r--libodb-sqlite/odb/sqlite/details/config-vc.h5
-rw-r--r--libodb-sqlite/odb/sqlite/details/config.h.in14
-rw-r--r--libodb-sqlite/odb/sqlite/details/config.hxx33
-rw-r--r--libodb-sqlite/odb/sqlite/details/conversion.hxx58
-rw-r--r--libodb-sqlite/odb/sqlite/details/export.hxx86
-rw-r--r--libodb-sqlite/odb/sqlite/details/options.cli47
-rw-r--r--libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.cxx1027
-rw-r--r--libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.hxx530
-rw-r--r--libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.ixx324
-rw-r--r--libodb-sqlite/odb/sqlite/error.cxx94
-rw-r--r--libodb-sqlite/odb/sqlite/error.hxx27
-rw-r--r--libodb-sqlite/odb/sqlite/exceptions.cxx92
-rw-r--r--libodb-sqlite/odb/sqlite/exceptions.hxx101
-rw-r--r--libodb-sqlite/odb/sqlite/forward.hxx112
-rw-r--r--libodb-sqlite/odb/sqlite/no-id-object-result.hxx80
-rw-r--r--libodb-sqlite/odb/sqlite/no-id-object-result.txx114
-rw-r--r--libodb-sqlite/odb/sqlite/no-id-object-statements.hxx136
-rw-r--r--libodb-sqlite/odb/sqlite/no-id-object-statements.txx36
-rw-r--r--libodb-sqlite/odb/sqlite/polymorphic-object-result.hxx98
-rw-r--r--libodb-sqlite/odb/sqlite/polymorphic-object-result.txx287
-rw-r--r--libodb-sqlite/odb/sqlite/polymorphic-object-statements.hxx479
-rw-r--r--libodb-sqlite/odb/sqlite/polymorphic-object-statements.txx145
-rw-r--r--libodb-sqlite/odb/sqlite/prepared-query.cxx27
-rw-r--r--libodb-sqlite/odb/sqlite/prepared-query.hxx37
-rw-r--r--libodb-sqlite/odb/sqlite/query-const-expr.cxx14
-rw-r--r--libodb-sqlite/odb/sqlite/query-dynamic.cxx157
-rw-r--r--libodb-sqlite/odb/sqlite/query-dynamic.hxx32
-rw-r--r--libodb-sqlite/odb/sqlite/query-dynamic.ixx26
-rw-r--r--libodb-sqlite/odb/sqlite/query-dynamic.txx20
-rw-r--r--libodb-sqlite/odb/sqlite/query.cxx378
-rw-r--r--libodb-sqlite/odb/sqlite/query.hxx1728
-rw-r--r--libodb-sqlite/odb/sqlite/query.ixx46
-rw-r--r--libodb-sqlite/odb/sqlite/query.txx168
-rw-r--r--libodb-sqlite/odb/sqlite/section-statements.hxx198
-rw-r--r--libodb-sqlite/odb/sqlite/section-statements.txx40
-rw-r--r--libodb-sqlite/odb/sqlite/simple-object-result.hxx88
-rw-r--r--libodb-sqlite/odb/sqlite/simple-object-result.txx158
-rw-r--r--libodb-sqlite/odb/sqlite/simple-object-statements.cxx15
-rw-r--r--libodb-sqlite/odb/sqlite/simple-object-statements.hxx591
-rw-r--r--libodb-sqlite/odb/sqlite/simple-object-statements.ixx68
-rw-r--r--libodb-sqlite/odb/sqlite/simple-object-statements.txx146
-rw-r--r--libodb-sqlite/odb/sqlite/sqlite-types.hxx57
-rw-r--r--libodb-sqlite/odb/sqlite/statement-cache.hxx60
-rw-r--r--libodb-sqlite/odb/sqlite/statement-cache.txx60
-rw-r--r--libodb-sqlite/odb/sqlite/statement.cxx988
-rw-r--r--libodb-sqlite/odb/sqlite/statement.hxx392
-rw-r--r--libodb-sqlite/odb/sqlite/statements-base.cxx15
-rw-r--r--libodb-sqlite/odb/sqlite/statements-base.hxx63
-rw-r--r--libodb-sqlite/odb/sqlite/stream.cxx120
-rw-r--r--libodb-sqlite/odb/sqlite/stream.hxx85
-rw-r--r--libodb-sqlite/odb/sqlite/text-stream.hxx31
-rw-r--r--libodb-sqlite/odb/sqlite/text.hxx69
-rw-r--r--libodb-sqlite/odb/sqlite/tracer.cxx60
-rw-r--r--libodb-sqlite/odb/sqlite/tracer.hxx61
-rw-r--r--libodb-sqlite/odb/sqlite/traits-calls.hxx214
-rw-r--r--libodb-sqlite/odb/sqlite/traits.cxx219
-rw-r--r--libodb-sqlite/odb/sqlite/traits.hxx1099
-rw-r--r--libodb-sqlite/odb/sqlite/transaction-impl.cxx172
-rw-r--r--libodb-sqlite/odb/sqlite/transaction-impl.hxx66
-rw-r--r--libodb-sqlite/odb/sqlite/transaction.cxx26
-rw-r--r--libodb-sqlite/odb/sqlite/transaction.hxx87
-rw-r--r--libodb-sqlite/odb/sqlite/transaction.ixx57
-rw-r--r--libodb-sqlite/odb/sqlite/version-build2-stub.hxx4
-rw-r--r--libodb-sqlite/odb/sqlite/version-build2.hxx0
-rw-r--r--libodb-sqlite/odb/sqlite/version-build2.hxx.in42
-rw-r--r--libodb-sqlite/odb/sqlite/version.hxx47
-rw-r--r--libodb-sqlite/odb/sqlite/view-result.hxx80
-rw-r--r--libodb-sqlite/odb/sqlite/view-result.txx114
-rw-r--r--libodb-sqlite/odb/sqlite/view-statements.hxx88
-rw-r--r--libodb-sqlite/odb/sqlite/view-statements.txx33
-rw-r--r--libodb-sqlite/tests/.gitignore1
-rw-r--r--libodb-sqlite/tests/basics/buildfile6
-rw-r--r--libodb-sqlite/tests/basics/driver.cxx52
-rw-r--r--libodb-sqlite/tests/build/.gitignore3
-rw-r--r--libodb-sqlite/tests/build/bootstrap.build8
-rw-r--r--libodb-sqlite/tests/build/root.build23
-rw-r--r--libodb-sqlite/tests/buildfile4
108 files changed, 16714 insertions, 0 deletions
diff --git a/libodb-sqlite/.gitignore b/libodb-sqlite/.gitignore
new file mode 100644
index 0000000..1c363a0
--- /dev/null
+++ b/libodb-sqlite/.gitignore
@@ -0,0 +1,25 @@
+# Compiler/linker output.
+#
+*.d
+*.t
+*.i
+*.i.*
+*.ii
+*.ii.*
+*.o
+*.obj
+*.gcm
+*.pcm
+*.ifc
+*.so
+*.dylib
+*.dll
+*.a
+*.lib
+*.exp
+*.pdb
+*.ilk
+*.exe
+*.exe.dlls/
+*.exe.manifest
+*.pc
diff --git a/libodb-sqlite/GPLv2 b/libodb-sqlite/GPLv2
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/libodb-sqlite/GPLv2
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/libodb-sqlite/INSTALL b/libodb-sqlite/INSTALL
new file mode 100644
index 0000000..3eb4c33
--- /dev/null
+++ b/libodb-sqlite/INSTALL
@@ -0,0 +1,6 @@
+The easiest way to build this package is with the bpkg package manager:
+
+$ bpkg build libodb-sqlite
+
+But if you don't want to use the package manager, then you can also build it
+manually using the standard build2 build system.
diff --git a/libodb-sqlite/LICENSE b/libodb-sqlite/LICENSE
new file mode 100644
index 0000000..9d92da1
--- /dev/null
+++ b/libodb-sqlite/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2009-2024 Code Synthesis Tools CC.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+For more information on ODB licensing as well as for answers to
+some of the common licensing questions, visit the ODB License
+page:
+
+http://www.codesynthesis.com/products/odb/license.xhtml
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
diff --git a/libodb-sqlite/README b/libodb-sqlite/README
new file mode 100644
index 0000000..ffd4049
--- /dev/null
+++ b/libodb-sqlite/README
@@ -0,0 +1,20 @@
+ODB is an object-relational mapping (ORM) system for C++. It provides
+tools, APIs, and library support that allow you to persist C++ objects
+to a relational database (RDBMS) without having to deal with tables,
+columns, or SQL and without manually writing any of the mapping code.
+For more information see:
+
+http://www.codesynthesis.com/products/odb/
+
+This package contains the SQLite ODB runtime library. Every application
+that includes code generated for the SQLite database will need to link
+to this library.
+
+See the NEWS file for the user-visible changes from the previous release.
+
+See the LICENSE file for distribution conditions.
+
+See the INSTALL file for prerequisites and installation instructions.
+
+Send questions, bug reports, or any other feedback to the
+odb-users@codesynthesis.com mailing list.
diff --git a/libodb-sqlite/build/.gitignore b/libodb-sqlite/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-sqlite/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-sqlite/build/bootstrap.build b/libodb-sqlite/build/bootstrap.build
new file mode 100644
index 0000000..bb9c901
--- /dev/null
+++ b/libodb-sqlite/build/bootstrap.build
@@ -0,0 +1,10 @@
+# file : build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = libodb-sqlite
+
+using version
+using config
+using dist
+using test
+using install
diff --git a/libodb-sqlite/build/export.build b/libodb-sqlite/build/export.build
new file mode 100644
index 0000000..8ce77d8
--- /dev/null
+++ b/libodb-sqlite/build/export.build
@@ -0,0 +1,9 @@
+# file : build/export.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+$out_root/
+{
+ include odb/sqlite/
+}
+
+export $out_root/odb/sqlite/lib{odb-sqlite}
diff --git a/libodb-sqlite/build/root.build b/libodb-sqlite/build/root.build
new file mode 100644
index 0000000..a1975db
--- /dev/null
+++ b/libodb-sqlite/build/root.build
@@ -0,0 +1,19 @@
+# file : build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+config [bool] config.libodb_sqlite.develop ?= false
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+ixx{*}: extension = ixx
+txx{*}: extension = txx
+cxx{*}: extension = cxx
+
+if ($cxx.target.system == 'win32-msvc')
+ cxx.poptions += -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS
+
+if ($cxx.class == 'msvc')
+ cxx.coptions += /wd4251 /wd4275 /wd4800
diff --git a/libodb-sqlite/buildfile b/libodb-sqlite/buildfile
new file mode 100644
index 0000000..a04e206
--- /dev/null
+++ b/libodb-sqlite/buildfile
@@ -0,0 +1,9 @@
+# file : buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/} doc{INSTALL NEWS README} legal{GPLv2 LICENSE} manifest
+
+# Don't install tests or the INSTALL file.
+#
+tests/: install = false
+doc{INSTALL}@./: install = false
diff --git a/libodb-sqlite/manifest b/libodb-sqlite/manifest
new file mode 100644
index 0000000..fef85a0
--- /dev/null
+++ b/libodb-sqlite/manifest
@@ -0,0 +1,22 @@
+: 1
+name: libodb-sqlite
+version: 2.5.0-b.26.z
+project: odb
+summary: SQLite ODB runtime library
+license: GPL-2.0-only
+license: other: proprietary ; Not free/open source.
+topics: C++, ORM, SQLite, SQL
+description-file: README
+changes-file: NEWS
+url: https://www.codesynthesis.com/products/odb/
+doc-url: https://www.codesynthesis.com/products/odb/doc/manual.xhtml
+src-url: https://git.codesynthesis.com/cgit/odb/libodb-sqlite/
+email: odb-users@codesynthesis.com
+build-warning-email: odb-builds@codesynthesis.com
+builds: all
+requires: c++11
+depends: * build2 >= 0.16.0-
+depends: * bpkg >= 0.16.0-
+depends: libsqlite3 ^3.6.18
+depends: libodb [2.5.0-b.26.1 2.5.0-b.27)
+depends: * cli ^1.2.0- ? ($config.libodb_sqlite.develop)
diff --git a/libodb-sqlite/odb/sqlite/auto-handle.hxx b/libodb-sqlite/odb/sqlite/auto-handle.hxx
new file mode 100644
index 0000000..d25e919
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/auto-handle.hxx
@@ -0,0 +1,101 @@
+// file : odb/sqlite/auto-handle.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_AUTO_HANDLE_HXX
+#define ODB_SQLITE_AUTO_HANDLE_HXX
+
+#include <odb/pre.hxx>
+
+#include <cassert>
+#include <sqlite3.h>
+
+#include <odb/sqlite/version.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename H>
+ struct handle_traits;
+
+ template <>
+ struct handle_traits<sqlite3>
+ {
+ static void
+ release (sqlite3* h)
+ {
+ if (sqlite3_close (h) == SQLITE_BUSY)
+ {
+ // Connection has outstanding prepared statements.
+ //
+ assert (false);
+ }
+ }
+ };
+
+ template <>
+ struct handle_traits<sqlite3_stmt>
+ {
+ static void
+ release (sqlite3_stmt* h)
+ {
+ sqlite3_finalize (h);
+ }
+ };
+
+ template <typename H>
+ class auto_handle
+ {
+ public:
+ auto_handle (H* h = 0)
+ : h_ (h)
+ {
+ }
+
+ ~auto_handle ()
+ {
+ if (h_ != 0)
+ handle_traits<H>::release (h_);
+ }
+
+ H*
+ get () const
+ {
+ return h_;
+ }
+
+ void
+ reset (H* h = 0)
+ {
+ if (h_ != 0)
+ handle_traits<H>::release (h_);
+
+ h_ = h;
+ }
+
+ H*
+ release ()
+ {
+ H* h (h_);
+ h_ = 0;
+ return h;
+ }
+
+ operator H* () const
+ {
+ return h_;
+ }
+
+ private:
+ auto_handle (const auto_handle&);
+ auto_handle& operator= (const auto_handle&);
+
+ private:
+ H* h_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_AUTO_HANDLE_HXX
diff --git a/libodb-sqlite/odb/sqlite/binding.hxx b/libodb-sqlite/odb/sqlite/binding.hxx
new file mode 100644
index 0000000..3807130
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/binding.hxx
@@ -0,0 +1,44 @@
+// file : odb/sqlite/binding.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_BINDING_HXX
+#define ODB_SQLITE_BINDING_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+
+#include <odb/sqlite/sqlite-types.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class binding
+ {
+ public:
+ typedef sqlite::bind bind_type;
+
+ binding (): bind (0), count (0), version (0) {}
+
+ binding (bind_type* b, std::size_t n)
+ : bind (b), count (n), version (0)
+ {
+ }
+
+ bind_type* bind;
+ std::size_t count;
+ std::size_t version;
+
+ private:
+ binding (const binding&);
+ binding& operator= (const binding&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_BINDING_HXX
diff --git a/libodb-sqlite/odb/sqlite/blob-stream.hxx b/libodb-sqlite/odb/sqlite/blob-stream.hxx
new file mode 100644
index 0000000..caa7c24
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/blob-stream.hxx
@@ -0,0 +1,31 @@
+// file : odb/sqlite/blob-stream.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_BLOB_STREAM_HXX
+#define ODB_SQLITE_BLOB_STREAM_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/sqlite/blob.hxx>
+#include <odb/sqlite/stream.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class blob_stream: public stream
+ {
+ public:
+ blob_stream (const blob& b, bool rw)
+ : stream (b.db ().c_str (),
+ b.table ().c_str (),
+ b.column ().c_str (),
+ b.rowid (),
+ rw) {}
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_BLOB_STREAM_HXX
diff --git a/libodb-sqlite/odb/sqlite/blob.hxx b/libodb-sqlite/odb/sqlite/blob.hxx
new file mode 100644
index 0000000..a4892a2
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/blob.hxx
@@ -0,0 +1,68 @@
+// file : odb/sqlite/blob.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_BLOB_HXX
+#define ODB_SQLITE_BLOB_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+// Carefully allow this header to be included into the ODB compilation.
+//
+#ifndef ODB_COMPILER
+# include <odb/sqlite/forward.hxx>
+#endif
+
+namespace odb
+{
+ namespace sqlite
+ {
+#ifdef ODB_COMPILER
+ #pragma db sqlite:type("BLOB STREAM")
+ class blob
+#else
+ class blob
+#endif
+ {
+ public:
+ // BLOB size to provision for. Set before calling persist() or update().
+ //
+ explicit
+ blob (std::size_t size = 0): size_ (size) {}
+
+ std::size_t size () const {return size_;}
+ void size (std::size_t s) {size_ = s;}
+
+ const std::string& db () const {return db_;}
+ const std::string& table () const {return table_;}
+ const std::string& column () const {return column_;}
+ long long rowid () const {return rowid_;}
+
+ void
+ clear ()
+ {
+ size_ = 0;
+ db_.clear ();
+ table_.clear ();
+ column_.clear ();
+ rowid_ = 0;
+ }
+
+ private:
+#ifndef ODB_COMPILER
+ friend struct default_value_traits<blob, id_blob_stream>;
+#endif
+ std::size_t size_;
+ mutable std::string db_;
+ mutable std::string table_;
+ mutable std::string column_;
+ mutable long long rowid_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_BLOB_HXX
diff --git a/libodb-sqlite/odb/sqlite/buildfile b/libodb-sqlite/odb/sqlite/buildfile
new file mode 100644
index 0000000..d2da43f
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/buildfile
@@ -0,0 +1,155 @@
+# file : odb/sqlite/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+define cli: file
+cli{*}: extension = cli
+
+import int_libs = libodb%lib{odb}
+import int_libs += libsqlite3%lib{sqlite3}
+
+lib{odb-sqlite}: {hxx ixx txx cxx}{* -version-build2} {hxx}{version-build2} \
+ details/{hxx ixx txx cxx}{* -options} \
+ details/build2/{h}{*} \
+ $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_SQLITE_BUILD2
+
+obja{*}: cxx.poptions += -DLIBODB_SQLITE_STATIC_BUILD
+objs{*}: cxx.poptions += -DLIBODB_SQLITE_SHARED_BUILD
+
+# Export options.
+#
+lib{odb-sqlite}:
+{
+ cxx.export.poptions = "-I$out_root" "-I$src_root" -DLIBODB_SQLITE_BUILD2
+ cxx.export.libs = $int_libs
+}
+
+liba{odb-sqlite}: cxx.export.poptions += -DLIBODB_SQLITE_STATIC
+libs{odb-sqlite}: cxx.export.poptions += -DLIBODB_SQLITE_SHARED
+
+# For pre-releases use the complete version to make sure they cannot be used
+# in place of another pre-release or the final version. See the version module
+# for details on the version.* variable values.
+#
+if $version.pre_release
+ lib{odb-sqlite}: bin.lib.version = @"-$version.project_id"
+else
+ lib{odb-sqlite}: bin.lib.version = @"-$version.major.$version.minor"
+
+develop = $config.libodb_sqlite.develop
+
+## Consumption build ($develop == false).
+#
+
+# Use pregenerated versions in the consumption build.
+#
+lib{odb-sqlite}: details/pregenerated/{hxx ixx cxx}{**}: include = (!$develop)
+
+if! $develop
+ cxx.poptions =+ "-I($src_base/details/pregenerated)" # Note: must come first.
+
+# Don't install pregenerated headers since they are only used internally in
+# the database implementation (also below).
+#
+details/pregenerated/{hxx ixx}{*}: install = false
+
+# Distribute pregenerated versions only in the consumption build.
+#
+details/pregenerated/{hxx ixx cxx}{*}: dist = (!$develop)
+
+#
+##
+
+## Development build ($develop == true).
+#
+
+lib{odb-sqlite}: details/{hxx ixx cxx}{options}: include = $develop
+
+if $develop
+ import! [metadata] cli = cli%exe{cli}
+
+# In the development build distribute regenerated {hxx ixx cxx}{options},
+# remapping their locations to the paths of the pregenerated versions (which
+# are only distributed in the consumption build; see above). This way we make
+# sure that the distributed files are always up-to-date.
+#
+<details/{hxx ixx cxx}{options}>: details/cli{options} $cli
+{
+ install = false
+ dist = ($develop ? details/pregenerated/odb/sqlite/details/ : false)
+
+ # Symlink the generated code in src for convenience of development.
+ #
+ backlink = true
+}
+%
+if $develop
+{{
+ options = --include-with-brackets --include-prefix odb/sqlite/details \
+ --guard-prefix LIBODB_SQLITE_DETAILS --generate-file-scanner \
+ --cli-namespace odb::sqlite::details::cli --long-usage \
+ --no-combined-flags
+
+ $cli $options -o $out_base/details/ $path($<[0])
+
+ # If the result differs from the pregenerated version, copy it over.
+ #
+ d = [dir_path] $src_base/details/pregenerated/odb/sqlite/details/
+
+ if diff $d/options.hxx $path($>[0]) >- && \
+ diff $d/options.ixx $path($>[1]) >- && \
+ diff $d/options.cxx $path($>[2]) >-
+ exit
+ end
+
+ cp $path($>[0]) $d/options.hxx
+ cp $path($>[1]) $d/options.ixx
+ cp $path($>[2]) $d/options.cxx
+}}
+
+# Install into the odb/sqlite/ subdirectory of, say, /usr/include/
+# recreating subdirectories.
+#
+install_include = [dir_path] include/odb/sqlite/
+
+{hxx ixx txx}{*}:
+{
+ install = $install_include
+ install.subdirs = true
+}
+
+# We want these to be picked up whether LIBODB_SQLITE_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-sqlite/odb/sqlite/connection-factory.cxx b/libodb-sqlite/odb/sqlite/connection-factory.cxx
new file mode 100644
index 0000000..794c6dd
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/connection-factory.cxx
@@ -0,0 +1,417 @@
+// file : odb/sqlite/connection-factory.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/details/lock.hxx>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/connection-factory.hxx>
+
+#include <odb/sqlite/details/config.hxx> // LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+
+using namespace std;
+
+namespace odb
+{
+ using namespace details;
+
+ namespace sqlite
+ {
+ //
+ // serial_connection_factory
+ //
+
+ serial_connection_factory::
+ ~serial_connection_factory ()
+ {
+ // We should hold the last reference to the connection.
+ //
+ if (connection_ != 0)
+ assert (connection_.count () == 1);
+ }
+
+ connection_ptr serial_connection_factory::
+ create ()
+ {
+ return connection_ptr (new (shared) connection (*this));
+ }
+
+ connection_ptr serial_connection_factory::
+ connect ()
+ {
+ return connection_;
+ }
+
+ void serial_connection_factory::
+ database (database_type& db)
+ {
+ connection_factory::database (db);
+
+ if (!connection_)
+ connection_ = create ();
+ }
+
+ //
+ // single_connection_factory
+ //
+
+ single_connection_factory::
+ ~single_connection_factory ()
+ {
+ // If the connection is currently in use, wait for it to return to
+ // the factory.
+ //
+ lock l (mutex_);
+ }
+
+ single_connection_factory::single_connection_ptr
+ single_connection_factory::
+ create ()
+ {
+ return single_connection_ptr (new (shared) single_connection (*this));
+ }
+
+ connection_ptr single_connection_factory::
+ connect ()
+ {
+ mutex_.lock ();
+ connection_->callback_ = &connection_->cb_;
+ connection_ptr r (connection_);
+ connection_.reset ();
+ return r;
+ }
+
+ void single_connection_factory::
+ database (database_type& db)
+ {
+ connection_factory::database (db);
+
+ if (!connection_)
+ connection_ = create ();
+ }
+
+ bool single_connection_factory::
+ release (single_connection* c)
+ {
+ c->callback_ = 0;
+ connection_.reset (inc_ref (c));
+ connection_->recycle ();
+ mutex_.unlock ();
+ return false;
+ }
+
+ //
+ // single_connection_factory::single_connection
+ //
+
+ single_connection_factory::single_connection::
+ single_connection (single_connection_factory& f, int extra_flags)
+ : connection (f, extra_flags)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ single_connection_factory::single_connection::
+ single_connection (single_connection_factory& f, sqlite3* handle)
+ : connection (f, handle)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ bool single_connection_factory::single_connection::
+ zero_counter (void* arg)
+ {
+ single_connection* c (static_cast<single_connection*> (arg));
+ return static_cast<single_connection_factory&> (c->factory_).release (c);
+ }
+
+ //
+ // new_connection_factory
+ //
+
+ connection_ptr new_connection_factory::
+ connect ()
+ {
+ return connection_ptr (new (shared) connection (*this, extra_flags_));
+ }
+
+ void new_connection_factory::
+ database (database_type& db)
+ {
+ bool first (db_ == 0);
+
+ connection_factory::database (db);
+
+ if (!first)
+ return;
+
+ // Unless explicitly disabled, enable shared cache.
+ //
+#if SQLITE_VERSION_NUMBER >= 3006018 && defined(LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY)
+ if ((db_->flags () & SQLITE_OPEN_PRIVATECACHE) == 0)
+ extra_flags_ |= SQLITE_OPEN_SHAREDCACHE;
+#endif
+ }
+
+ //
+ // connection_pool_factory
+ //
+
+ connection_pool_factory::pooled_connection_ptr connection_pool_factory::
+ create ()
+ {
+ return pooled_connection_ptr (
+ new (shared) pooled_connection (*this, extra_flags_));
+ }
+
+ connection_pool_factory::
+ ~connection_pool_factory ()
+ {
+ // Wait for all the connections currently in use to return to the pool.
+ //
+ lock l (mutex_);
+ while (in_use_ != 0)
+ {
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ connection_ptr connection_pool_factory::
+ connect ()
+ {
+ lock l (mutex_);
+
+ while (true)
+ {
+ // See if we have a spare connection.
+ //
+ if (connections_.size () != 0)
+ {
+ shared_ptr<pooled_connection> c (connections_.back ());
+ connections_.pop_back ();
+
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // See if we can create a new one.
+ //
+ if(max_ == 0 || in_use_ < max_)
+ {
+ shared_ptr<pooled_connection> c (create ());
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // Wait until someone releases a connection.
+ //
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ void connection_pool_factory::
+ database (database_type& db)
+ {
+ bool first (db_ == 0);
+
+ connection_factory::database (db);
+
+ if (!first)
+ return;
+
+ // Unless explicitly disabled, enable shared cache.
+ //
+#if SQLITE_VERSION_NUMBER >= 3006018 && defined(LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY)
+ if ((db_->flags () & SQLITE_OPEN_PRIVATECACHE) == 0)
+ extra_flags_ |= SQLITE_OPEN_SHAREDCACHE;
+#endif
+
+ if (min_ > 0)
+ {
+ connections_.reserve (min_);
+
+ for(size_t i (0); i < min_; ++i)
+ connections_.push_back (create ());
+ }
+ }
+
+ bool connection_pool_factory::
+ release (pooled_connection* c)
+ {
+ c->callback_ = 0;
+
+ lock l (mutex_);
+
+ // Determine if we need to keep or free this connection.
+ //
+ bool keep (waiters_ != 0 ||
+ min_ == 0 ||
+ (connections_.size () + in_use_ <= min_));
+
+ in_use_--;
+
+ if (keep)
+ {
+ connections_.push_back (pooled_connection_ptr (inc_ref (c)));
+ connections_.back ()->recycle ();
+ }
+
+ if (waiters_ != 0)
+ cond_.signal ();
+
+ return !keep;
+ }
+
+ //
+ // connection_pool_factory::pooled_connection
+ //
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f, int extra_flags)
+ : connection (f, extra_flags)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f, sqlite3* handle)
+ : connection (f, handle)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ bool connection_pool_factory::pooled_connection::
+ zero_counter (void* arg)
+ {
+ pooled_connection* c (static_cast<pooled_connection*> (arg));
+ return static_cast<connection_pool_factory&> (c->factory_).release (c);
+ }
+
+ //
+ // default_attached_connection_factory
+ //
+
+ void default_attached_connection_factory::
+ detach ()
+ {
+ // Note that this function may be called several times, for example, in
+ // case of detach_database() failure.
+ //
+ if (attached_connection_ != 0)
+ {
+ // We should hold the last reference to the attached connection.
+ //
+ assert (attached_connection_.count () == 1);
+
+ // While it may seem like a good idea to also invalidate query results
+ // and reset active statements, if any such result/statement is still
+ // alive, then there would be bigger problems since it would have a
+ // dangling reference to the connection. In a way, that's the same
+ // reason we don't do it in the connection destructor.
+
+ // Remove ourselves from the active object list of the main
+ // connection.
+ //
+ if (next_ != this) // Might have already been done.
+ list_remove ();
+
+ const string& s (database ().schema ());
+
+ if (s != "main" && s != "temp")
+ main_factory ().detach_database (main_connection_, s);
+
+ // Explicitly free the attached connection so that we don't try to
+ // redo this.
+ //
+ attached_connection_.reset ();
+ }
+ }
+
+ default_attached_connection_factory::
+ ~default_attached_connection_factory ()
+ {
+ if (attached_connection_ != 0)
+ {
+ // This can throw. Ignoring the failure to detach seems like the most
+ // sensible thing to do here.
+ //
+ try{ detach (); } catch (...) {}
+ }
+ }
+
+ connection_ptr default_attached_connection_factory::
+ connect ()
+ {
+ return attached_connection_;
+ }
+
+ void default_attached_connection_factory::
+ database (database_type& db)
+ {
+ attached_connection_factory::database (db);
+
+ if (!attached_connection_)
+ {
+ const string& s (db.schema ());
+
+ if (s != "main" && s != "temp")
+ main_factory ().attach_database (main_connection_, db.name (), s);
+
+ attached_connection_.reset (
+ new (shared) connection (*this,
+ s != "main" ? &translate_statement : 0));
+
+ // Add ourselves to the active object list of the main connection.
+ //
+ list_add ();
+ }
+ }
+
+ void default_attached_connection_factory::
+ clear ()
+ {
+ attached_connection_->clear ();
+ }
+
+ void default_attached_connection_factory::
+ translate_statement (string& r,
+ const char* text,
+ size_t text_size,
+ connection& conn)
+ {
+ r.assign (text, text_size);
+
+ // Things will fall apart if any of the statements we translate use
+ // "main" as a table alias. So we have this crude check even though it
+ // means we cannot use "main" for other aliases (e.g., column).
+ //
+ assert (r.find ("AS \"main\"") == string::npos);
+
+ const string& s (conn.database ().schema ());
+ for (size_t p (0); (p = r.find ("\"main\".", p, 7)) != string::npos; )
+ {
+ // Verify the preceding character.
+ //
+ if (p != 0 && r[p - 1] == '.')
+ {
+ p += 7;
+ continue;
+ }
+
+ r.replace (p + 1, 4, s);
+ p += s.size () + 3;
+ }
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/connection-factory.hxx b/libodb-sqlite/odb/sqlite/connection-factory.hxx
new file mode 100644
index 0000000..b410997
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/connection-factory.hxx
@@ -0,0 +1,273 @@
+// file : odb/sqlite/connection-factory.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_CONNECTION_FACTORY_HXX
+#define ODB_SQLITE_CONNECTION_FACTORY_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/details/mutex.hxx>
+#include <odb/details/condition.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // Share a single connection in a guaranteed serial database access.
+ //
+ // For example, a single-threaded application that executes all the
+ // operations via the database instance without messing with multiple
+ // connections/transactions would qualify.
+ //
+ class LIBODB_SQLITE_EXPORT serial_connection_factory:
+ public connection_factory
+ {
+ public:
+ serial_connection_factory () {}
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ database (database_type&);
+
+ virtual
+ ~serial_connection_factory ();
+
+ private:
+ serial_connection_factory (const serial_connection_factory&);
+ serial_connection_factory& operator= (const serial_connection_factory&);
+
+ protected:
+ // This function is called when the factory needs to create the
+ // connection.
+ //
+ virtual connection_ptr
+ create ();
+
+ connection_ptr connection_;
+ };
+
+ // Share a single connection potentially between multiple threads.
+ //
+ class LIBODB_SQLITE_EXPORT single_connection_factory:
+ public connection_factory
+ {
+ public:
+ single_connection_factory () {}
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ database (database_type&);
+
+ virtual
+ ~single_connection_factory ();
+
+ private:
+ single_connection_factory (const single_connection_factory&);
+ single_connection_factory& operator= (const single_connection_factory&);
+
+ protected:
+ class LIBODB_SQLITE_EXPORT single_connection: public connection
+ {
+ public:
+ single_connection (single_connection_factory&, int extra_flags = 0);
+ single_connection (single_connection_factory&, sqlite3*);
+
+ private:
+ static bool
+ zero_counter (void*);
+
+ private:
+ friend class single_connection_factory;
+ shared_base::refcount_callback cb_;
+ };
+
+ friend class single_connection;
+
+ typedef details::shared_ptr<single_connection> single_connection_ptr;
+
+ // This function is called when the factory needs to create the
+ // connection.
+ //
+ virtual single_connection_ptr
+ create ();
+
+ protected:
+ // Return true if the connection should be deleted, false otherwise.
+ //
+ bool
+ release (single_connection*);
+
+ protected:
+ details::mutex mutex_;
+ single_connection_ptr connection_;
+ };
+
+ // Create a new connection every time one is requested.
+ //
+ class LIBODB_SQLITE_EXPORT new_connection_factory:
+ public connection_factory
+ {
+ public:
+ new_connection_factory (): extra_flags_ (0) {}
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ database (database_type&);
+
+ private:
+ new_connection_factory (const new_connection_factory&);
+ new_connection_factory& operator= (const new_connection_factory&);
+
+ private:
+ int extra_flags_;
+ };
+
+ // Pool a number of connections.
+ //
+ class LIBODB_SQLITE_EXPORT connection_pool_factory:
+ public connection_factory
+ {
+ public:
+ // The max_connections argument specifies the maximum number of
+ // concurrent connections this pool will maintain. If this value
+ // is 0 then the pool will create a new connection every time all
+ // of the existing connections are in use.
+ //
+ // The min_connections argument specifies the minimum number of
+ // connections that should be maintained by the pool. If the
+ // number of connections maintained by the pool exceeds this
+ // number and there are no active waiters for a new connection,
+ // then the pool will release the excess connections. If this
+ // value is 0 then the pool will maintain all the connections
+ // that were ever created.
+ //
+ // The ping argument specifies whether to ping the connection to
+ // make sure it is still alive before returning it to the caller.
+ //
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0)
+ : max_ (max_connections),
+ min_ (min_connections),
+ extra_flags_ (0),
+ in_use_ (0),
+ waiters_ (0),
+ cond_ (mutex_)
+ {
+ // max_connections == 0 means unlimited.
+ //
+ assert (max_connections == 0 || max_connections >= min_connections);
+ }
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ database (database_type&);
+
+ virtual
+ ~connection_pool_factory ();
+
+ private:
+ connection_pool_factory (const connection_pool_factory&);
+ connection_pool_factory& operator= (const connection_pool_factory&);
+
+ protected:
+ class LIBODB_SQLITE_EXPORT pooled_connection: public connection
+ {
+ public:
+ pooled_connection (connection_pool_factory&, int extra_flags = 0);
+ pooled_connection (connection_pool_factory&, sqlite3*);
+
+ private:
+ static bool
+ zero_counter (void*);
+
+ private:
+ friend class connection_pool_factory;
+ shared_base::refcount_callback cb_;
+ };
+
+ friend class pooled_connection;
+
+ typedef details::shared_ptr<pooled_connection> pooled_connection_ptr;
+ typedef std::vector<pooled_connection_ptr> connections;
+
+ // This function is called whenever the pool needs to create a new
+ // connection.
+ //
+ virtual pooled_connection_ptr
+ create ();
+
+ protected:
+ // Return true if the connection should be deleted, false otherwise.
+ //
+ bool
+ release (pooled_connection*);
+
+ protected:
+ const std::size_t max_;
+ const std::size_t min_;
+ int extra_flags_;
+
+ std::size_t in_use_; // Number of connections currently in use.
+ std::size_t waiters_; // Number of threads waiting for a connection.
+
+ connections connections_;
+
+ details::mutex mutex_;
+ details::condition cond_;
+ };
+
+ class LIBODB_SQLITE_EXPORT default_attached_connection_factory:
+ public attached_connection_factory
+ {
+ public:
+ explicit
+ default_attached_connection_factory (const connection_ptr& main)
+ : attached_connection_factory (main) {}
+
+ using attached_connection_factory::database; // Accessor.
+
+ virtual void
+ database (database_type&);
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ detach ();
+
+ // Active object interface.
+ //
+ virtual void
+ clear ();
+
+ virtual
+ ~default_attached_connection_factory ();
+
+ protected:
+ static void
+ translate_statement (std::string&, const char*, std::size_t, connection&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_CONNECTION_FACTORY_HXX
diff --git a/libodb-sqlite/odb/sqlite/connection.cxx b/libodb-sqlite/odb/sqlite/connection.cxx
new file mode 100644
index 0000000..0445163
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/connection.cxx
@@ -0,0 +1,310 @@
+// file : odb/sqlite/connection.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <new> // std::bad_alloc
+#include <string>
+#include <cassert>
+
+#include <odb/details/lock.hxx>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/transaction.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/statement-cache.hxx>
+#include <odb/sqlite/prepared-query.hxx>
+#include <odb/sqlite/error.hxx>
+#include <odb/sqlite/exceptions.hxx> // deadlock
+
+#include <odb/sqlite/details/config.hxx> // LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+
+using namespace std;
+
+extern "C" void
+odb_sqlite_connection_unlock_callback (void**, int);
+
+namespace odb
+{
+ using namespace details;
+
+ namespace sqlite
+ {
+ connection::
+ connection (connection_factory& cf,
+ int extra_flags,
+ statement_translator* st)
+ : odb::connection (cf),
+ statement_translator_ (st),
+ unlock_cond_ (unlock_mutex_),
+ active_objects_ (0)
+ {
+ database_type& db (database ());
+
+ int f (db.flags () | extra_flags);
+ const string& n (db.name ());
+
+ // If we are opening a temporary database, then add the create flag.
+ //
+ if (n.empty () || n == ":memory:")
+ f |= SQLITE_OPEN_CREATE;
+
+ // A connection can only be used by a single thread at a time. So
+ // disable locking in SQLite unless explicitly requested.
+ //
+#if defined(SQLITE_OPEN_NOMUTEX)
+ if ((f & SQLITE_OPEN_FULLMUTEX) == 0)
+ f |= SQLITE_OPEN_NOMUTEX;
+#endif
+
+ sqlite3* h (0);
+
+ // sqlite3_open_v2() was only addedin SQLite 3.5.0.
+ //
+#if SQLITE_VERSION_NUMBER >= 3005000
+ const string& vfs (db.vfs ());
+ int e (
+ sqlite3_open_v2 (
+ n.c_str (), &h, f, (vfs.empty () ? 0 : vfs.c_str ())));
+#else
+ // Readonly opening not supported in SQLite earlier than 3.5.0.
+ //
+ assert ((f & SQLITE_OPEN_READONLY) == 0);
+ int e (sqlite3_open (n.c_str (), &h));
+#endif
+
+ handle_.reset (h);
+
+ if (e != SQLITE_OK)
+ {
+ if (handle_ == 0)
+ throw bad_alloc ();
+
+ translate_error (e, *this);
+ }
+
+ init ();
+ }
+
+ connection::
+ connection (connection_factory& cf,
+ sqlite3* handle,
+ statement_translator* st)
+ : odb::connection (cf),
+ handle_ (handle),
+ statement_translator_ (st),
+ unlock_cond_ (unlock_mutex_),
+ active_objects_ (0)
+ {
+ init ();
+ }
+
+ void connection::
+ init ()
+ {
+ database_type& db (database ());
+
+ // Enable/disable foreign key constraints.
+ //
+ generic_statement st (
+ *this,
+ db.foreign_keys ()
+ ? "PRAGMA foreign_keys=ON"
+ : "PRAGMA foreign_keys=OFF",
+ db.foreign_keys () ? 22 : 23);
+ st.execute ();
+
+ // String lengths include '\0', as per the SQLite manual suggestion.
+ //
+ begin_.reset (new (shared) generic_statement (*this, "BEGIN", 6));
+ commit_.reset (new (shared) generic_statement (*this, "COMMIT", 7));
+ rollback_.reset (new (shared) generic_statement (*this, "ROLLBACK", 9));
+
+ // Create statement cache.
+ //
+ statement_cache_.reset (new statement_cache_type (*this));
+ }
+
+ connection::
+ connection (attached_connection_factory& cf, statement_translator* st)
+ : odb::connection (cf),
+ handle_ (0),
+ statement_translator_ (st),
+ unlock_cond_ (unlock_mutex_),
+ active_objects_ (0)
+ {
+ // Copy some things over from the main connection.
+ //
+ connection& main (*cf.main_connection_);
+
+ tracer_ = main.tracer_;
+
+ // Create statement cache.
+ //
+ statement_cache_.reset (new statement_cache_type (*this));
+ }
+
+ connection::
+ ~connection ()
+ {
+ // Destroy prepared query statements before freeing the connections.
+ //
+ recycle ();
+ clear_prepared_map ();
+ }
+
+ generic_statement& connection::
+ begin_statement ()
+ {
+ return static_cast<generic_statement&> (*begin_);
+ }
+
+ generic_statement& connection::
+ begin_immediate_statement ()
+ {
+ if (!begin_immediate_)
+ begin_immediate_.reset (
+ new (shared) generic_statement (*this, "BEGIN IMMEDIATE", 16));
+
+ return static_cast<generic_statement&> (*begin_immediate_);
+ }
+
+ generic_statement& connection::
+ begin_exclusive_statement ()
+ {
+ if (!begin_exclusive_)
+ begin_exclusive_.reset (
+ new (shared) generic_statement (*this, "BEGIN EXCLUSIVE", 16));
+
+ return static_cast<generic_statement&> (*begin_exclusive_);
+ }
+
+ generic_statement& connection::
+ commit_statement ()
+ {
+ return static_cast<generic_statement&> (*commit_);
+ }
+
+ generic_statement& connection::
+ rollback_statement ()
+ {
+ return static_cast<generic_statement&> (*rollback_);
+ }
+
+ transaction_impl* connection::
+ begin ()
+ {
+ return new transaction_impl (
+ connection_ptr (inc_ref (this)), transaction_impl::deferred);
+ }
+
+ transaction_impl* connection::
+ begin_immediate ()
+ {
+ return new transaction_impl (
+ connection_ptr (inc_ref (this)), transaction_impl::immediate);
+ }
+
+ transaction_impl* connection::
+ begin_exclusive ()
+ {
+ return new transaction_impl (
+ connection_ptr (inc_ref (this)), transaction_impl::exclusive);
+ }
+
+ unsigned long long connection::
+ execute (const char* s, std::size_t n)
+ {
+ generic_statement st (*this, s, n);
+ return st.execute ();
+ }
+
+ inline void
+ connection_unlock_callback (void** args, int n)
+ {
+ for (int i (0); i < n; ++i)
+ {
+ connection* c (static_cast<connection*> (args[i]));
+ details::lock l (c->unlock_mutex_);
+ c->unlocked_ = true;
+ c->unlock_cond_.signal ();
+ }
+ }
+
+ void connection::
+ wait ()
+ {
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ unlocked_ = false;
+
+ // unlock_notify() returns SQLITE_OK or SQLITE_LOCKED (deadlock).
+ //
+ int e (sqlite3_unlock_notify (handle (),
+ &odb_sqlite_connection_unlock_callback,
+ this));
+ if (e == SQLITE_LOCKED)
+ throw deadlock ();
+
+ details::lock l (unlock_mutex_);
+
+ while (!unlocked_)
+ unlock_cond_.wait (l);
+#else
+ translate_error (SQLITE_LOCKED, *this);
+#endif
+ }
+
+ void connection::
+ clear ()
+ {
+ invalidate_results ();
+
+ // The current first active_object may remove itself from the list and
+ // make the second object (if any) the new first.
+ //
+ for (active_object** pp (&active_objects_); *pp != 0; )
+ {
+ active_object* p (*pp);
+ p->clear ();
+
+ // Move to the next object if this one decided to stay on the list.
+ //
+ if (*pp == p)
+ pp = &p->next_;
+ }
+ }
+
+ // connection_factory
+ //
+ connection_factory::
+ ~connection_factory ()
+ {
+ }
+
+ void connection_factory::
+ database (database_type& db)
+ {
+ odb::connection_factory::db_ = &db;
+ db_ = &db;
+ }
+
+ void connection_factory::
+ attach_database (const connection_ptr& conn,
+ const std::string& name,
+ const std::string& schema)
+ {
+ conn->execute ("ATTACH DATABASE '" + name + "' AS \"" + schema + '"');
+ }
+
+ void connection_factory::
+ detach_database (const connection_ptr& conn, const std::string& schema)
+ {
+ conn->execute ("DETACH DATABASE \"" + schema + '"');
+ }
+ }
+}
+
+extern "C" void
+odb_sqlite_connection_unlock_callback (void** args, int n)
+{
+ odb::sqlite::connection_unlock_callback (args, n);
+}
diff --git a/libodb-sqlite/odb/sqlite/connection.hxx b/libodb-sqlite/odb/sqlite/connection.hxx
new file mode 100644
index 0000000..dbe4494
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/connection.hxx
@@ -0,0 +1,356 @@
+// file : odb/sqlite/connection.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_CONNECTION_HXX
+#define ODB_SQLITE_CONNECTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <sqlite3.h>
+
+#include <odb/statement.hxx>
+#include <odb/connection.hxx>
+
+#include <odb/details/mutex.hxx>
+#include <odb/details/condition.hxx>
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/unique-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/query.hxx>
+#include <odb/sqlite/tracer.hxx>
+#include <odb/sqlite/transaction-impl.hxx>
+#include <odb/sqlite/auto-handle.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class statement_cache;
+ class generic_statement;
+ class connection_factory;
+ class attached_connection_factory;
+
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+
+ // SQLite "active object", i.e., an object that needs to be
+ // "cleared" before the transaction can be committed and the
+ // connection released. These form a doubly-linked list.
+ //
+ class LIBODB_SQLITE_EXPORT active_object
+ {
+ public:
+ // This function may remove the object from the list since it may no
+ // longer be "active".
+ //
+ virtual void
+ clear () = 0;
+
+ protected:
+ active_object (connection& c): prev_ (0), next_ (this), conn_ (c) {}
+
+ void
+ list_add ();
+
+ void
+ list_remove ();
+
+ protected:
+ friend class connection;
+
+ // prev_ == 0 means we are the first element.
+ // next_ == 0 means we are the last element.
+ // next_ == this means we are not on the list (prev_ should be 0).
+ //
+ active_object* prev_;
+ active_object* next_;
+
+ connection& conn_;
+ };
+
+ class LIBODB_SQLITE_EXPORT connection: public odb::connection
+ {
+ public:
+ typedef sqlite::statement_cache statement_cache_type;
+ typedef sqlite::database database_type;
+
+ // Translate the database schema in the statement text (used to
+ // implement attached databases). If the result is empty, then no
+ // translation is required and the original text should be used as is.
+ //
+ typedef void (statement_translator) (std::string& result,
+ const char* text,
+ std::size_t text_size,
+ connection&);
+ virtual
+ ~connection ();
+
+ connection (connection_factory&,
+ int extra_flags = 0,
+ statement_translator* = 0);
+
+ connection (connection_factory&,
+ sqlite3* handle,
+ statement_translator* = 0);
+
+ // Create an attached connection (see the attached database constructor
+ // for details).
+ //
+ connection (attached_connection_factory&, statement_translator*);
+
+ database_type&
+ database ();
+
+ // Return the main connection of an attached connection. If this
+ // connection is main, return itself.
+ //
+ connection&
+ main_connection ();
+
+ static connection_ptr
+ main_connection (const connection_ptr&);
+
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ transaction_impl*
+ begin_immediate ();
+
+ transaction_impl*
+ begin_exclusive ();
+
+ public:
+ using odb::connection::execute;
+
+ virtual unsigned long long
+ execute (const char* statement, std::size_t length);
+
+ // Query preparation.
+ //
+ public:
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const sqlite::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef sqlite::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ using odb::connection::tracer;
+
+ public:
+ sqlite3*
+ handle ();
+
+ statement_cache_type&
+ statement_cache ()
+ {
+ return *statement_cache_;
+ }
+
+ // Wait for the locks to be released via unlock notification. Can
+ // be called after getting SQLITE_LOCKED_SHAREDCACHE.
+ //
+ void
+ wait ();
+
+ public:
+ // Reset active statements. Also invalidates query results by first
+ // calling invalidate_results().
+ //
+ void
+ clear ();
+
+ public:
+ // Note: only available on main connection.
+ //
+ generic_statement&
+ begin_statement ();
+
+ generic_statement&
+ begin_immediate_statement ();
+
+ generic_statement&
+ begin_exclusive_statement ();
+
+ generic_statement&
+ commit_statement ();
+
+ generic_statement&
+ rollback_statement ();
+
+ protected:
+ friend class attached_connection_factory;
+
+ connection_factory&
+ factory ();
+
+ private:
+ connection (const connection&);
+ connection& operator= (const connection&);
+
+ private:
+ void
+ init ();
+
+ private:
+ // Note that we use NULL handle as an indication of an attached
+ // connection.
+ //
+ auto_handle<sqlite3> handle_;
+
+ statement_translator* statement_translator_;
+
+ // Keep statement_cache_ after handle_ so that it is destroyed before
+ // the connection is closed.
+ //
+ details::unique_ptr<statement_cache_type> statement_cache_;
+
+ // Note: using odb::statement in order to break the connection-statement
+ // dependency cycle.
+ //
+ details::shared_ptr<odb::statement> begin_;
+ details::shared_ptr<odb::statement> begin_immediate_;
+ details::shared_ptr<odb::statement> begin_exclusive_;
+ details::shared_ptr<odb::statement> commit_;
+ details::shared_ptr<odb::statement> rollback_;
+
+ // Unlock notification machinery.
+ //
+ private:
+ bool unlocked_;
+ details::mutex unlock_mutex_;
+ details::condition unlock_cond_;
+
+ friend void
+ connection_unlock_callback (void**, int);
+
+ private:
+ friend class statement; // statement_translator_
+ friend class transaction_impl; // invalidate_results()
+
+ // Linked list of active objects currently associated
+ // with this connection.
+ //
+ private:
+ friend class active_object;
+ active_object* active_objects_;
+ };
+
+ class LIBODB_SQLITE_EXPORT connection_factory:
+ public odb::connection_factory
+ {
+ public:
+ typedef sqlite::database database_type;
+
+ virtual void
+ database (database_type&);
+
+ database_type&
+ database () {return *db_;}
+
+ virtual connection_ptr
+ connect () = 0;
+
+ virtual
+ ~connection_factory ();
+
+ connection_factory (): db_ (0) {}
+
+ // Attach/detach additional databases. Connection is one of the main
+ // connections created by this factory. Note: not called for "main" and
+ // "temp" schemas.
+ //
+ // The default implementations simply execute the ATTACH DATABASE and
+ // DETACH DATABASE SQLite statements.
+ //
+ virtual void
+ attach_database (const connection_ptr&,
+ const std::string& name,
+ const std::string& schema);
+
+ virtual void
+ detach_database (const connection_ptr&, const std::string& schema);
+
+ // Needed to break the circular connection_factory-database dependency
+ // (odb::connection_factory has the odb::database member).
+ //
+ protected:
+ database_type* db_;
+ };
+
+ // The call to database() should cause ATTACH DATABASE (or otherwise make
+ // sure the database is attached). Destruction of the factory should cause
+ // DETACH DATABASE (or otherwise notice that this factory no longer needs
+ // the database attached).
+ //
+ // Note that attached_connection_factory is an active object that
+ // registers itself with the main connection in order to get notified on
+ // transaction finalization.
+ //
+ class LIBODB_SQLITE_EXPORT attached_connection_factory:
+ public connection_factory,
+ public active_object
+ {
+ public:
+ explicit
+ attached_connection_factory (const connection_ptr& main)
+ : active_object (*main), main_connection_ (main) {}
+
+ virtual void
+ detach () = 0;
+
+ protected:
+ friend class database;
+ friend class connection;
+ friend class transaction_impl;
+
+ connection_factory&
+ main_factory ();
+
+ // Note that this essentially establishes a "framework" for all the
+ // attached connection factory implementations: they hold a counted
+ // reference to the main connection and they maintain a single shared
+ // attached connection.
+ //
+ connection_ptr main_connection_;
+ connection_ptr attached_connection_;
+ };
+ }
+}
+
+#include <odb/sqlite/connection.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_CONNECTION_HXX
diff --git a/libodb-sqlite/odb/sqlite/connection.ixx b/libodb-sqlite/odb/sqlite/connection.ixx
new file mode 100644
index 0000000..094fd52
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/connection.ixx
@@ -0,0 +1,108 @@
+// file : odb/sqlite/connection.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // active_object
+ //
+ inline void active_object::
+ list_add ()
+ {
+ next_ = conn_.active_objects_;
+ conn_.active_objects_ = this;
+
+ if (next_ != 0)
+ next_->prev_ = this;
+ }
+
+ inline void active_object::
+ list_remove ()
+ {
+ (prev_ == 0 ? conn_.active_objects_ : prev_->next_) = next_;
+
+ if (next_ != 0)
+ next_->prev_ = prev_;
+
+ prev_ = 0;
+ next_ = this;
+ }
+
+ // connection
+ //
+ inline database& connection::
+ database ()
+ {
+ return static_cast<connection_factory&> (factory_).database ();
+ }
+
+ inline connection& connection::
+ main_connection ()
+ {
+ return handle_ != 0
+ ? *this
+ : *static_cast<attached_connection_factory&> (factory_).main_connection_;
+ }
+
+ inline connection_ptr connection::
+ main_connection (const connection_ptr& c)
+ {
+ return c->handle_ != 0
+ ? c
+ : static_cast<attached_connection_factory&> (c->factory_).main_connection_;
+ }
+
+ inline sqlite3* connection::
+ handle ()
+ {
+ return handle_ != 0
+ ? handle_
+ : static_cast<attached_connection_factory&> (factory_).main_connection_->handle_;
+ }
+
+ inline connection_factory& connection::
+ factory ()
+ {
+ return static_cast<connection_factory&> (factory_);
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const sqlite::query_base& q)
+ {
+ return query_<T, id_sqlite>::call (*this, n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, sqlite::query_base (q));
+ }
+
+ // attached_connection_factory
+ //
+ inline connection_factory& attached_connection_factory::
+ main_factory ()
+ {
+ return main_connection_->factory ();
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/container-statements.hxx b/libodb-sqlite/odb/sqlite/container-statements.hxx
new file mode 100644
index 0000000..b9cccf0
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/container-statements.hxx
@@ -0,0 +1,355 @@
+// file : odb/sqlite/container-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_CONTAINER_STATEMENTS_HXX
+#define ODB_SQLITE_CONTAINER_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/binding.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class connection;
+
+ // Template argument is the generated abstract container traits type.
+ // That is, it doesn't need to provide column counts and statements.
+ //
+ template <typename T>
+ class container_statements
+ {
+ public:
+ typedef T traits;
+
+ typedef typename traits::data_image_type data_image_type;
+ typedef typename traits::functions_type functions_type;
+
+ typedef sqlite::insert_statement insert_statement_type;
+ typedef sqlite::select_statement select_statement_type;
+ typedef sqlite::delete_statement delete_statement_type;
+
+ typedef sqlite::connection connection_type;
+
+ container_statements (connection_type&, binding& id_binding);
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Functions.
+ //
+ functions_type&
+ functions ()
+ {
+ return functions_;
+ }
+
+ // Schema version.
+ //
+ const schema_version_migration&
+ version_migration () const {return *svm_;}
+
+ void
+ version_migration (const schema_version_migration& svm) {svm_ = &svm;}
+
+ // Id image binding (external).
+ //
+ const binding&
+ id_binding ()
+ {
+ return id_binding_;
+ }
+
+ // Data image. The image is split into the id (that comes as a
+ // binding) and index/key plus value which are in data_image_type.
+ // The select binding is a subset of the full binding (no id).
+ //
+ data_image_type&
+ data_image ()
+ {
+ return data_image_;
+ }
+
+ bind*
+ data_bind ()
+ {
+ return insert_image_binding_.bind;
+ }
+
+ bool
+ data_binding_test_version () const
+ {
+ return data_id_binding_version_ != id_binding_.version ||
+ data_image_version_ != data_image_.version ||
+ insert_image_binding_.version == 0;
+ }
+
+ void
+ data_binding_update_version ()
+ {
+ data_id_binding_version_ = id_binding_.version;
+ data_image_version_ = data_image_.version;
+ insert_image_binding_.version++;
+ select_image_binding_.version++;
+ }
+
+ bool*
+ select_image_truncated ()
+ {
+ return select_image_truncated_;
+ }
+
+ //
+ // Statements.
+ //
+
+ insert_statement_type&
+ insert_statement ()
+ {
+ if (insert_ == 0)
+ insert_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ insert_text_,
+ versioned_, // Process if versioned.
+ insert_image_binding_,
+ 0));
+
+ return *insert_;
+ }
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ select_text_,
+ versioned_, // Process if versioned.
+ false, // Don't optimize.
+ id_binding_,
+ select_image_binding_));
+
+ return *select_;
+ }
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (delete_ == 0)
+ delete_.reset (
+ new (details::shared) delete_statement_type (
+ conn_, delete_text_, id_binding_));
+
+ return *delete_;
+ }
+
+ private:
+ container_statements (const container_statements&);
+ container_statements& operator= (const container_statements&);
+
+ protected:
+ connection_type& conn_;
+ binding& id_binding_;
+
+ functions_type functions_;
+
+ data_image_type data_image_;
+ std::size_t data_image_version_;
+ std::size_t data_id_binding_version_;
+
+ binding insert_image_binding_;
+
+ binding select_image_binding_;
+ bool* select_image_truncated_;
+
+ const char* insert_text_;
+ const char* select_text_;
+ const char* delete_text_;
+
+ bool versioned_;
+ const schema_version_migration* svm_;
+
+ details::shared_ptr<insert_statement_type> insert_;
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<delete_statement_type> delete_;
+ };
+
+ template <typename T>
+ class smart_container_statements: public container_statements<T>
+ {
+ public:
+ typedef T traits;
+ typedef typename traits::cond_image_type cond_image_type;
+
+ typedef sqlite::update_statement update_statement_type;
+ typedef sqlite::delete_statement delete_statement_type;
+
+ typedef sqlite::connection connection_type;
+
+ smart_container_statements (connection_type&, binding& id_binding);
+
+ // Condition image. The image is split into the id (that comes as
+ // a binding) and index/key/value which is in cond_image_type.
+ //
+ cond_image_type&
+ cond_image ()
+ {
+ return cond_image_;
+ }
+
+ bind*
+ cond_bind ()
+ {
+ return cond_image_binding_.bind;
+ }
+
+ bool
+ cond_binding_test_version () const
+ {
+ return cond_id_binding_version_ != this->id_binding_.version ||
+ cond_image_version_ != cond_image_.version ||
+ cond_image_binding_.version == 0;
+ }
+
+ void
+ cond_binding_update_version ()
+ {
+ cond_id_binding_version_ = this->id_binding_.version;
+ cond_image_version_ = cond_image_.version;
+ cond_image_binding_.version++;
+ }
+
+ // Update image. The image is split as follows: value comes
+ // from the data image, id comes as binding, and index/key
+ // comes from the condition image.
+ //
+ bind*
+ update_bind ()
+ {
+ return update_image_binding_.bind;
+ }
+
+ bool
+ update_binding_test_version () const
+ {
+ return update_id_binding_version_ != this->id_binding_.version ||
+ update_cond_image_version_ != cond_image_.version ||
+ update_data_image_version_ != this->data_image_.version ||
+ update_image_binding_.version == 0;
+ }
+
+ void
+ update_binding_update_version ()
+ {
+ update_id_binding_version_ = this->id_binding_.version;
+ update_cond_image_version_ = cond_image_.version;
+ update_data_image_version_ = this->data_image_.version;
+ update_image_binding_.version++;
+ }
+
+ //
+ // Statements.
+ //
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (this->delete_ == 0)
+ this->delete_.reset (
+ new (details::shared) delete_statement_type (
+ this->conn_,
+ this->delete_text_,
+ this->cond_image_binding_));
+
+ return *this->delete_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ this->conn_,
+ update_text_,
+ this->versioned_, // Process if versioned.
+ update_image_binding_));
+
+ return *update_;
+ }
+
+ protected:
+ cond_image_type cond_image_;
+ std::size_t cond_image_version_;
+ std::size_t cond_id_binding_version_;
+ binding cond_image_binding_;
+
+ std::size_t update_id_binding_version_;
+ std::size_t update_cond_image_version_;
+ std::size_t update_data_image_version_;
+ binding update_image_binding_;
+
+ const char* update_text_;
+
+ details::shared_ptr<update_statement_type> update_;
+ };
+
+ // Template argument is the generated concrete container traits type.
+ //
+ template <typename T>
+ class container_statements_impl: public T::statements_type
+ {
+ public:
+ typedef T traits;
+ typedef typename T::statements_type base;
+ typedef sqlite::connection connection_type;
+
+ container_statements_impl (connection_type&, binding&);
+
+ private:
+ container_statements_impl (const container_statements_impl&);
+ container_statements_impl& operator= (const container_statements_impl&);
+
+ private:
+ bind data_image_bind_[traits::data_column_count];
+ bool select_image_truncated_array_[traits::data_column_count -
+ traits::id_column_count];
+ };
+
+ template <typename T>
+ class smart_container_statements_impl: public container_statements_impl<T>
+ {
+ public:
+ typedef T traits;
+ typedef sqlite::connection connection_type;
+
+ smart_container_statements_impl (connection_type&, binding&);
+
+ private:
+ bind cond_image_bind_[traits::cond_column_count];
+ bind update_image_bind_[traits::value_column_count +
+ traits::cond_column_count];
+ };
+ }
+}
+
+#include <odb/sqlite/container-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_CONTAINER_STATEMENTS_HXX
diff --git a/libodb-sqlite/odb/sqlite/container-statements.txx b/libodb-sqlite/odb/sqlite/container-statements.txx
new file mode 100644
index 0000000..6db91f2
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/container-statements.txx
@@ -0,0 +1,107 @@
+// file : odb/sqlite/container-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // container_statements
+ //
+ template <typename T>
+ container_statements<T>::
+ container_statements (connection_type& conn, binding& id)
+ : conn_ (conn),
+ id_binding_ (id),
+ functions_ (this),
+ insert_image_binding_ (0, 0), // Initialized by impl.
+ select_image_binding_ (0, 0), // Initialized by impl.
+ svm_ (0)
+ {
+ functions_.insert_ = &traits::insert;
+ functions_.select_ = &traits::select;
+ functions_.delete__ = &traits::delete_;
+
+ data_image_.version = 0;
+ data_image_version_ = 0;
+ data_id_binding_version_ = 0;
+ }
+
+ // smart_container_statements
+ //
+ template <typename T>
+ smart_container_statements<T>::
+ smart_container_statements (connection_type& conn, binding& id)
+ : container_statements<T> (conn, id),
+ cond_image_binding_ (0, 0), // Initialized by impl.
+ update_image_binding_ (0, 0) // Initialized by impl.
+ {
+ this->functions_.update_ = &traits::update;
+
+ cond_image_.version = 0;
+ cond_image_version_ = 0;
+ cond_id_binding_version_ = 0;
+
+ update_id_binding_version_ = 0;
+ update_cond_image_version_ = 0;
+ update_data_image_version_ = 0;
+ }
+
+ // container_statements_impl
+ //
+ template <typename T>
+ container_statements_impl<T>::
+ container_statements_impl (connection_type& conn, binding& id)
+ : base (conn, id)
+ {
+ this->select_image_truncated_ = select_image_truncated_array_;
+
+ this->insert_image_binding_.bind = data_image_bind_;
+ this->insert_image_binding_.count = traits::data_column_count;
+
+ this->select_image_binding_.bind = data_image_bind_ +
+ traits::id_column_count;
+ this->select_image_binding_.count = traits::data_column_count -
+ traits::id_column_count;
+
+ std::memset (data_image_bind_, 0, sizeof (data_image_bind_));
+ std::memset (select_image_truncated_array_,
+ 0,
+ sizeof (select_image_truncated_array_));
+
+ for (std::size_t i (0);
+ i < traits::data_column_count - traits::id_column_count;
+ ++i)
+ data_image_bind_[i + traits::id_column_count].truncated =
+ select_image_truncated_array_ + i;
+
+ this->insert_text_ = traits::insert_statement;
+ this->select_text_ = traits::select_statement;
+ this->delete_text_ = traits::delete_statement;
+
+ this->versioned_ = traits::versioned;
+ }
+
+ // smart_container_statements_impl
+ //
+ template <typename T>
+ smart_container_statements_impl<T>::
+ smart_container_statements_impl (connection_type& conn, binding& id)
+ : container_statements_impl<T> (conn, id)
+ {
+ this->cond_image_binding_.bind = cond_image_bind_;
+ this->cond_image_binding_.count = traits::cond_column_count;
+
+ this->update_image_binding_.bind = update_image_bind_;
+ this->update_image_binding_.count = traits::value_column_count +
+ traits::cond_column_count;
+
+ std::memset (cond_image_bind_, 0, sizeof (cond_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+ this->update_text_ = traits::update_statement;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/database.cxx b/libodb-sqlite/odb/sqlite/database.cxx
new file mode 100644
index 0000000..a7cf098
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/database.cxx
@@ -0,0 +1,306 @@
+// file : odb/sqlite/database.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifdef _WIN32
+# include <odb/details/win32/windows.hxx> // WideCharToMultiByte
+#endif
+
+#include <sqlite3.h>
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/connection-factory.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/transaction.hxx>
+#include <odb/sqlite/error.hxx>
+#include <odb/sqlite/exceptions.hxx>
+
+#include <odb/sqlite/details/options.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ using odb::details::transfer_ptr;
+
+ database::
+ ~database ()
+ {
+ }
+
+ database::
+ database (const string& name,
+ int flags,
+ bool foreign_keys,
+ const string& vfs,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_sqlite),
+ name_ (name),
+ flags_ (flags),
+ foreign_keys_ (foreign_keys),
+ vfs_ (vfs),
+ factory_ (factory.transfer ())
+ {
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+#ifdef _WIN32
+ database::
+ database (const wstring& name,
+ int flags,
+ bool foreign_keys,
+ const string& vfs,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_sqlite),
+ flags_ (flags),
+ foreign_keys_ (foreign_keys),
+ vfs_ (vfs),
+ factory_ (factory.transfer ())
+ {
+ // Convert UTF-16 name to UTF-8 using the WideCharToMultiByte() Win32
+ // function.
+ //
+ int n (
+ WideCharToMultiByte (
+ CP_UTF8,
+ 0,
+ name.c_str (),
+ static_cast<int> (name.size ()),
+ 0,
+ 0,
+ 0,
+ 0));
+
+ if (n == 0)
+ throw database_exception (
+ SQLITE_CANTOPEN, SQLITE_CANTOPEN, "unable to open database file");
+
+ // This string is not shared so we are going to modify the underlying
+ // buffer directly.
+ //
+ name_.resize (static_cast<string::size_type> (n));
+
+ n = WideCharToMultiByte (
+ CP_UTF8,
+ 0,
+ name.c_str (),
+ static_cast<int> (name.size ()),
+ const_cast<char*> (name_.c_str ()),
+ n,
+ 0,
+ 0);
+
+ if (n == 0)
+ throw database_exception (
+ SQLITE_CANTOPEN, SQLITE_CANTOPEN, "unable to open database file");
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+#endif
+
+ database::
+ database (int& argc,
+ char* argv[],
+ bool erase,
+ int flags,
+ bool foreign_keys,
+ const string& vfs,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_sqlite),
+ flags_ (flags),
+ foreign_keys_ (foreign_keys),
+ vfs_ (vfs),
+ factory_ (factory.transfer ())
+ {
+ using namespace details;
+
+ try
+ {
+ cli::argv_file_scanner scan (argc, argv, "--options-file", erase);
+ options ops (scan, cli::unknown_mode::skip, cli::unknown_mode::skip);
+
+ name_ = ops.database ();
+
+ if (ops.create ())
+ flags_ |= SQLITE_OPEN_CREATE;
+
+ if (ops.read_only ())
+ flags_ = (flags_ & ~SQLITE_OPEN_READWRITE) | SQLITE_OPEN_READONLY;
+ }
+ catch (const cli::exception& e)
+ {
+ ostringstream ostr;
+ ostr << e;
+ throw cli_exception (ostr.str ());
+ }
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const connection_ptr& conn,
+ const string& name,
+ const string& schema,
+ transfer_ptr<attached_connection_factory> factory)
+ : odb::database (id_sqlite),
+ name_ (name),
+ schema_ (schema),
+ flags_ (0),
+ factory_ (factory.transfer ())
+ {
+ assert (!schema_.empty ());
+
+ // Copy some things over from the connection's database.
+ //
+ database& db (conn->database ());
+
+ tracer_ = db.tracer_;
+ foreign_keys_ = db.foreign_keys_;
+
+ if (!factory_)
+ factory_.reset (new default_attached_connection_factory (
+ connection::main_connection (conn)));
+
+ factory_->database (*this);
+ }
+
+ void database::
+ print_usage (ostream& os)
+ {
+ details::options::print_usage (os);
+ }
+
+ transaction_impl* database::
+ begin ()
+ {
+ return new transaction_impl (*this, transaction_impl::deferred);
+ }
+
+ transaction_impl* database::
+ begin_immediate ()
+ {
+ return new transaction_impl (*this, transaction_impl::immediate);
+ }
+
+ transaction_impl* database::
+ begin_exclusive ()
+ {
+ return new transaction_impl (*this, transaction_impl::exclusive);
+ }
+
+ odb::connection* database::
+ connection_ ()
+ {
+ connection_ptr c (factory_->connect ());
+ return c.release ();
+ }
+
+ const database::schema_version_info& database::
+ load_schema_version (const string& name) const
+ {
+ schema_version_info& svi (schema_version_map_[name]);
+
+ // Construct the SELECT statement text.
+ //
+ string text ("SELECT \"version\", \"migration\" FROM ");
+
+ if (!svi.version_table.empty ())
+ text += svi.version_table; // Already quoted.
+ else if (!schema_version_table_.empty ())
+ text += schema_version_table_; // Already quoted.
+ else
+ text += "\"main\".\"schema_version\"";
+
+ text += " WHERE \"name\" = ?";
+
+ // Bind parameters and results.
+ //
+ size_t psize[1] = {name.size ()};
+ bind pbind[1] = {{bind::text,
+ const_cast<char*> (name.c_str ()),
+ &psize[0],
+ 0, 0, 0}};
+ binding param (pbind, 1);
+ param.version++;
+
+ long long migration;
+ bool rnull[2];
+ bind rbind[2] = {{bind::integer, &svi.version, 0, 0, &rnull[0], 0},
+ {bind::integer, &migration, 0, 0, &rnull[1], 0}};
+ binding result (rbind, 2);
+ result.version++;
+
+ // If we are not in transaction, SQLite will start an implicit one
+ // which suits us just fine.
+ //
+ connection_ptr cp;
+ if (!transaction::has_current ())
+ cp = factory_->connect ();
+
+ sqlite::connection& c (
+ cp != 0
+ ? *cp
+ : transaction::current ().connection (const_cast<database&> (*this)));
+
+ try
+ {
+ select_statement st (c,
+ text,
+ false, // Don't process.
+ false, // Don't optimize.
+ param,
+ result);
+ st.execute ();
+ auto_result ar (st);
+
+ switch (st.fetch ())
+ {
+ case select_statement::success:
+ {
+ svi.migration = migration != 0;
+ assert (st.fetch () == select_statement::no_data);
+ break;
+ }
+ case select_statement::no_data:
+ {
+ svi.version = 0; // No schema.
+ break;
+ }
+ case select_statement::truncated:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+ catch (const database_exception& e)
+ {
+ // Try to detect the case where there is no version table. SQLite
+ // doesn't have an extended error code for this so we have to use
+ // the error text.
+ //
+ if (e.error () == SQLITE_ERROR &&
+ e.message ().compare (0, 14, "no such table:") == 0)
+ svi.version = 0; // No schema.
+ else
+ throw;
+ }
+
+ return svi;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/database.hxx b/libodb-sqlite/odb/sqlite/database.hxx
new file mode 100644
index 0000000..e1e62cc
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/database.hxx
@@ -0,0 +1,567 @@
+// file : odb/sqlite/database.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_DATABASE_HXX
+#define ODB_SQLITE_DATABASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <sqlite3.h>
+
+#include <string>
+#include <iosfwd> // std::ostream
+
+#include <odb/database.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/transfer-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/query.hxx>
+#include <odb/sqlite/tracer.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/connection-factory.hxx>
+#include <odb/sqlite/transaction-impl.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+// We use the sqlite3_open_v2() flags in our interface. Define them
+// for SQLite earlier that 3.5.0.
+//
+#if SQLITE_VERSION_NUMBER < 3005000
+# define SQLITE_OPEN_READONLY 0x00000001
+# define SQLITE_OPEN_READWRITE 0x00000002
+# define SQLITE_OPEN_CREATE 0x00000004
+#endif
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class transaction_impl;
+
+ class LIBODB_SQLITE_EXPORT database: public odb::database
+ {
+ public:
+ database (const std::string& name,
+ int flags = SQLITE_OPEN_READWRITE,
+ bool foreign_keys = true,
+ const std::string& vfs = "",
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+#ifdef _WIN32
+ database (const std::wstring& name,
+ int flags = SQLITE_OPEN_READWRITE,
+ bool foreign_keys = true,
+ const std::string& vfs = "",
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+#endif
+
+ // Extract the database parameters from the command line. The
+ // following options are recognized:
+ //
+ // --database
+ // --create
+ // --read-only
+ // --options-file
+ //
+ // For more information, see the output of the print_usage() function
+ // below. If erase is true, the above options are removed from the argv
+ // array and the argc count is updated accordingly. The command line
+ // options override the flags passed as an argument. This constructor
+ // may throw the cli_exception exception.
+ //
+ database (int& argc,
+ char* argv[],
+ bool erase = false,
+ int flags = SQLITE_OPEN_READWRITE,
+ bool foreign_keys = true,
+ const std::string& vfs = "",
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Attach to the specified connection a database with the specified name
+ // as the specified schema. Good understanding of SQLite ATTACH/DETACH
+ // DATABASE statements semantics and ODB connection management is
+ // strongly recommended when using this mechanism.
+ //
+ // The resulting database instance is referred to as an "attached
+ // database" and the connection it returns as an "attached connection"
+ // (which is just a proxy for the main connection). Database operations
+ // executed on the attached database or attached connection are
+ // automatically translated to refer to the specified schema rather than
+ // "main". For uniformity attached databases can also be created for the
+ // pre-attached "main" and "temp" schemas (in this case name can be
+ // anything).
+ //
+ // The automatic translation of the statements relies on their text
+ // having references to top-level database entities (tables, indexes,
+ // etc) qualified with the "main" schema. To achieve this, compile your
+ // headers with `--schema main` and, if using schema migration, with
+ // `--schema-version-table main.schema_version`. You must also not use
+ // "main" as an object/table alias in views of native statements. For
+ // optimal translation performance use 4-character schema names.
+ //
+ // The main connection and attached to it databases and connections are
+ // all meant to be used within the same thread. In particular, the
+ // attached database holds a counted reference to the main connection
+ // which means the connection will not be released until all the
+ // attached to this connection databases are destroyed.
+ //
+ // Note that in this model the attached databases are attached to the
+ // main connection, not to the (main) database, which mimics the
+ // underlying semantics of SQLite. An alternative model would have been
+ // to notionally attach the databases to the main database and under the
+ // hood automatically attach them to each returned connection. While
+ // this may seem like a more convenient model in some cases, it is also
+ // less flexible: the current model allows attaching a different set of
+ // databases to different connections, attaching them on demand as the
+ // transaction progresses, etc. Also, the more convenient model can be
+ // implemented on top of this model by deriving an application-specific
+ // database class and/or providing custom connection factories.
+ //
+ // Note that unless the name is a URI with appropriate mode, it is
+ // opened with the SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE flags. So if
+ // you want just SQLITE_OPEN_READWRITE, then you will need to verify its
+ // existence manually prior to calling this constructor.
+ //
+ // Note that attaching/detaching databases withing a transaction is only
+ // supported since SQLite 3.21.0.
+ //
+ database (const connection_ptr&,
+ const std::string& name,
+ const std::string& schema,
+ details::transfer_ptr<attached_connection_factory> =
+ details::transfer_ptr<attached_connection_factory> ());
+
+ // The database is automatically detached on destruction but a failure
+ // to detach is ignored. To detect such a failure perform explicit
+ // detach. For uniformity detaching a main database is a no-op.
+ //
+ void
+ detach ();
+
+ // Return the main database of an attached database. If this database
+ // is main, return itself.
+ //
+ database&
+ main_database ();
+
+ // Move-constructible but not move-assignable.
+ //
+ // Note: noexcept is not specified since odb::database(odb::database&&)
+ // can throw.
+ //
+#ifdef ODB_CXX11
+ database (database&&);
+#endif
+
+ static void
+ print_usage (std::ostream&);
+
+ public:
+ const std::string&
+ name () const
+ {
+ return name_;
+ }
+
+ // Schema name under which this database was attached or empty for the
+ // main database.
+ //
+ const std::string&
+ schema () const
+ {
+ return schema_;
+ }
+
+ int
+ flags () const
+ {
+ return flags_;
+ }
+
+ bool
+ foreign_keys () const
+ {
+ return foreign_keys_;
+ }
+
+ const std::string&
+ vfs () const
+ {
+ return vfs_;
+ }
+
+ // Object persistence API.
+ //
+ public:
+
+ // Make the object persistent.
+ //
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Load an object. Throw object_not_persistent if not found.
+ //
+ template <typename T>
+ typename object_traits<T>::pointer_type
+ load (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ void
+ load (const typename object_traits<T>::id_type& id, T& object);
+
+ // Load (or reload, if it is already loaded) a section of an object.
+ //
+ template <typename T>
+ void
+ load (T& object, section&);
+
+ // Reload an object.
+ //
+ template <typename T>
+ void
+ reload (T& object);
+
+ template <typename T>
+ void
+ reload (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ reload (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Loan an object if found. Return NULL/false if not found.
+ //
+ template <typename T>
+ typename object_traits<T>::pointer_type
+ find (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ bool
+ find (const typename object_traits<T>::id_type& id, T& object);
+
+ // Update the state of a modified objects.
+ //
+ template <typename T>
+ void
+ update (T& object);
+
+ template <typename T>
+ void
+ update (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ update (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Update a section of an object. Throws the section_not_loaded
+ // exception if the section is not loaded. Note also that this
+ // function does not clear the changed flag if it is set.
+ //
+ template <typename T>
+ void
+ update (const T& object, const section&);
+
+ // Make the object transient. Throw object_not_persistent if not
+ // found.
+ //
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ void
+ erase (T& object);
+
+ template <typename T>
+ void
+ erase (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Erase multiple objects matching a query predicate.
+ //
+ template <typename T>
+ unsigned long long
+ erase_query ();
+
+ template <typename T>
+ unsigned long long
+ erase_query (const char*);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const std::string&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const sqlite::query_base&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const odb::query_base&);
+
+ // Query API.
+ //
+ template <typename T>
+ result<T>
+ query ();
+
+ template <typename T>
+ result<T>
+ query (const char*);
+
+ template <typename T>
+ result<T>
+ query (const std::string&);
+
+ template <typename T>
+ result<T>
+ query (const sqlite::query_base&);
+
+ template <typename T>
+ result<T>
+ query (const odb::query_base&);
+
+ // Query one API.
+ //
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one ();
+
+ template <typename T>
+ bool
+ query_one (T& object);
+
+ template <typename T>
+ T
+ query_value ();
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const char*);
+
+ template <typename T>
+ bool
+ query_one (const char*, T& object);
+
+ template <typename T>
+ T
+ query_value (const char*);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const std::string&);
+
+ template <typename T>
+ bool
+ query_one (const std::string&, T& object);
+
+ template <typename T>
+ T
+ query_value (const std::string&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const sqlite::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const sqlite::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const sqlite::query_base&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const odb::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const odb::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const odb::query_base&);
+
+ // Query preparation.
+ //
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const sqlite::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // Transactions.
+ //
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ transaction_impl*
+ begin_immediate ();
+
+ transaction_impl*
+ begin_exclusive ();
+
+ public:
+ connection_ptr
+ connection ();
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef sqlite::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::database::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::database::tracer (t);
+ }
+
+ using odb::database::tracer;
+
+ // Database schema version.
+ //
+ protected:
+ virtual const schema_version_info&
+ load_schema_version (const std::string& schema_name) const;
+
+ // Database id constant (useful for meta-programming).
+ //
+ public:
+ static const odb::database_id database_id = id_sqlite;
+
+ public:
+ virtual
+ ~database ();
+
+ protected:
+ virtual odb::connection*
+ connection_ ();
+
+ private:
+ friend class transaction_impl; // factory_
+
+ // Note: remember to update move ctor if adding any new members.
+ //
+ std::string name_;
+ std::string schema_;
+ int flags_;
+ bool foreign_keys_;
+ std::string vfs_;
+
+ // Note: keep last so that all other database members are still valid
+ // during factory's destruction.
+ //
+ details::unique_ptr<connection_factory> factory_;
+ };
+ }
+}
+
+#include <odb/sqlite/database.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_DATABASE_HXX
diff --git a/libodb-sqlite/odb/sqlite/database.ixx b/libodb-sqlite/odb/sqlite/database.ixx
new file mode 100644
index 0000000..e906a39
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/database.ixx
@@ -0,0 +1,622 @@
+// file : odb/sqlite/database.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <utility> // move()
+
+#include <odb/sqlite/transaction.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+#ifdef ODB_CXX11
+ inline database::
+ database (database&& db) // Has to be inline.
+ : odb::database (std::move (db)),
+ name_ (std::move (db.name_)),
+ schema_ (std::move (db.schema_)),
+ flags_ (db.flags_),
+ foreign_keys_ (db.foreign_keys_),
+ vfs_ (std::move (db.vfs_)),
+ factory_ (std::move (db.factory_))
+ {
+ factory_->database (*this); // New database instance.
+ }
+#endif
+
+ inline void database::
+ detach ()
+ {
+ if (!schema_.empty ())
+ static_cast<attached_connection_factory&> (*factory_).detach ();
+ }
+
+ inline database& database::
+ main_database ()
+ {
+ return schema_.empty ()
+ ? *this
+ : static_cast<attached_connection_factory&> (*factory_).main_connection_->database ();
+ }
+
+ inline connection_ptr database::
+ connection ()
+ {
+ // Go through the virtual connection_() function instead of
+ // directly to allow overriding.
+ //
+ return connection_ptr (
+ static_cast<sqlite::connection*> (connection_ ()));
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T& obj)
+ {
+ return persist_<T, id_sqlite> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const T& obj)
+ {
+ return persist_<const T, id_sqlite> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T>& p)
+ {
+ const P<T>& cr (p);
+ return persist<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ return persist<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const typename object_traits<T>::pointer_type& pobj)
+ {
+ return persist_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ load (const typename object_traits<T>::id_type& id)
+ {
+ return load_<T, id_sqlite> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ load (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return load_<T, id_sqlite> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ load (T& obj, section& s)
+ {
+ return load_<T, id_sqlite> (obj, s);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ find (const typename object_traits<T>::id_type& id)
+ {
+ return find_<T, id_sqlite> (id);
+ }
+
+ template <typename T>
+ inline bool database::
+ find (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return find_<T, id_sqlite> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T& obj)
+ {
+ reload_<T, id_sqlite> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T* p)
+ {
+ reload<T> (*p);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (const P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (const P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T>
+ inline void database::
+ reload (const typename object_traits<T>::pointer_type& pobj)
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ reload (odb::pointer_traits<pointer_type>::get_ref (pobj));
+ }
+
+ template <typename T>
+ inline void database::
+ update (T& obj)
+ {
+ update_<T, id_sqlite> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ update (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (P<T>& p)
+ {
+ const P<T>& cr (p);
+ update<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ update<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const typename object_traits<T>::pointer_type& pobj)
+ {
+ update_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const T& obj, const section& s)
+ {
+ update_<T, id_sqlite> (obj, s);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::id_type& id)
+ {
+ return erase_<T, id_sqlite> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T& obj)
+ {
+ return erase_<T, id_sqlite> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (P<T>& p)
+ {
+ const P<T>& cr (p);
+ erase<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ erase<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::pointer_type& pobj)
+ {
+ erase_<T, id_sqlite> (pobj);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query ()
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (sqlite::query_base ());
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const char* q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const std::string& q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const sqlite::query_base& q)
+ {
+ // T is always object_type.
+ //
+ return object_traits_impl<T, id_sqlite>::erase_query (*this, q);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return erase_query<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query ()
+ {
+ return query<T> (sqlite::query_base ());
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const char* q)
+ {
+ return query<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const std::string& q)
+ {
+ return query<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const sqlite::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_<T, id_sqlite>::call (*this, q);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one ()
+ {
+ return query_one<T> (sqlite::query_base ());
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (T& o)
+ {
+ return query_one<T> (sqlite::query_base (), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value ()
+ {
+ return query_value<T> (sqlite::query_base ());
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const char* q)
+ {
+ return query_one<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const char* q, T& o)
+ {
+ return query_one<T> (sqlite::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const char* q)
+ {
+ return query_value<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const std::string& q)
+ {
+ return query_one<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const std::string& q, T& o)
+ {
+ return query_one<T> (sqlite::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const std::string& q)
+ {
+ return query_value<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const sqlite::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_sqlite> (q);
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const sqlite::query_base& q, T& o)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_sqlite> (q, o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const sqlite::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_value_<T, id_sqlite> (q);
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const odb::query_base& q, T& o)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (sqlite::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_value<T> (sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, sqlite::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const sqlite::query_base& q)
+ {
+ // Throws if not in transaction.
+ //
+ sqlite::connection& c (transaction::current ().connection (*this));
+ return c.prepare_query<T> (n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, sqlite::query_base (q));
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/details/.gitignore b/libodb-sqlite/odb/sqlite/details/.gitignore
new file mode 100644
index 0000000..b298f89
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/.gitignore
@@ -0,0 +1 @@
+/options.?xx
diff --git a/libodb-sqlite/odb/sqlite/details/build2/config-stub.h b/libodb-sqlite/odb/sqlite/details/build2/config-stub.h
new file mode 100644
index 0000000..34ba8ff
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/build2/config-stub.h
@@ -0,0 +1,5 @@
+/* file : odb/sqlite/details/build2/config-stub.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+#include <odb/sqlite/details/config.h>
diff --git a/libodb-sqlite/odb/sqlite/details/build2/config-vc-stub.h b/libodb-sqlite/odb/sqlite/details/build2/config-vc-stub.h
new file mode 100644
index 0000000..70e3e93
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/build2/config-vc-stub.h
@@ -0,0 +1,5 @@
+/* file : odb/sqlite/details/build2/config-vc-stub.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+#include <odb/sqlite/details/config-vc.h>
diff --git a/libodb-sqlite/odb/sqlite/details/build2/config-vc.h b/libodb-sqlite/odb/sqlite/details/build2/config-vc.h
new file mode 100644
index 0000000..75ce442
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/build2/config-vc.h
@@ -0,0 +1,15 @@
+/* file : odb/sqlite/details/build2/config-vc.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* Configuration file for Windows/VC++ for the build2 build. */
+
+#ifndef ODB_SQLITE_DETAILS_CONFIG_VC_H
+#define ODB_SQLITE_DETAILS_CONFIG_VC_H
+
+/* Define LIBODB_SQLITE_BUILD2 for the installed case. */
+#ifndef LIBODB_SQLITE_BUILD2
+# define LIBODB_SQLITE_BUILD2
+#endif
+
+#endif /* ODB_SQLITE_DETAILS_CONFIG_VC_H */
diff --git a/libodb-sqlite/odb/sqlite/details/build2/config.h b/libodb-sqlite/odb/sqlite/details/build2/config.h
new file mode 100644
index 0000000..b4a1a1f
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/build2/config.h
@@ -0,0 +1,17 @@
+/* file : odb/sqlite/details/build2/config.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* Static configuration file for the build2 build. The installed case
+ (when LIBODB_SQLITE_BUILD2 is not necessarily defined) is the only
+ reason we have it. */
+
+#ifndef ODB_SQLITE_DETAILS_CONFIG_H
+#define ODB_SQLITE_DETAILS_CONFIG_H
+
+/* Define LIBODB_SQLITE_BUILD2 for the installed case. */
+#ifndef LIBODB_SQLITE_BUILD2
+# define LIBODB_SQLITE_BUILD2
+#endif
+
+#endif /* ODB_SQLITE_DETAILS_CONFIG_H */
diff --git a/libodb-sqlite/odb/sqlite/details/config-vc.h b/libodb-sqlite/odb/sqlite/details/config-vc.h
new file mode 100644
index 0000000..f42c2a0
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/config-vc.h
@@ -0,0 +1,5 @@
+/* file : odb/sqlite/details/config-vc.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* Dummy configuration file for Windows/VC++. */
diff --git a/libodb-sqlite/odb/sqlite/details/config.h.in b/libodb-sqlite/odb/sqlite/details/config.h.in
new file mode 100644
index 0000000..a223ecb
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/config.h.in
@@ -0,0 +1,14 @@
+/* file : odb/sqlite/details/config.h.in
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* This file is automatically processed by configure. */
+
+#ifndef ODB_SQLITE_DETAILS_CONFIG_H
+#define ODB_SQLITE_DETAILS_CONFIG_H
+
+#undef LIBODB_SQLITE_STATIC_LIB
+#undef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+#undef LIBODB_SQLITE_HAVE_COLUMN_METADATA
+
+#endif /* ODB_SQLITE_DETAILS_CONFIG_H */
diff --git a/libodb-sqlite/odb/sqlite/details/config.hxx b/libodb-sqlite/odb/sqlite/details/config.hxx
new file mode 100644
index 0000000..4f1c5ce
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/config.hxx
@@ -0,0 +1,33 @@
+// file : odb/sqlite/details/config.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_DETAILS_CONFIG_HXX
+#define ODB_SQLITE_DETAILS_CONFIG_HXX
+
+// no pre
+
+#ifdef ODB_COMPILER
+# error libodb-sqlite header included in odb-compiled header
+#elif !defined(LIBODB_SQLITE_BUILD2)
+# ifdef _MSC_VER
+# include <odb/sqlite/details/config-vc.h>
+# else
+# include <odb/sqlite/details/config.h>
+# endif
+#endif
+
+// LIBODB_SQLITE_BUILD2 macro can be defined either by the buildfile or by the
+// included odb/sqlite/details/config*.h (see above).
+//
+#ifdef LIBODB_SQLITE_BUILD2
+# include <sqlite3.h>
+
+# if SQLITE_VERSION_NUMBER >= 3006012
+# define LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY 1
+# endif
+# define LIBODB_SQLITE_HAVE_COLUMN_METADATA 1
+#endif
+
+// no post
+
+#endif // ODB_SQLITE_DETAILS_CONFIG_HXX
diff --git a/libodb-sqlite/odb/sqlite/details/conversion.hxx b/libodb-sqlite/odb/sqlite/details/conversion.hxx
new file mode 100644
index 0000000..04843b2
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/conversion.hxx
@@ -0,0 +1,58 @@
+// file : odb/sqlite/details/conversion.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_DETAILS_CONVERSION_HXX
+#define ODB_SQLITE_DETAILS_CONVERSION_HXX
+
+#include <odb/sqlite/traits.hxx>
+
+#include <odb/details/meta/answer.hxx>
+
+namespace odb
+{
+ // @@ Revise this.
+ //
+ namespace details {}
+
+ namespace sqlite
+ {
+ namespace details
+ {
+ using namespace odb::details;
+
+ // Detect whether conversion is specified in type_traits.
+ //
+ template <typename T>
+ meta::yes
+ conversion_p_test (typename type_traits<T>::conversion*);
+
+ template <typename T>
+ meta::no
+ conversion_p_test (...);
+
+ template <typename T>
+ struct conversion_p
+ {
+ static const bool value =
+ sizeof (conversion_p_test<T> (0)) == sizeof (meta::yes);
+ };
+
+ template <typename T, bool = conversion_p<T>::value>
+ struct conversion;
+
+ template <typename T>
+ struct conversion<T, true>
+ {
+ static const char* to () {return type_traits<T>::conversion::to ();}
+ };
+
+ template <typename T>
+ struct conversion<T, false>
+ {
+ static const char* to () {return 0;}
+ };
+ }
+ }
+}
+
+#endif // ODB_SQLITE_DETAILS_CONVERSION_HXX
diff --git a/libodb-sqlite/odb/sqlite/details/export.hxx b/libodb-sqlite/odb/sqlite/details/export.hxx
new file mode 100644
index 0000000..c0903ae
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/export.hxx
@@ -0,0 +1,86 @@
+// file : odb/sqlite/details/export.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_DETAILS_EXPORT_HXX
+#define ODB_SQLITE_DETAILS_EXPORT_HXX
+
+#include <odb/pre.hxx>
+
+#ifdef ODB_COMPILER
+# error libodb-sqlite header included in odb-compiled header
+#elif !defined(LIBODB_SQLITE_BUILD2)
+# ifdef _MSC_VER
+# include <odb/sqlite/details/config-vc.h>
+# else
+# include <odb/sqlite/details/config.h>
+# endif
+#endif
+
+// Normally we don't export class templates (but do complete specializations),
+// inline functions, and classes with only inline member functions. Exporting
+// classes that inherit from non-exported/imported bases (e.g., std::string)
+// will end up badly. The only known workarounds are to not inherit or to not
+// export. Also, MinGW GCC doesn't like seeing non-exported function being
+// used before their inline definition. The workaround is to reorder code. In
+// the end it's all trial and error.
+
+#ifdef LIBODB_SQLITE_BUILD2
+
+#if defined(LIBODB_SQLITE_STATIC) // Using static.
+# define LIBODB_SQLITE_EXPORT
+#elif defined(LIBODB_SQLITE_STATIC_BUILD) // Building static.
+# define LIBODB_SQLITE_EXPORT
+#elif defined(LIBODB_SQLITE_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBODB_SQLITE_EXPORT __declspec(dllimport)
+# else
+# define LIBODB_SQLITE_EXPORT
+# endif
+#elif defined(LIBODB_SQLITE_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBODB_SQLITE_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_SQLITE_EXPORT
+# endif
+#else
+// If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared but in case
+// of shared will be sub-optimal compared to having dllimport.
+//
+# define LIBODB_SQLITE_EXPORT // Using static or shared.
+#endif
+
+#else // LIBODB_SQLITE_BUILD2
+
+#ifdef LIBODB_SQLITE_STATIC_LIB
+# define LIBODB_SQLITE_EXPORT
+#else
+# ifdef _WIN32
+# ifdef _MSC_VER
+# ifdef LIBODB_SQLITE_DYNAMIC_LIB
+# define LIBODB_SQLITE_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_SQLITE_EXPORT __declspec(dllimport)
+# endif
+# else
+# ifdef LIBODB_SQLITE_DYNAMIC_LIB
+# ifdef DLL_EXPORT
+# define LIBODB_SQLITE_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_SQLITE_EXPORT
+# endif
+# else
+# define LIBODB_SQLITE_EXPORT __declspec(dllimport)
+# endif
+# endif
+# else
+# define LIBODB_SQLITE_EXPORT
+# endif
+#endif
+
+#endif // LIBODB_SQLITE_BUILD2
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_DETAILS_EXPORT_HXX
diff --git a/libodb-sqlite/odb/sqlite/details/options.cli b/libodb-sqlite/odb/sqlite/details/options.cli
new file mode 100644
index 0000000..d1955c3
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/options.cli
@@ -0,0 +1,47 @@
+// file : odb/sqlite/details/options.cli
+// license : GNU GPL v2; see accompanying LICENSE file
+
+include <string>;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace details
+ {
+ class options
+ {
+ std::string --database
+ {
+ "<filename>",
+ "SQLite database file name. If the database file is not specified
+ then a private, temporary on-disk database will be created. Use
+ the \cb{:memory:} special name to create a private, temporary
+ in-memory database."
+ };
+
+ bool --create
+ {
+ "Create the SQLite database if it does not already exist. By default
+ opening the database fails if it does not already exist."
+ };
+
+ bool --read-only
+ {
+ "Open the SQLite database in read-only mode. By default the database
+ is opened for reading and writing if possible, or reading only if
+ the file is write-protected by the operating system."
+ };
+
+ std::string --options-file
+ {
+ "<file>",
+ "Read additional options from <file>. Each option should appear on a
+ separate line optionally followed by space or equal sign (\cb{=})
+ and an option value. Empty lines and lines starting with \cb{#} are
+ ignored."
+ };
+ };
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.cxx b/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.cxx
new file mode 100644
index 0000000..12f4193
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.cxx
@@ -0,0 +1,1027 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <odb/sqlite/details/options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <utility>
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <fstream>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // unknown_option
+ //
+ unknown_option::
+ ~unknown_option () throw ()
+ {
+ }
+
+ void unknown_option::
+ print (::std::ostream& os) const
+ {
+ os << "unknown option '" << option ().c_str () << "'";
+ }
+
+ const char* unknown_option::
+ what () const throw ()
+ {
+ return "unknown option";
+ }
+
+ // unknown_argument
+ //
+ unknown_argument::
+ ~unknown_argument () throw ()
+ {
+ }
+
+ void unknown_argument::
+ print (::std::ostream& os) const
+ {
+ os << "unknown argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unknown_argument::
+ what () const throw ()
+ {
+ return "unknown argument";
+ }
+
+ // missing_value
+ //
+ missing_value::
+ ~missing_value () throw ()
+ {
+ }
+
+ void missing_value::
+ print (::std::ostream& os) const
+ {
+ os << "missing value for option '" << option ().c_str () << "'";
+ }
+
+ const char* missing_value::
+ what () const throw ()
+ {
+ return "missing option value";
+ }
+
+ // invalid_value
+ //
+ invalid_value::
+ ~invalid_value () throw ()
+ {
+ }
+
+ void invalid_value::
+ print (::std::ostream& os) const
+ {
+ os << "invalid value '" << value ().c_str () << "' for option '"
+ << option ().c_str () << "'";
+
+ if (!message ().empty ())
+ os << ": " << message ().c_str ();
+ }
+
+ const char* invalid_value::
+ what () const throw ()
+ {
+ return "invalid option value";
+ }
+
+ // eos_reached
+ //
+ void eos_reached::
+ print (::std::ostream& os) const
+ {
+ os << what ();
+ }
+
+ const char* eos_reached::
+ what () const throw ()
+ {
+ return "end of argument stream reached";
+ }
+
+ // file_io_failure
+ //
+ file_io_failure::
+ ~file_io_failure () throw ()
+ {
+ }
+
+ void file_io_failure::
+ print (::std::ostream& os) const
+ {
+ os << "unable to open file '" << file ().c_str () << "' or read failure";
+ }
+
+ const char* file_io_failure::
+ what () const throw ()
+ {
+ return "unable to open file or read failure";
+ }
+
+ // unmatched_quote
+ //
+ unmatched_quote::
+ ~unmatched_quote () throw ()
+ {
+ }
+
+ void unmatched_quote::
+ print (::std::ostream& os) const
+ {
+ os << "unmatched quote in argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unmatched_quote::
+ what () const throw ()
+ {
+ return "unmatched quote";
+ }
+
+ // scanner
+ //
+ scanner::
+ ~scanner ()
+ {
+ }
+
+ // argv_scanner
+ //
+ bool argv_scanner::
+ more ()
+ {
+ return i_ < argc_;
+ }
+
+ const char* argv_scanner::
+ peek ()
+ {
+ if (i_ < argc_)
+ return argv_[i_];
+ else
+ throw eos_reached ();
+ }
+
+ const char* argv_scanner::
+ next ()
+ {
+ if (i_ < argc_)
+ {
+ const char* r (argv_[i_]);
+
+ if (erase_)
+ {
+ for (int i (i_ + 1); i < argc_; ++i)
+ argv_[i - 1] = argv_[i];
+
+ --argc_;
+ argv_[argc_] = 0;
+ }
+ else
+ ++i_;
+
+ ++start_position_;
+ return r;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ void argv_scanner::
+ skip ()
+ {
+ if (i_ < argc_)
+ {
+ ++i_;
+ ++start_position_;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ std::size_t argv_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ // argv_file_scanner
+ //
+ int argv_file_scanner::zero_argc_ = 0;
+ std::string argv_file_scanner::empty_string_;
+
+ bool argv_file_scanner::
+ more ()
+ {
+ if (!args_.empty ())
+ return true;
+
+ while (base::more ())
+ {
+ // See if the next argument is the file option.
+ //
+ const char* a (base::peek ());
+ const option_info* oi = 0;
+ const char* ov = 0;
+
+ if (!skip_)
+ {
+ if ((oi = find (a)) != 0)
+ {
+ base::next ();
+
+ if (!base::more ())
+ throw missing_value (a);
+
+ ov = base::next ();
+ }
+ else if (std::strncmp (a, "-", 1) == 0)
+ {
+ if ((ov = std::strchr (a, '=')) != 0)
+ {
+ std::string o (a, 0, ov - a);
+ if ((oi = find (o.c_str ())) != 0)
+ {
+ base::next ();
+ ++ov;
+ }
+ }
+ }
+ }
+
+ if (oi != 0)
+ {
+ if (oi->search_func != 0)
+ {
+ std::string f (oi->search_func (ov, oi->arg));
+
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ load (ov);
+
+ if (!args_.empty ())
+ return true;
+ }
+ else
+ {
+ if (!skip_)
+ skip_ = (std::strcmp (a, "--") == 0);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ const char* argv_file_scanner::
+ peek ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? base::peek () : args_.front ().value.c_str ();
+ }
+
+ const std::string& argv_file_scanner::
+ peek_file ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? empty_string_ : *args_.front ().file;
+ }
+
+ std::size_t argv_file_scanner::
+ peek_line ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? 0 : args_.front ().line;
+ }
+
+ const char* argv_file_scanner::
+ next ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::next ();
+ else
+ {
+ hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);
+ args_.pop_front ();
+ ++start_position_;
+ return hold_[i_].c_str ();
+ }
+ }
+
+ void argv_file_scanner::
+ skip ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::skip ();
+ else
+ {
+ args_.pop_front ();
+ ++start_position_;
+ }
+ }
+
+ const argv_file_scanner::option_info* argv_file_scanner::
+ find (const char* a) const
+ {
+ for (std::size_t i (0); i < options_count_; ++i)
+ if (std::strcmp (a, options_[i].option) == 0)
+ return &options_[i];
+
+ return 0;
+ }
+
+ std::size_t argv_file_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ void argv_file_scanner::
+ load (const std::string& file)
+ {
+ using namespace std;
+
+ ifstream is (file.c_str ());
+
+ if (!is.is_open ())
+ throw file_io_failure (file);
+
+ files_.push_back (file);
+
+ arg a;
+ a.file = &*files_.rbegin ();
+
+ for (a.line = 1; !is.eof (); ++a.line)
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ throw file_io_failure (file);
+
+ string::size_type n (line.size ());
+
+ // Trim the line from leading and trailing whitespaces.
+ //
+ if (n != 0)
+ {
+ const char* f (line.c_str ());
+ const char* l (f + n);
+
+ const char* of (f);
+ while (f < l && (*f == ' ' || *f == '\t' || *f == '\r'))
+ ++f;
+
+ --l;
+
+ const char* ol (l);
+ while (l > f && (*l == ' ' || *l == '\t' || *l == '\r'))
+ --l;
+
+ if (f != of || l != ol)
+ line = f <= l ? string (f, l - f + 1) : string ();
+ }
+
+ // Ignore empty lines, those that start with #.
+ //
+ if (line.empty () || line[0] == '#')
+ continue;
+
+ string::size_type p (string::npos);
+ if (line.compare (0, 1, "-") == 0)
+ {
+ p = line.find (' ');
+
+ string::size_type q (line.find ('='));
+ if (q != string::npos && q < p)
+ p = q;
+ }
+
+ string s1;
+ if (p != string::npos)
+ {
+ s1.assign (line, 0, p);
+
+ // Skip leading whitespaces in the argument.
+ //
+ if (line[p] == '=')
+ ++p;
+ else
+ {
+ n = line.size ();
+ for (++p; p < n; ++p)
+ {
+ char c (line[p]);
+ if (c != ' ' && c != '\t' && c != '\r')
+ break;
+ }
+ }
+ }
+ else if (!skip_)
+ skip_ = (line == "--");
+
+ string s2 (line, p != string::npos ? p : 0);
+
+ // If the string (which is an option value or argument) is
+ // wrapped in quotes, remove them.
+ //
+ n = s2.size ();
+ char cf (s2[0]), cl (s2[n - 1]);
+
+ if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
+ {
+ if (n == 1 || cf != cl)
+ throw unmatched_quote (s2);
+
+ s2 = string (s2, 1, n - 2);
+ }
+
+ if (!s1.empty ())
+ {
+ // See if this is another file option.
+ //
+ const option_info* oi;
+ if (!skip_ && (oi = find (s1.c_str ())))
+ {
+ if (s2.empty ())
+ throw missing_value (oi->option);
+
+ if (oi->search_func != 0)
+ {
+ string f (oi->search_func (s2.c_str (), oi->arg));
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ {
+ // If the path of the file being parsed is not simple and the
+ // path of the file that needs to be loaded is relative, then
+ // complete the latter using the former as a base.
+ //
+#ifndef _WIN32
+ string::size_type p (file.find_last_of ('/'));
+ bool c (p != string::npos && s2[0] != '/');
+#else
+ string::size_type p (file.find_last_of ("/\\"));
+ bool c (p != string::npos && s2[1] != ':');
+#endif
+ if (c)
+ s2.insert (0, file, 0, p + 1);
+
+ load (s2);
+ }
+
+ continue;
+ }
+
+ a.value = s1;
+ args_.push_back (a);
+ }
+
+ a.value = s2;
+ args_.push_back (a);
+ }
+ }
+
+ template <typename X>
+ struct parser
+ {
+ static void
+ parse (X& x, scanner& s)
+ {
+ using namespace std;
+
+ const char* o (s.next ());
+ if (s.more ())
+ {
+ string v (s.next ());
+ istringstream is (v);
+ if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+ }
+ };
+
+ template <>
+ struct parser<bool>
+ {
+ static void
+ parse (bool& x, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ const char* v (s.next ());
+
+ if (std::strcmp (v, "1") == 0 ||
+ std::strcmp (v, "true") == 0 ||
+ std::strcmp (v, "TRUE") == 0 ||
+ std::strcmp (v, "True") == 0)
+ x = true;
+ else if (std::strcmp (v, "0") == 0 ||
+ std::strcmp (v, "false") == 0 ||
+ std::strcmp (v, "FALSE") == 0 ||
+ std::strcmp (v, "False") == 0)
+ x = false;
+ else
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+ }
+ };
+
+ template <>
+ struct parser<std::string>
+ {
+ static void
+ parse (std::string& x, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ x = s.next ();
+ else
+ throw missing_value (o);
+ }
+ };
+
+ template <typename X>
+ struct parser<std::pair<X, std::size_t> >
+ {
+ static void
+ parse (std::pair<X, std::size_t>& x, scanner& s)
+ {
+ x.second = s.position ();
+ parser<X>::parse (x.first, s);
+ }
+ };
+
+ template <typename X>
+ struct parser<std::vector<X> >
+ {
+ static void
+ parse (std::vector<X>& c, scanner& s)
+ {
+ X x;
+ parser<X>::parse (x, s);
+ c.push_back (x);
+ }
+ };
+
+ template <typename X, typename C>
+ struct parser<std::set<X, C> >
+ {
+ static void
+ parse (std::set<X, C>& c, scanner& s)
+ {
+ X x;
+ parser<X>::parse (x, s);
+ c.insert (x);
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::map<K, V, C> >
+ {
+ static void
+ parse (std::map<K, V, C>& m, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, s);
+ }
+
+ m[k] = v;
+ }
+ else
+ throw missing_value (o);
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::multimap<K, V, C> >
+ {
+ static void
+ parse (std::multimap<K, V, C>& m, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, s);
+ }
+
+ m.insert (typename std::multimap<K, V, C>::value_type (k, v));
+ }
+ else
+ throw missing_value (o);
+ }
+ };
+
+ template <typename X, typename T, T X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, s);
+ }
+
+ template <typename X, bool X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ s.next ();
+ x.*M = true;
+ }
+ }
+ }
+ }
+}
+
+#include <map>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace details
+ {
+ // options
+ //
+
+ options::
+ options ()
+ : database_ (),
+ create_ (),
+ read_only_ (),
+ options_file_ ()
+ {
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ bool erase,
+ ::odb::sqlite::details::cli::unknown_mode opt,
+ ::odb::sqlite::details::cli::unknown_mode arg)
+ : database_ (),
+ create_ (),
+ read_only_ (),
+ options_file_ ()
+ {
+ ::odb::sqlite::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ ::odb::sqlite::details::cli::unknown_mode opt,
+ ::odb::sqlite::details::cli::unknown_mode arg)
+ : database_ (),
+ create_ (),
+ read_only_ (),
+ options_file_ ()
+ {
+ ::odb::sqlite::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::sqlite::details::cli::unknown_mode opt,
+ ::odb::sqlite::details::cli::unknown_mode arg)
+ : database_ (),
+ create_ (),
+ read_only_ (),
+ options_file_ ()
+ {
+ ::odb::sqlite::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::sqlite::details::cli::unknown_mode opt,
+ ::odb::sqlite::details::cli::unknown_mode arg)
+ : database_ (),
+ create_ (),
+ read_only_ (),
+ options_file_ ()
+ {
+ ::odb::sqlite::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (::odb::sqlite::details::cli::scanner& s,
+ ::odb::sqlite::details::cli::unknown_mode opt,
+ ::odb::sqlite::details::cli::unknown_mode arg)
+ : database_ (),
+ create_ (),
+ read_only_ (),
+ options_file_ ()
+ {
+ _parse (s, opt, arg);
+ }
+
+ ::odb::sqlite::details::cli::usage_para options::
+ print_usage (::std::ostream& os, ::odb::sqlite::details::cli::usage_para p)
+ {
+ CLI_POTENTIALLY_UNUSED (os);
+
+ if (p != ::odb::sqlite::details::cli::usage_para::none)
+ os << ::std::endl;
+
+ os << "--database <filename> SQLite database file name. If the database file is not" << ::std::endl
+ << " specified then a private, temporary on-disk database will" << ::std::endl
+ << " be created. Use the :memory: special name to create a" << ::std::endl
+ << " private, temporary in-memory database." << ::std::endl;
+
+ os << std::endl
+ << "--create Create the SQLite database if it does not already exist." << ::std::endl
+ << " By default opening the database fails if it does not" << ::std::endl
+ << " already exist." << ::std::endl;
+
+ os << std::endl
+ << "--read-only Open the SQLite database in read-only mode. By default" << ::std::endl
+ << " the database is opened for reading and writing if" << ::std::endl
+ << " possible, or reading only if the file is write-protected" << ::std::endl
+ << " by the operating system." << ::std::endl;
+
+ os << std::endl
+ << "--options-file <file> Read additional options from <file>. Each option should" << ::std::endl
+ << " appear on a separate line optionally followed by space or" << ::std::endl
+ << " equal sign (=) and an option value. Empty lines and lines" << ::std::endl
+ << " starting with # are ignored." << ::std::endl;
+
+ p = ::odb::sqlite::details::cli::usage_para::option;
+
+ return p;
+ }
+
+ typedef
+ std::map<std::string, void (*) (options&, ::odb::sqlite::details::cli::scanner&)>
+ _cli_options_map;
+
+ static _cli_options_map _cli_options_map_;
+
+ struct _cli_options_map_init
+ {
+ _cli_options_map_init ()
+ {
+ _cli_options_map_["--database"] =
+ &::odb::sqlite::details::cli::thunk< options, std::string, &options::database_ >;
+ _cli_options_map_["--create"] =
+ &::odb::sqlite::details::cli::thunk< options, &options::create_ >;
+ _cli_options_map_["--read-only"] =
+ &::odb::sqlite::details::cli::thunk< options, &options::read_only_ >;
+ _cli_options_map_["--options-file"] =
+ &::odb::sqlite::details::cli::thunk< options, std::string, &options::options_file_ >;
+ }
+ };
+
+ static _cli_options_map_init _cli_options_map_init_;
+
+ bool options::
+ _parse (const char* o, ::odb::sqlite::details::cli::scanner& s)
+ {
+ _cli_options_map::const_iterator i (_cli_options_map_.find (o));
+
+ if (i != _cli_options_map_.end ())
+ {
+ (*(i->second)) (*this, s);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool options::
+ _parse (::odb::sqlite::details::cli::scanner& s,
+ ::odb::sqlite::details::cli::unknown_mode opt_mode,
+ ::odb::sqlite::details::cli::unknown_mode arg_mode)
+ {
+ bool r = false;
+ bool opt = true;
+
+ while (s.more ())
+ {
+ const char* o = s.peek ();
+
+ if (std::strcmp (o, "--") == 0)
+ {
+ opt = false;
+ s.skip ();
+ r = true;
+ continue;
+ }
+
+ if (opt)
+ {
+ if (_parse (o, s))
+ {
+ r = true;
+ continue;
+ }
+
+ if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
+ {
+ // Handle combined option values.
+ //
+ std::string co;
+ if (const char* v = std::strchr (o, '='))
+ {
+ co.assign (o, 0, v - o);
+ ++v;
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (co.c_str ()),
+ const_cast<char*> (v)
+ };
+
+ ::odb::sqlite::details::cli::argv_scanner ns (0, ac, av);
+
+ if (_parse (co.c_str (), ns))
+ {
+ // Parsed the option but not its value?
+ //
+ if (ns.end () != 2)
+ throw ::odb::sqlite::details::cli::invalid_value (co, v);
+
+ s.next ();
+ r = true;
+ continue;
+ }
+ else
+ {
+ // Set the unknown option and fall through.
+ //
+ o = co.c_str ();
+ }
+ }
+
+ switch (opt_mode)
+ {
+ case ::odb::sqlite::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::sqlite::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::sqlite::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::sqlite::details::cli::unknown_option (o);
+ }
+ }
+
+ break;
+ }
+ }
+
+ switch (arg_mode)
+ {
+ case ::odb::sqlite::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::sqlite::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::sqlite::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::sqlite::details::cli::unknown_argument (o);
+ }
+ }
+
+ break;
+ }
+
+ return r;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
diff --git a/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.hxx b/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.hxx
new file mode 100644
index 0000000..abc4b3f
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.hxx
@@ -0,0 +1,530 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+#ifndef LIBODB_SQLITE_DETAILS_OPTIONS_HXX
+#define LIBODB_SQLITE_DETAILS_OPTIONS_HXX
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <list>
+#include <deque>
+#include <iosfwd>
+#include <string>
+#include <cstddef>
+#include <exception>
+
+#ifndef CLI_POTENTIALLY_UNUSED
+# if defined(_MSC_VER) || defined(__xlC__)
+# define CLI_POTENTIALLY_UNUSED(x) (void*)&x
+# else
+# define CLI_POTENTIALLY_UNUSED(x) (void)x
+# endif
+#endif
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ class usage_para
+ {
+ public:
+ enum value
+ {
+ none,
+ text,
+ option
+ };
+
+ usage_para (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ class unknown_mode
+ {
+ public:
+ enum value
+ {
+ skip,
+ stop,
+ fail
+ };
+
+ unknown_mode (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ // Exceptions.
+ //
+
+ class exception: public std::exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const = 0;
+ };
+
+ ::std::ostream&
+ operator<< (::std::ostream&, const exception&);
+
+ class unknown_option: public exception
+ {
+ public:
+ virtual
+ ~unknown_option () throw ();
+
+ unknown_option (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class unknown_argument: public exception
+ {
+ public:
+ virtual
+ ~unknown_argument () throw ();
+
+ unknown_argument (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ class missing_value: public exception
+ {
+ public:
+ virtual
+ ~missing_value () throw ();
+
+ missing_value (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class invalid_value: public exception
+ {
+ public:
+ virtual
+ ~invalid_value () throw ();
+
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message = std::string ());
+
+ const std::string&
+ option () const;
+
+ const std::string&
+ value () const;
+
+ const std::string&
+ message () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ std::string value_;
+ std::string message_;
+ };
+
+ class eos_reached: public exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class file_io_failure: public exception
+ {
+ public:
+ virtual
+ ~file_io_failure () throw ();
+
+ file_io_failure (const std::string& file);
+
+ const std::string&
+ file () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string file_;
+ };
+
+ class unmatched_quote: public exception
+ {
+ public:
+ virtual
+ ~unmatched_quote () throw ();
+
+ unmatched_quote (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ // Command line argument scanner interface.
+ //
+ // The values returned by next() are guaranteed to be valid
+ // for the two previous arguments up until a call to a third
+ // peek() or next().
+ //
+ // The position() function returns a monotonically-increasing
+ // number which, if stored, can later be used to determine the
+ // relative position of the argument returned by the following
+ // call to next(). Note that if multiple scanners are used to
+ // extract arguments from multiple sources, then the end
+ // position of the previous scanner should be used as the
+ // start position of the next.
+ //
+ class scanner
+ {
+ public:
+ virtual
+ ~scanner ();
+
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+
+ virtual std::size_t
+ position () = 0;
+ };
+
+ class argv_scanner: public scanner
+ {
+ public:
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ int
+ end () const;
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ protected:
+ std::size_t start_position_;
+ int i_;
+ int& argc_;
+ char** argv_;
+ bool erase_;
+ };
+
+ class argv_file_scanner: public argv_scanner
+ {
+ public:
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t start_position = 0);
+
+ struct option_info
+ {
+ // If search_func is not NULL, it is called, with the arg
+ // value as the second argument, to locate the options file.
+ // If it returns an empty string, then the file is ignored.
+ //
+ const char* option;
+ std::string (*search_func) (const char*, void* arg);
+ void* arg;
+ };
+
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const option_info* options = 0,
+ std::size_t options_count = 0,
+ std::size_t start_position = 0);
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ // Return the file path if the peeked at argument came from a file and
+ // the empty string otherwise. The reference is guaranteed to be valid
+ // till the end of the scanner lifetime.
+ //
+ const std::string&
+ peek_file ();
+
+ // Return the 1-based line number if the peeked at argument came from
+ // a file and zero otherwise.
+ //
+ std::size_t
+ peek_line ();
+
+ private:
+ const option_info*
+ find (const char*) const;
+
+ void
+ load (const std::string& file);
+
+ typedef argv_scanner base;
+
+ const std::string option_;
+ option_info option_info_;
+ const option_info* options_;
+ std::size_t options_count_;
+
+ struct arg
+ {
+ std::string value;
+ const std::string* file;
+ std::size_t line;
+ };
+
+ std::deque<arg> args_;
+ std::list<std::string> files_;
+
+ // Circular buffer of two arguments.
+ //
+ std::string hold_[2];
+ std::size_t i_;
+
+ bool skip_;
+
+ static int zero_argc_;
+ static std::string empty_string_;
+ };
+
+ template <typename X>
+ struct parser;
+ }
+ }
+ }
+}
+
+#include <string>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace details
+ {
+ class options
+ {
+ public:
+ options ();
+
+ options (int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::sqlite::details::cli::unknown_mode option = ::odb::sqlite::details::cli::unknown_mode::fail,
+ ::odb::sqlite::details::cli::unknown_mode argument = ::odb::sqlite::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::sqlite::details::cli::unknown_mode option = ::odb::sqlite::details::cli::unknown_mode::fail,
+ ::odb::sqlite::details::cli::unknown_mode argument = ::odb::sqlite::details::cli::unknown_mode::stop);
+
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::sqlite::details::cli::unknown_mode option = ::odb::sqlite::details::cli::unknown_mode::fail,
+ ::odb::sqlite::details::cli::unknown_mode argument = ::odb::sqlite::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::sqlite::details::cli::unknown_mode option = ::odb::sqlite::details::cli::unknown_mode::fail,
+ ::odb::sqlite::details::cli::unknown_mode argument = ::odb::sqlite::details::cli::unknown_mode::stop);
+
+ options (::odb::sqlite::details::cli::scanner&,
+ ::odb::sqlite::details::cli::unknown_mode option = ::odb::sqlite::details::cli::unknown_mode::fail,
+ ::odb::sqlite::details::cli::unknown_mode argument = ::odb::sqlite::details::cli::unknown_mode::stop);
+
+ // Option accessors.
+ //
+ const std::string&
+ database () const;
+
+ const bool&
+ create () const;
+
+ const bool&
+ read_only () const;
+
+ const std::string&
+ options_file () const;
+
+ // Print usage information.
+ //
+ static ::odb::sqlite::details::cli::usage_para
+ print_usage (::std::ostream&,
+ ::odb::sqlite::details::cli::usage_para = ::odb::sqlite::details::cli::usage_para::none);
+
+ // Implementation details.
+ //
+ protected:
+ bool
+ _parse (const char*, ::odb::sqlite::details::cli::scanner&);
+
+ private:
+ bool
+ _parse (::odb::sqlite::details::cli::scanner&,
+ ::odb::sqlite::details::cli::unknown_mode option,
+ ::odb::sqlite::details::cli::unknown_mode argument);
+
+ public:
+ std::string database_;
+ bool create_;
+ bool read_only_;
+ std::string options_file_;
+ };
+ }
+ }
+}
+
+#include <odb/sqlite/details/options.ixx>
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
+#endif // LIBODB_SQLITE_DETAILS_OPTIONS_HXX
diff --git a/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.ixx b/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.ixx
new file mode 100644
index 0000000..54092aa
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/details/pregenerated/odb/sqlite/details/options.ixx
@@ -0,0 +1,324 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <cassert>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // usage_para
+ //
+ inline usage_para::
+ usage_para (value v)
+ : v_ (v)
+ {
+ }
+
+ // unknown_mode
+ //
+ inline unknown_mode::
+ unknown_mode (value v)
+ : v_ (v)
+ {
+ }
+
+ // exception
+ //
+ inline ::std::ostream&
+ operator<< (::std::ostream& os, const exception& e)
+ {
+ e.print (os);
+ return os;
+ }
+
+ // unknown_option
+ //
+ inline unknown_option::
+ unknown_option (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& unknown_option::
+ option () const
+ {
+ return option_;
+ }
+
+ // unknown_argument
+ //
+ inline unknown_argument::
+ unknown_argument (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unknown_argument::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // missing_value
+ //
+ inline missing_value::
+ missing_value (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& missing_value::
+ option () const
+ {
+ return option_;
+ }
+
+ // invalid_value
+ //
+ inline invalid_value::
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message)
+ : option_ (option),
+ value_ (value),
+ message_ (message)
+ {
+ }
+
+ inline const std::string& invalid_value::
+ option () const
+ {
+ return option_;
+ }
+
+ inline const std::string& invalid_value::
+ value () const
+ {
+ return value_;
+ }
+
+ inline const std::string& invalid_value::
+ message () const
+ {
+ return message_;
+ }
+
+ // file_io_failure
+ //
+ inline file_io_failure::
+ file_io_failure (const std::string& file)
+ : file_ (file)
+ {
+ }
+
+ inline const std::string& file_io_failure::
+ file () const
+ {
+ return file_;
+ }
+
+ // unmatched_quote
+ //
+ inline unmatched_quote::
+ unmatched_quote (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unmatched_quote::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // argv_scanner
+ //
+ inline argv_scanner::
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + 1),
+ i_ (1),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline argv_scanner::
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + static_cast<std::size_t> (start)),
+ i_ (start),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline int argv_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ // argv_file_scanner
+ //
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+
+ load (file);
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const option_info* options,
+ std::size_t options_count,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ load (file);
+ }
+ }
+ }
+ }
+}
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace details
+ {
+ // options
+ //
+
+ inline const std::string& options::
+ database () const
+ {
+ return this->database_;
+ }
+
+ inline const bool& options::
+ create () const
+ {
+ return this->create_;
+ }
+
+ inline const bool& options::
+ read_only () const
+ {
+ return this->read_only_;
+ }
+
+ inline const std::string& options::
+ options_file () const
+ {
+ return this->options_file_;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
diff --git a/libodb-sqlite/odb/sqlite/error.cxx b/libodb-sqlite/odb/sqlite/error.cxx
new file mode 100644
index 0000000..ae6bbe3
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/error.cxx
@@ -0,0 +1,94 @@
+// file : odb/sqlite/error.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <sqlite3.h>
+
+#include <new> // std::bad_alloc
+#include <string>
+
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/exceptions.hxx>
+
+#include <odb/sqlite/details/config.hxx> // LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ void
+ translate_error (int e, connection& c)
+ {
+ sqlite3* h (c.handle ());
+
+ // Extended error codes are only available in 3.6.5 and later.
+ //
+#if SQLITE_VERSION_NUMBER >= 3006005
+ int ee (sqlite3_extended_errcode (h));
+#else
+ int ee (0);
+#endif
+ string m;
+
+ switch (e)
+ {
+ case SQLITE_NOMEM:
+ {
+ throw bad_alloc ();
+ }
+ case SQLITE_MISUSE:
+ {
+ // In case of SQLITE_MISUSE, error code/message may or may not
+ // be set.
+ //
+ ee = e;
+ m = "SQLite API misuse";
+ break;
+ }
+#ifdef SQLITE_ABORT_ROLLBACK
+ case SQLITE_ABORT:
+ {
+ if (ee == SQLITE_ABORT_ROLLBACK)
+ throw forced_rollback ();
+
+ break;
+ }
+#endif
+ case SQLITE_LOCKED:
+ {
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ if (ee != SQLITE_LOCKED_SHAREDCACHE)
+ throw deadlock (); // The DROP TABLE special case.
+#endif
+ // Getting SQLITE_LOCKED_SHAREDCACHE here means we don't have
+ // the unlock notify support. Translate this to timeout.
+ //
+ throw timeout ();
+ }
+ case SQLITE_BUSY:
+ case SQLITE_IOERR:
+ {
+#if SQLITE_VERSION_NUMBER >= 3006005
+ if (e != SQLITE_IOERR || ee == SQLITE_IOERR_BLOCKED)
+ throw timeout ();
+#endif
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (m.empty ())
+ m = sqlite3_errmsg (h);
+
+ // Get rid of a trailing newline if there is one.
+ //
+ string::size_type n (m.size ());
+ if (n != 0 && m[n - 1] == '\n')
+ m.resize (n - 1);
+
+ throw database_exception (e, ee, m);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/error.hxx b/libodb-sqlite/odb/sqlite/error.hxx
new file mode 100644
index 0000000..4646f85
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/error.hxx
@@ -0,0 +1,27 @@
+// file : odb/sqlite/error.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_ERROR_HXX
+#define ODB_SQLITE_ERROR_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class connection;
+
+ // Translate SQLite error and throw an appropriate exception.
+ //
+ LIBODB_SQLITE_EXPORT void
+ translate_error (int error, connection&);
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_ERROR_HXX
diff --git a/libodb-sqlite/odb/sqlite/exceptions.cxx b/libodb-sqlite/odb/sqlite/exceptions.cxx
new file mode 100644
index 0000000..0621189
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/exceptions.cxx
@@ -0,0 +1,92 @@
+// file : odb/sqlite/exceptions.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/sqlite/exceptions.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // forced_rollback
+ //
+
+ const char* forced_rollback::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "transaction is forced to rollback";
+ }
+
+ forced_rollback* forced_rollback::
+ clone () const
+ {
+ return new forced_rollback (*this);
+ }
+
+ //
+ // database_exception
+ //
+
+ database_exception::
+ ~database_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ database_exception::
+ database_exception (int e, int ee, const string& m)
+ : error_ (e), extended_error_ (ee), message_ (m)
+ {
+ ostringstream ostr;
+ ostr << error_;
+
+ if (error_ != extended_error_)
+ ostr << " (" << extended_error_ << ")";
+
+ ostr << ": " << message_;
+ what_ = ostr.str ();
+ }
+
+ const char* database_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ database_exception* database_exception::
+ clone () const
+ {
+ return new database_exception (*this);
+ }
+
+ //
+ // cli_exception
+ //
+
+ cli_exception::
+ cli_exception (const std::string& what)
+ : what_ (what)
+ {
+ }
+
+ cli_exception::
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* cli_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ cli_exception* cli_exception::
+ clone () const
+ {
+ return new cli_exception (*this);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/exceptions.hxx b/libodb-sqlite/odb/sqlite/exceptions.hxx
new file mode 100644
index 0000000..3adb433
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/exceptions.hxx
@@ -0,0 +1,101 @@
+// file : odb/sqlite/exceptions.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_EXCEPTIONS_HXX
+#define ODB_SQLITE_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+
+#include <odb/exceptions.hxx>
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // This exception is thrown if SQLite is forcing the current transaction
+ // to rollback. This can happen in SQLite 3.7.11 or later if one of the
+ // connections participating in the shared cache rolls back a transaction.
+ // See the SQLITE_ABORT_ROLLBACK extended error code for detail on this
+ // behavior.
+ //
+ struct LIBODB_SQLITE_EXPORT forced_rollback: recoverable
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual forced_rollback*
+ clone () const;
+ };
+
+ struct LIBODB_SQLITE_EXPORT database_exception: odb::database_exception
+ {
+ database_exception (int error,
+ int extended_error,
+ const std::string& message);
+
+ ~database_exception () ODB_NOTHROW_NOEXCEPT;
+
+ int
+ error () const
+ {
+ return error_;
+ }
+
+ int
+ extended_error () const
+ {
+ return extended_error_;
+ }
+
+ const std::string&
+ message () const
+ {
+ return message_;
+ }
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual database_exception*
+ clone () const;
+
+ private:
+ int error_;
+ int extended_error_;
+ std::string message_;
+ std::string what_;
+ };
+
+ struct LIBODB_SQLITE_EXPORT cli_exception: odb::exception
+ {
+ cli_exception (const std::string& what);
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT;
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual cli_exception*
+ clone () const;
+
+ private:
+ std::string what_;
+ };
+
+ namespace core
+ {
+ using sqlite::database_exception;
+ using sqlite::cli_exception;
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_EXCEPTIONS_HXX
diff --git a/libodb-sqlite/odb/sqlite/forward.hxx b/libodb-sqlite/odb/sqlite/forward.hxx
new file mode 100644
index 0000000..1be05b4
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/forward.hxx
@@ -0,0 +1,112 @@
+// file : odb/sqlite/forward.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_FORWARD_HXX
+#define ODB_SQLITE_FORWARD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ namespace core
+ {
+ using namespace odb::common;
+ }
+
+ //
+ //
+ class database;
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+ class connection_factory;
+ class statement;
+ class transaction;
+ class tracer;
+
+ namespace core
+ {
+ using sqlite::database;
+ using sqlite::connection;
+ using sqlite::connection_ptr;
+ using sqlite::transaction;
+ using sqlite::statement;
+ }
+
+ // Implementation details.
+ //
+ enum database_type_id
+ {
+ id_integer,
+ id_real,
+ id_text,
+ id_blob,
+ id_text_stream,
+ id_blob_stream
+ };
+
+ template <typename T, database_type_id>
+ struct default_value_traits;
+
+ enum statement_kind
+ {
+ statement_select,
+ statement_insert,
+ statement_update,
+ statement_delete,
+ statement_generic
+ };
+
+ class binding;
+ class select_statement;
+
+ template <typename T>
+ class object_statements;
+
+ template <typename T>
+ class polymorphic_root_object_statements;
+
+ template <typename T>
+ class polymorphic_derived_object_statements;
+
+ template <typename T>
+ class no_id_object_statements;
+
+ template <typename T>
+ class view_statements;
+
+ template <typename T>
+ class container_statements;
+
+ template <typename T>
+ class smart_container_statements;
+
+ template <typename T, typename ST>
+ class section_statements;
+
+ class query_base;
+ class query_params;
+ }
+
+ namespace details
+ {
+ template <>
+ struct counter_type<sqlite::connection>
+ {
+ typedef shared_base counter;
+ };
+
+ template <>
+ struct counter_type<sqlite::query_params>
+ {
+ typedef shared_base counter;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_FORWARD_HXX
diff --git a/libodb-sqlite/odb/sqlite/no-id-object-result.hxx b/libodb-sqlite/odb/sqlite/no-id-object-result.hxx
new file mode 100644
index 0000000..b0edb09
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/no-id-object-result.hxx
@@ -0,0 +1,80 @@
+// file : odb/sqlite/no-id-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_NO_ID_OBJECT_RESULT_HXX
+#define ODB_SQLITE_NO_ID_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/no-id-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx> // query_base, query_params
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/traits-calls.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ class no_id_object_result_impl: public odb::no_id_object_result_impl<T>
+ {
+ public:
+ typedef odb::no_id_object_result_impl<T> base_type;
+
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_sqlite> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~no_id_object_result_impl ();
+
+ no_id_object_result_impl (const query_base&,
+ const details::shared_ptr<select_statement>&,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ // We need to hold on to the query parameters because SQLite uses
+ // the parameter buffers to find each next row.
+ //
+ details::shared_ptr<query_params> params_;
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ };
+ }
+}
+
+#include <odb/sqlite/no-id-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_NO_ID_OBJECT_RESULT_HXX
diff --git a/libodb-sqlite/odb/sqlite/no-id-object-result.txx b/libodb-sqlite/odb/sqlite/no-id-object-result.txx
new file mode 100644
index 0000000..bd26afc
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/no-id-object-result.txx
@@ -0,0 +1,114 @@
+// file : odb/sqlite/no-id-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/sqlite/no-id-object-statements.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ no_id_object_result_impl<T>::
+ ~no_id_object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ params_.reset ();
+ statement_.reset ();
+ }
+
+ template <typename T>
+ no_id_object_result_impl<T>::
+ no_id_object_result_impl (const query_base& q,
+ const details::shared_ptr<select_statement>& s,
+ statements_type& sts,
+ const schema_version_migration* svm)
+ : base_type (sts.connection ()),
+ params_ (q.parameters ()),
+ statement_ (s),
+ statements_ (sts),
+ tc_ (svm)
+ {
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ load (object_type& obj)
+ {
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ statement_->reload ();
+ }
+ }
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+ tc_.init (obj, im, &this->db_);
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (!statement_->next ())
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t no_id_object_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/no-id-object-statements.hxx b/libodb-sqlite/odb/sqlite/no-id-object-statements.hxx
new file mode 100644
index 0000000..7a0376a
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/no-id-object-statements.hxx
@@ -0,0 +1,136 @@
+// file : odb/sqlite/no-id-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_NO_ID_OBJECT_STATEMENTS_HXX
+#define ODB_SQLITE_NO_ID_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/sqlite-types.hxx>
+#include <odb/sqlite/binding.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/statements-base.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // Implementation for objects without object id.
+ //
+
+ template <typename T>
+ class no_id_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_sqlite> object_traits;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef sqlite::insert_statement insert_statement_type;
+
+ public:
+ no_id_object_statements (connection_type&);
+
+ virtual
+ ~no_id_object_statements ();
+
+ // Object image.
+ //
+ image_type&
+ image () {return image_;}
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ {
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ 0));
+ }
+
+ return *persist_;
+ }
+
+ public:
+ // select = total
+ // insert = total - inverse; inverse == 0 for object without id
+ //
+ static const std::size_t insert_column_count =
+ object_traits::column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count;
+
+ private:
+ no_id_object_statements (const no_id_object_statements&);
+ no_id_object_statements& operator= (const no_id_object_statements&);
+
+ private:
+ image_type image_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ bind select_image_bind_[select_column_count];
+ bool select_image_truncated_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ };
+ }
+}
+
+#include <odb/sqlite/no-id-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_NO_ID_OBJECT_STATEMENTS_HXX
diff --git a/libodb-sqlite/odb/sqlite/no-id-object-statements.txx b/libodb-sqlite/odb/sqlite/no-id-object-statements.txx
new file mode 100644
index 0000000..d0a62b2
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/no-id-object-statements.txx
@@ -0,0 +1,36 @@
+// file : odb/sqlite/no-id-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ no_id_object_statements<T>::
+ ~no_id_object_statements ()
+ {
+ }
+
+ template <typename T>
+ no_id_object_statements<T>::
+ no_id_object_statements (connection_type& conn)
+ : statements_base (conn),
+ select_image_binding_ (select_image_bind_, select_column_count),
+ insert_image_binding_ (insert_image_bind_, insert_column_count)
+ {
+ image_.version = 0;
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/polymorphic-object-result.hxx b/libodb-sqlite/odb/sqlite/polymorphic-object-result.hxx
new file mode 100644
index 0000000..3239471
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/polymorphic-object-result.hxx
@@ -0,0 +1,98 @@
+// file : odb/sqlite/polymorphic-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_POLYMORPHIC_OBJECT_RESULT_HXX
+#define ODB_SQLITE_POLYMORPHIC_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/polymorphic-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx> // query_base, query_params
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/traits-calls.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ class polymorphic_object_result_impl:
+ public odb::polymorphic_object_result_impl<T>
+ {
+ public:
+ typedef odb::polymorphic_object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_sqlite> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename base_type::root_type root_type;
+ typedef typename base_type::discriminator_type discriminator_type;
+
+ typedef object_traits_impl<root_type, id_sqlite> root_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~polymorphic_object_result_impl ();
+
+ polymorphic_object_result_impl (
+ const query_base&,
+ const details::shared_ptr<select_statement>&,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type*, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual discriminator_type
+ load_discriminator ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ load_image ();
+
+ private:
+ // We need to hold on to the query parameters because SQLite uses
+ // the parameter buffers to find each next row.
+ //
+ details::shared_ptr<query_params> params_;
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ };
+ }
+}
+
+#include <odb/sqlite/polymorphic-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_POLYMORPHIC_OBJECT_RESULT_HXX
diff --git a/libodb-sqlite/odb/sqlite/polymorphic-object-result.txx b/libodb-sqlite/odb/sqlite/polymorphic-object-result.txx
new file mode 100644
index 0000000..bd22f01
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/polymorphic-object-result.txx
@@ -0,0 +1,287 @@
+// file : odb/sqlite/polymorphic-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/sqlite/polymorphic-object-statements.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ ~polymorphic_object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ params_.reset ();
+ statement_.reset ();
+ }
+
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ polymorphic_object_result_impl (
+ const query_base& q,
+ const details::shared_ptr<select_statement>& s,
+ statements_type& sts,
+ const schema_version_migration* svm)
+ : base_type (sts.connection ()),
+ params_ (q.parameters ()),
+ statement_ (s),
+ statements_ (sts),
+ tc_ (svm)
+ {
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ load (object_type* pobj, bool fetch)
+ {
+ if (fetch)
+ load_image ();
+
+ typename statements_type::root_statements_type& rsts (
+ statements_.root_statements ());
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!rsts.locked ());
+ typename statements_type::auto_lock l (rsts);
+
+ typename object_traits::image_type& i (statements_.image ());
+ typename root_traits::image_type& ri (rsts.image ());
+
+ id_type id (root_traits::id (ri));
+
+ // Determine this object's dynamic type.
+ //
+ typedef typename root_traits::info_type info_type;
+ discriminator_type d (root_traits::discriminator (ri));
+ discriminator_type disc (d);
+
+ // Use the polymorphic_info() helper to get concrete_info if
+ // object_type is concrete and NULL if it is abstract.
+ //
+ const info_type* spi (polymorphic_info (object_traits::info));
+ const info_type& pi (
+ spi != 0 && spi->discriminator == d
+ ? *spi
+ : root_traits::map->find (d));
+
+ typedef typename root_traits::pointer_type root_pointer_type;
+ typedef typename root_traits::pointer_traits root_pointer_traits;
+
+ typename object_traits::pointer_cache_traits::insert_guard ig;
+
+ if (pobj == 0)
+ {
+ // Need to create a new instance of the dynamic type.
+ //
+ root_pointer_type rp (pi.create ());
+ pointer_type p (
+ root_pointer_traits::template static_pointer_cast<object_type> (rp));
+
+ // Insert it as a root pointer (for non-unique pointers, rp should
+ // still be valid and for unique pointers this is a no-op).
+ //
+ ig.reset (
+ object_traits::pointer_cache_traits::insert (this->db_, id, rp));
+
+ pobj = &pointer_traits::get_ref (p);
+ current (p);
+ }
+ else
+ {
+ // We are loading into an existing instance. If the static and
+ // dynamic types differ, then make sure the instance is at least
+ // of the dynamic type.
+ //
+ if (&pi != &object_traits::info)
+ {
+ const info_type& dpi (root_traits::map->find (typeid (*pobj)));
+
+ if (&dpi != &pi && dpi.derived (pi))
+ throw object_not_persistent (); // @@ type_mismatch ?
+ }
+ }
+
+ callback_event ce (callback_event::pre_load);
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+
+ tc_.init (*pobj, i, &this->db_);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, dynamic part, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ root_traits::init (idi, id);
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, *pobj, false);
+
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ if (&pi != &object_traits::info)
+ {
+ std::size_t d (object_traits::depth);
+ pi.dispatch (info_type::call_load, this->db_, pobj, &d);
+ };
+
+ rsts.load_delayed (tc_.version ());
+ l.unlock ();
+
+ ce = callback_event::post_load;
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+ object_traits::pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::id_type
+ polymorphic_object_result_impl<T>::
+ load_id ()
+ {
+ load_image ();
+ return root_traits::id (statements_.root_statements ().image ());
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::discriminator_type
+ polymorphic_object_result_impl<T>::
+ load_discriminator ()
+ {
+ load_image ();
+ return root_traits::discriminator (
+ statements_.root_statements ().image ());
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (!statement_->next ())
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T, typename R>
+ struct polymorphic_image_rebind
+ {
+ // Derived type version.
+ //
+ typedef object_traits_impl<T, id_sqlite> traits;
+
+ static bool
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (traits::check_version (sts.select_image_versions (), im))
+ {
+ binding& b (sts.select_image_binding (traits::depth));
+ object_traits_calls<T> tc (svm);
+ tc.bind (b.bind, 0, 0, im, statement_select);
+ traits::update_version (
+ sts.select_image_versions (), im, sts.select_image_bindings ());
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ template <typename R>
+ struct polymorphic_image_rebind<R, R>
+ {
+ // Root type version.
+ //
+ typedef object_traits_impl<R, id_sqlite> traits;
+
+ static bool
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (im.version != sts.select_image_version ())
+ {
+ binding& b (sts.select_image_binding ());
+ object_traits_calls<R> tc (svm);
+ tc.bind (b.bind, im, statement_select);
+ sts.select_image_version (im.version);
+ b.version++;
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ load_image ()
+ {
+ typedef polymorphic_image_rebind<object_type, root_type> image_rebind;
+
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ image_rebind::rebind (statements_, tc_.version ());
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (image_rebind::rebind (statements_, tc_.version ()))
+ statement_->reload ();
+ }
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t polymorphic_object_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/polymorphic-object-statements.hxx b/libodb-sqlite/odb/sqlite/polymorphic-object-statements.hxx
new file mode 100644
index 0000000..736686b
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/polymorphic-object-statements.hxx
@@ -0,0 +1,479 @@
+// file : odb/sqlite/polymorphic-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+#define ODB_SQLITE_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/sqlite-types.hxx>
+#include <odb/sqlite/binding.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/statements-base.hxx>
+#include <odb/sqlite/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // Implementation for polymorphic objects.
+ //
+
+ template <typename T>
+ class polymorphic_root_object_statements: public object_statements<T>
+ {
+ public:
+ typedef typename object_statements<T>::connection_type connection_type;
+ typedef typename object_statements<T>::object_traits object_traits;
+ typedef typename object_statements<T>::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::discriminator_image_type
+ discriminator_image_type;
+
+ typedef
+ typename object_statements<T>::select_statement_type
+ select_statement_type;
+
+ public:
+ // Interface compatibility with derived_object_statements.
+ //
+ typedef polymorphic_root_object_statements root_statements_type;
+
+ root_statements_type&
+ root_statements ()
+ {
+ return *this;
+ }
+
+ public:
+ // Discriminator binding.
+ //
+ discriminator_image_type&
+ discriminator_image () {return discriminator_image_;}
+
+ std::size_t
+ discriminator_image_version () const
+ {return discriminator_image_version_;}
+
+ void
+ discriminator_image_version (std::size_t v)
+ {discriminator_image_version_ = v;}
+
+ binding&
+ discriminator_image_binding () {return discriminator_image_binding_;}
+
+ bool*
+ discriminator_image_truncated () {return discriminator_image_truncated_;}
+
+ // Id binding for discriminator retrieval.
+ //
+ id_image_type&
+ discriminator_id_image () {return discriminator_id_image_;}
+
+ std::size_t
+ discriminator_id_image_version () const
+ {return discriminator_id_image_version_;}
+
+ void
+ discriminator_id_image_version (std::size_t v)
+ {discriminator_id_image_version_ = v;}
+
+ binding&
+ discriminator_id_image_binding ()
+ {return discriminator_id_image_binding_;}
+
+ //
+ //
+ select_statement_type&
+ find_discriminator_statement ()
+ {
+ if (find_discriminator_ == 0)
+ {
+ find_discriminator_.reset (
+ new (details::shared) select_statement_type (
+ this->conn_,
+ object_traits::find_discriminator_statement,
+ false, // Doesn't need to be processed.
+ false, // Don't optimize.
+ discriminator_id_image_binding_,
+ discriminator_image_binding_));
+ }
+
+ return *find_discriminator_;
+ }
+
+ public:
+ polymorphic_root_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_root_object_statements ();
+
+ // Static "override" (statements type).
+ //
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (this->locked ());
+
+ if (!this->delayed_.empty ())
+ this->template load_delayed_<polymorphic_root_object_statements> (
+ svm);
+ }
+
+ public:
+ static const std::size_t id_column_count =
+ object_statements<T>::id_column_count;
+
+ static const std::size_t discriminator_column_count =
+ object_traits::discriminator_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ private:
+ // Discriminator image.
+ //
+ discriminator_image_type discriminator_image_;
+ std::size_t discriminator_image_version_;
+ binding discriminator_image_binding_;
+ bind discriminator_image_bind_[discriminator_column_count +
+ managed_optimistic_column_count];
+ bool discriminator_image_truncated_[discriminator_column_count +
+ managed_optimistic_column_count];
+
+ // Id image for discriminator retrieval (only used as a parameter).
+ //
+ id_image_type discriminator_id_image_;
+ std::size_t discriminator_id_image_version_;
+ binding discriminator_id_image_binding_;
+ bind discriminator_id_image_bind_[id_column_count];
+
+ details::shared_ptr<select_statement_type> find_discriminator_;
+ };
+
+ template <typename T>
+ class polymorphic_derived_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_sqlite> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::id_image_type id_image_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef typename object_traits::root_type root_type;
+ typedef
+ polymorphic_root_object_statements<root_type>
+ root_statements_type;
+
+ typedef typename object_traits::base_type base_type;
+ typedef
+ typename object_traits::base_traits::statements_type
+ base_statements_type;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef sqlite::insert_statement insert_statement_type;
+ typedef sqlite::select_statement select_statement_type;
+ typedef sqlite::update_statement update_statement_type;
+ typedef sqlite::delete_statement delete_statement_type;
+
+ typedef typename root_statements_type::auto_lock auto_lock;
+
+ public:
+ polymorphic_derived_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_derived_object_statements ();
+
+ public:
+ // Delayed loading.
+ //
+ static void
+ delayed_loader (odb::database&,
+ const id_type&,
+ root_type&,
+ const schema_version_migration*);
+
+ public:
+ // Root and immediate base statements.
+ //
+ root_statements_type&
+ root_statements ()
+ {
+ return root_statements_;
+ }
+
+ base_statements_type&
+ base_statements ()
+ {
+ return base_statements_;
+ }
+
+ public:
+ // Object image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ std::size_t
+ insert_id_binding_version () const { return insert_id_binding_version_;}
+
+ void
+ insert_id_binding_version (std::size_t v) {insert_id_binding_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t*
+ select_image_versions () { return select_image_versions_;}
+
+ binding*
+ select_image_bindings () {return select_image_bindings_;}
+
+ binding&
+ select_image_binding (std::size_t d)
+ {
+ return select_image_bindings_[object_traits::depth - d];
+ }
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Object id binding (comes from the root statements).
+ //
+ id_image_type&
+ id_image () {return root_statements_.id_image ();}
+
+ std::size_t
+ id_image_version () const {return root_statements_.id_image_version ();}
+
+ void
+ id_image_version (std::size_t v) {root_statements_.id_image_version (v);}
+
+ binding&
+ id_image_binding () {return root_statements_.id_image_binding ();}
+
+ binding&
+ optimistic_id_image_binding () {
+ return root_statements_.optimistic_id_image_binding ();}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ {
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ 0));
+ }
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement (std::size_t d)
+ {
+ std::size_t i (object_traits::depth - d);
+ details::shared_ptr<select_statement_type>& p (find_[i]);
+
+ if (p == 0)
+ {
+ p.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statements[i],
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ root_statements_.id_image_binding (),
+ select_image_bindings_[i]));
+ }
+
+ return *p;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ {
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
+ update_image_binding_));
+ }
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ {
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement,
+ root_statements_.id_image_binding ()));
+ }
+
+ return *erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ image_,
+ id_image (),
+ id_image_binding (),
+ &id_image_binding ()); // Note, not id+version.
+ }
+
+ public:
+ // select = total - id - separate_load + base::select
+ // insert = total - inverse
+ // update = total - inverse - id - readonly - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ id_column_count -
+ object_traits::separate_load_column_count +
+ base_statements_type::select_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count;
+
+ static const std::size_t update_column_count = insert_column_count -
+ object_traits::id_column_count -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ polymorphic_derived_object_statements (
+ const polymorphic_derived_object_statements&);
+
+ polymorphic_derived_object_statements&
+ operator= (const polymorphic_derived_object_statements&);
+
+ private:
+ root_statements_type& root_statements_;
+ base_statements_type& base_statements_;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ image_type image_;
+
+ // Select binding. Here we are have an array of statements/bindings
+ // one for each depth. In other words, if we have classes root, base,
+ // and derived, then we have the following array of statements:
+ //
+ // [0] d + b + r
+ // [1] d + b
+ // [2] d
+ //
+ // Also, because we have a chain of images bound to these statements,
+ // we have an array of versions, one entry for each base plus one for
+ // our own image.
+ //
+ // A poly-abstract class only needs the first statement and in this
+ // case we have only one entry in the the bindings and statements
+ // arrays (but not versions; we still have a chain of images).
+ //
+ std::size_t select_image_versions_[object_traits::depth];
+ binding select_image_bindings_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ bind select_image_bind_[select_column_count];
+ bool select_image_truncated_[select_column_count];
+
+ // Insert binding. The id binding is copied from the hierarchy root.
+ //
+ std::size_t insert_image_version_;
+ std::size_t insert_id_binding_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+
+ // Update binding. The id suffix binding is copied from the hierarchy
+ // root.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+ binding update_image_binding_;
+ bind update_image_bind_[update_column_count + id_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+ };
+ }
+}
+
+#include <odb/sqlite/polymorphic-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_POLYMORPHIC_OBJECT_STATEMENTS_HXX
diff --git a/libodb-sqlite/odb/sqlite/polymorphic-object-statements.txx b/libodb-sqlite/odb/sqlite/polymorphic-object-statements.txx
new file mode 100644
index 0000000..6a376d3
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/polymorphic-object-statements.txx
@@ -0,0 +1,145 @@
+// file : odb/sqlite/polymorphic-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/transaction.hxx>
+#include <odb/sqlite/statement-cache.hxx>
+#include <odb/sqlite/traits-calls.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // polymorphic_root_object_statements
+ //
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ ~polymorphic_root_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ polymorphic_root_object_statements (connection_type& conn)
+ : object_statements<T> (conn),
+ discriminator_image_binding_ (discriminator_image_bind_,
+ discriminator_column_count +
+ managed_optimistic_column_count),
+ discriminator_id_image_binding_ (discriminator_id_image_bind_,
+ id_column_count)
+ {
+ discriminator_image_.version = 0;
+ discriminator_id_image_.version = 0;
+
+ discriminator_image_version_ = 0;
+ discriminator_id_image_version_ = 0;
+
+ std::memset (discriminator_image_bind_,
+ 0,
+ sizeof (discriminator_image_bind_));
+ std::memset (discriminator_id_image_bind_,
+ 0,
+ sizeof (discriminator_id_image_bind_));
+ std::memset (discriminator_image_truncated_,
+ 0,
+ sizeof (discriminator_image_truncated_));
+
+ for (std::size_t i (0);
+ i < discriminator_column_count + managed_optimistic_column_count;
+ ++i)
+ {
+ discriminator_image_bind_[i].truncated =
+ discriminator_image_truncated_ + i;
+ }
+ }
+
+ //
+ // polymorphic_derived_object_statements
+ //
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ ~polymorphic_derived_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ polymorphic_derived_object_statements (connection_type& conn)
+ : statements_base (conn),
+ root_statements_ (conn.statement_cache ().find_object<root_type> ()),
+ base_statements_ (conn.statement_cache ().find_object<base_type> ()),
+ insert_image_binding_ (insert_image_bind_, insert_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count)
+ {
+ image_.base = &base_statements_.image ();
+ image_.version = 0;
+
+ for (std::size_t i (0); i < object_traits::depth; ++i)
+ select_image_versions_[i] = 0;
+
+ for (std::size_t i (0);
+ i < (object_traits::abstract ? 1 : object_traits::depth);
+ ++i)
+ {
+ select_image_bindings_[i].bind = select_image_bind_;
+ select_image_bindings_[i].count = object_traits::find_column_counts[i];
+ }
+
+ insert_image_version_ = 0;
+ insert_id_binding_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+
+ template <typename T>
+ void polymorphic_derived_object_statements<T>::
+ delayed_loader (odb::database& db,
+ const id_type& id,
+ root_type& robj,
+ const schema_version_migration* svm)
+ {
+ connection_type& conn (transaction::current ().connection (db));
+ polymorphic_derived_object_statements& sts (
+ conn.statement_cache ().find_object<object_type> ());
+ root_statements_type& rsts (sts.root_statements ());
+
+ object_type& obj (static_cast<object_type&> (robj));
+
+ // The same code as in object_statements::load_delayed_().
+ //
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (sts, &id))
+ throw object_not_persistent ();
+
+ object_traits::callback (db, obj, callback_event::pre_load);
+ tc.init (obj, sts.image (), &db);
+ tc.load_ (sts, obj, false); // Load containers, etc.
+
+ rsts.load_delayed (svm);
+
+ {
+ typename root_statements_type::auto_unlock u (rsts);
+ object_traits::callback (db, obj, callback_event::post_load);
+ }
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/prepared-query.cxx b/libodb-sqlite/odb/sqlite/prepared-query.cxx
new file mode 100644
index 0000000..79df0f2
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/prepared-query.cxx
@@ -0,0 +1,27 @@
+// file : odb/sqlite/prepared-query.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/prepared-query.hxx>
+
+#include <odb/sqlite/connection.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ prepared_query_impl::
+ ~prepared_query_impl ()
+ {
+ }
+
+ bool prepared_query_impl::
+ verify_connection (odb::transaction& t)
+ {
+ // The transaction can be started using the main database of any of the
+ // attached databases. So we verify the main connections match.
+ //
+ return &static_cast<connection&> (t.connection ()).main_connection () ==
+ &static_cast<connection&> (stmt->connection ()).main_connection ();
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/prepared-query.hxx b/libodb-sqlite/odb/sqlite/prepared-query.hxx
new file mode 100644
index 0000000..a8873a5
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/prepared-query.hxx
@@ -0,0 +1,37 @@
+// file : odb/sqlite/prepared-query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_PREPARED_QUERY_HXX
+#define ODB_SQLITE_PREPARED_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/prepared-query.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/query.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ struct LIBODB_SQLITE_EXPORT prepared_query_impl: odb::prepared_query_impl
+ {
+ virtual
+ ~prepared_query_impl ();
+
+ prepared_query_impl (odb::connection& c): odb::prepared_query_impl (c) {}
+
+ virtual bool
+ verify_connection (odb::transaction&);
+
+ sqlite::query_base query;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_PREPARED_QUERY_HXX
diff --git a/libodb-sqlite/odb/sqlite/query-const-expr.cxx b/libodb-sqlite/odb/sqlite/query-const-expr.cxx
new file mode 100644
index 0000000..c8eaec7
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query-const-expr.cxx
@@ -0,0 +1,14 @@
+// file : odb/sqlite/query-const-expr.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/query.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // Sun CC cannot handle this in query.cxx.
+ //
+ const query_base query_base::true_expr (true);
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/query-dynamic.cxx b/libodb-sqlite/odb/sqlite/query-dynamic.cxx
new file mode 100644
index 0000000..8089aed
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query-dynamic.cxx
@@ -0,0 +1,157 @@
+// file : odb/sqlite/query-dynamic.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+
+#include <odb/sqlite/query-dynamic.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ static const char* logic_operators[] = {") AND (", ") OR ("};
+ static const char* comp_operators[] = {"=", "!=", "<", ">", "<=", ">="};
+
+ static void
+ translate (query_base& q, const odb::query_base& s, size_t p)
+ {
+ typedef odb::query_base::clause_part part;
+
+ const part& x (s.clause ()[p]);
+
+ switch (x.kind)
+ {
+ case part::kind_column:
+ {
+ const query_column_base* c (
+ static_cast<const query_column_base*> (
+ x.native_info[id_sqlite].column));
+
+ q.append (c->table (), c->column ());
+ break;
+ }
+ case part::kind_param_val:
+ case part::kind_param_ref:
+ {
+ const query_column_base* c (
+ static_cast<const query_column_base*> (
+ x.native_info[id_sqlite].column));
+
+ query_param_factory f (
+ reinterpret_cast<query_param_factory> (
+ x.native_info[id_sqlite].param_factory));
+
+ const odb::query_param* p (
+ reinterpret_cast<const odb::query_param*> (x.data));
+
+ q.append (f (p->value, x.kind == part::kind_param_ref),
+ c->conversion ());
+ break;
+ }
+ case part::kind_native:
+ {
+ q.append (s.strings ()[x.data]);
+ break;
+ }
+ case part::kind_true:
+ case part::kind_false:
+ {
+ q.append (x.kind == part::kind_true);
+ break;
+ }
+ case part::op_add:
+ {
+ translate (q, s, x.data);
+ translate (q, s, p - 1);
+ break;
+ }
+ case part::op_and:
+ case part::op_or:
+ {
+ q += "(";
+ translate (q, s, x.data);
+ q += logic_operators[x.kind - part::op_and];
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_not:
+ {
+ q += "NOT (";
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_null:
+ case part::op_not_null:
+ {
+ translate (q, s, p - 1);
+ q += (x.kind == part::op_null ? "IS NULL" : "IS NOT NULL");
+ break;
+ }
+ case part::op_in:
+ {
+ if (x.data != 0)
+ {
+ size_t b (p - x.data);
+
+ translate (q, s, b - 1); // column
+ q += "IN (";
+
+ for (size_t i (b); i != p; ++i)
+ {
+ if (i != b)
+ q += ",";
+
+ translate (q, s, i);
+ }
+
+ q += ")";
+ }
+ else
+ q.append (false);
+
+ break;
+ }
+ case part::op_like:
+ {
+ translate (q, s, p - 2); // column
+ q += "LIKE";
+ translate (q, s, p - 1); // pattern
+ break;
+ }
+ case part::op_like_escape:
+ {
+ translate (q, s, p - 3); // column
+ q += "LIKE";
+ translate (q, s, p - 2); // pattern
+ q += "ESCAPE";
+ translate (q, s, p - 1); // escape
+ break;
+ }
+ case part::op_eq:
+ case part::op_ne:
+ case part::op_lt:
+ case part::op_gt:
+ case part::op_le:
+ case part::op_ge:
+ {
+ translate (q, s, x.data);
+ q += comp_operators[x.kind - part::op_eq];
+ translate (q, s, p - 1);
+ break;
+ }
+ }
+ }
+
+ query_base::
+ query_base (const odb::query_base& q)
+ : parameters_ (new (details::shared) query_params)
+ {
+ if (!q.empty ())
+ translate (*this, q, q.clause ().size () - 1);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/query-dynamic.hxx b/libodb-sqlite/odb/sqlite/query-dynamic.hxx
new file mode 100644
index 0000000..f720a95
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query-dynamic.hxx
@@ -0,0 +1,32 @@
+// file : odb/sqlite/query-dynamic.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_QUERY_DYNAMIC_HXX
+#define ODB_SQLITE_QUERY_DYNAMIC_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/query.hxx>
+#include <odb/query-dynamic.hxx>
+
+#include <odb/sqlite/query.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ typedef details::shared_ptr<query_param> (*query_param_factory) (
+ const void* val, bool by_ref);
+
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void*, bool);
+ }
+}
+
+#include <odb/sqlite/query-dynamic.ixx>
+#include <odb/sqlite/query-dynamic.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_QUERY_DYNAMIC_HXX
diff --git a/libodb-sqlite/odb/sqlite/query-dynamic.ixx b/libodb-sqlite/odb/sqlite/query-dynamic.ixx
new file mode 100644
index 0000000..7fafe3e
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query-dynamic.ixx
@@ -0,0 +1,26 @@
+// file : odb/sqlite/query-dynamic.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ //
+ template <typename T, database_type_id ID>
+ inline query_column<T, ID>::
+ query_column (odb::query_column<T>& qc,
+ const char* table, const char* column, const char* conv)
+ : query_column_base (table, column, conv)
+ {
+ native_column_info& ci (qc.native_info[id_sqlite]);
+ ci.column = static_cast<query_column_base*> (this);
+
+ // For some reason GCC needs this statically-typed pointer in
+ // order to instantiate the functions.
+ //
+ query_param_factory f (&query_param_factory_impl<T, ID>);
+ ci.param_factory = reinterpret_cast<void*> (f);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/query-dynamic.txx b/libodb-sqlite/odb/sqlite/query-dynamic.txx
new file mode 100644
index 0000000..48b7ec4
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query-dynamic.txx
@@ -0,0 +1,20 @@
+// file : odb/sqlite/query-dynamic.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void* val, bool by_ref)
+ {
+ const T& v (*static_cast<const T*> (val));
+
+ return details::shared_ptr<query_param> (
+ by_ref
+ ? new (details::shared) query_param_impl<T, ID> (ref_bind<T> (v))
+ : new (details::shared) query_param_impl<T, ID> (val_bind<T> (v)));
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/query.cxx b/libodb-sqlite/odb/sqlite/query.cxx
new file mode 100644
index 0000000..98eb1cd
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query.cxx
@@ -0,0 +1,378 @@
+// file : odb/sqlite/query.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+#include <odb/sqlite/query.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // query_param
+ //
+
+ query_param::
+ ~query_param ()
+ {
+ }
+
+ // query_params
+ //
+
+ query_params::
+ query_params (const query_params& x)
+ : details::shared_base (x),
+ params_ (x.params_), bind_ (x.bind_), binding_ (0, 0)
+ {
+ // Here and below we want to maintain up to date binding info so
+ // that the call to binding() below is an immutable operation,
+ // provided the query does not have any by-reference parameters.
+ // This way a by-value-only query can be shared between multiple
+ // threads without the need for synchronization.
+ //
+ if (size_t n = bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = n;
+ binding_.version++;
+ }
+ }
+
+ query_params& query_params::
+ operator= (const query_params& x)
+ {
+ if (this != &x)
+ {
+ params_ = x.params_;
+ bind_ = x.bind_;
+
+ size_t n (bind_.size ());
+ binding_.bind = n != 0 ? &bind_[0] : 0;
+ binding_.count = n;
+ binding_.version++;
+ }
+
+ return *this;
+ }
+
+ query_params& query_params::
+ operator+= (const query_params& x)
+ {
+ size_t n (bind_.size ());
+
+ params_.insert (params_.end (), x.params_.begin (), x.params_.end ());
+ bind_.insert (bind_.end (), x.bind_.begin (), x.bind_.end ());
+
+ if (n != bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+ }
+
+ return *this;
+ }
+
+ void query_params::
+ add (details::shared_ptr<query_param> p)
+ {
+ params_.push_back (p);
+ bind_.push_back (sqlite::bind ());
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+
+ sqlite::bind* b (&bind_.back ());
+ memset (b, 0, sizeof (sqlite::bind));
+ p->bind (b);
+ }
+
+ void query_params::
+ init ()
+ {
+ bool inc_ver (false);
+
+ for (size_t i (0); i < params_.size (); ++i)
+ {
+ query_param& p (*params_[i]);
+
+ if (p.reference ())
+ {
+ if (p.init ())
+ {
+ p.bind (&bind_[i]);
+ inc_ver = true;
+ }
+ }
+ }
+
+ if (inc_ver)
+ binding_.version++;
+ }
+
+ // query_base
+ //
+
+ query_base::
+ query_base (const query_base& q)
+ : clause_ (q.clause_),
+ parameters_ (new (details::shared) query_params (*q.parameters_))
+ {
+ }
+
+ query_base& query_base::
+ operator= (const query_base& q)
+ {
+ if (this != &q)
+ {
+ clause_ = q.clause_;
+ *parameters_ = *q.parameters_;
+ }
+
+ return *this;
+ }
+
+ query_base& query_base::
+ operator+= (const query_base& q)
+ {
+ clause_.insert (clause_.end (), q.clause_.begin (), q.clause_.end ());
+ *parameters_ += *q.parameters_;
+ return *this;
+ }
+
+ void query_base::
+ append (const string& q)
+ {
+ if (!clause_.empty () &&
+ clause_.back ().kind == clause_part::kind_native)
+ {
+ string& s (clause_.back ().part);
+
+ char first (!q.empty () ? q[0] : ' ');
+ char last (!s.empty () ? s[s.size () - 1] : ' ');
+
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ s += ' ';
+
+ s += q;
+ }
+ else
+ clause_.push_back (clause_part (clause_part::kind_native, q));
+ }
+
+ void query_base::
+ append (const char* table, const char* column)
+ {
+ string s (table);
+ s += '.';
+ s += column;
+
+ clause_.push_back (clause_part (clause_part::kind_column, s));
+ }
+
+ void query_base::
+ append (details::shared_ptr<query_param> p, const char* conv)
+ {
+ clause_.push_back (clause_part (clause_part::kind_param));
+
+ if (conv != 0)
+ clause_.back ().part = conv;
+
+ parameters_->add (p);
+ }
+
+ static bool
+ check_prefix (const string& s)
+ {
+ string::size_type n;
+
+ // It is easier to compare to upper and lower-case versions
+ // rather than getting involved with the portable case-
+ // insensitive string comparison mess.
+ //
+ if (s.compare (0, (n = 5), "WHERE") == 0 ||
+ s.compare (0, (n = 5), "where") == 0 ||
+ s.compare (0, (n = 6), "SELECT") == 0 ||
+ s.compare (0, (n = 6), "select") == 0 ||
+ s.compare (0, (n = 8), "ORDER BY") == 0 ||
+ s.compare (0, (n = 8), "order by") == 0 ||
+ s.compare (0, (n = 8), "GROUP BY") == 0 ||
+ s.compare (0, (n = 8), "group by") == 0 ||
+ s.compare (0, (n = 6), "HAVING") == 0 ||
+ s.compare (0, (n = 6), "having") == 0 ||
+ s.compare (0, (n = 4), "WITH") == 0 ||
+ s.compare (0, (n = 4), "with") == 0 ||
+ s.compare (0, (n = 6), "PRAGMA") == 0 ||
+ s.compare (0, (n = 6), "pragma") == 0)
+ {
+ // It either has to be an exact match, or there should be
+ // a whitespace following the keyword.
+ //
+ if (s.size () == n || s[n] == ' ' || s[n] == '\n' || s[n] =='\t')
+ return true;
+ }
+
+ return false;
+ }
+
+ void query_base::
+ optimize ()
+ {
+ // Remove a single TRUE literal or one that is followe by one of
+ // the other clauses. This avoids useless WHERE clauses like
+ //
+ // WHERE TRUE GROUP BY foo
+ //
+ clause_type::iterator i (clause_.begin ()), e (clause_.end ());
+
+ if (i != e && i->kind == clause_part::kind_bool && i->bool_part)
+ {
+ clause_type::iterator j (i + 1);
+
+ if (j == e ||
+ (j->kind == clause_part::kind_native && check_prefix (j->part)))
+ clause_.erase (i);
+ }
+ }
+
+ const char* query_base::
+ clause_prefix () const
+ {
+ if (!clause_.empty ())
+ {
+ const clause_part& p (clause_.front ());
+
+ if (p.kind == clause_part::kind_native && check_prefix (p.part))
+ return "";
+
+ return "WHERE ";
+ }
+
+ return "";
+ }
+
+ string query_base::
+ clause () const
+ {
+ string r;
+
+ for (clause_type::const_iterator i (clause_.begin ()),
+ end (clause_.end ());
+ i != end;
+ ++i)
+ {
+ char last (!r.empty () ? r[r.size () - 1] : ' ');
+
+ switch (i->kind)
+ {
+ case clause_part::kind_column:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ r += i->part;
+ break;
+ }
+ case clause_part::kind_param:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ // Add the conversion expression, if any.
+ //
+ string::size_type p (0);
+ if (!i->part.empty ())
+ {
+ p = i->part.find ("(?)");
+ r.append (i->part, 0, p);
+ }
+
+ r += '?';
+
+ if (!i->part.empty ())
+ r.append (i->part, p + 3, string::npos);
+
+ break;
+ }
+ case clause_part::kind_native:
+ {
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ const string& p (i->part);
+ char first (!p.empty () ? p[0] : ' ');
+
+ if (last != ' ' && first != '\n' && last != '(' &&
+ first != ' ' && last != '\n' && first != ',' && first != ')')
+ r += ' ';
+
+ r += p;
+ break;
+ }
+ case clause_part::kind_bool:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ r += i->bool_part ? "1" : "0";
+ break;
+ }
+ }
+ }
+
+ return clause_prefix () + r;
+ }
+
+ query_base
+ operator&& (const query_base& x, const query_base& y)
+ {
+ // Optimize cases where one or both sides are constant truth.
+ //
+ bool xt (x.const_true ()), yt (y.const_true ());
+
+ if (xt && yt)
+ return x;
+
+ if (xt)
+ return y;
+
+ if (yt)
+ return x;
+
+ query_base r ("(");
+ r += x;
+ r += ") AND (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator|| (const query_base& x, const query_base& y)
+ {
+ query_base r ("(");
+ r += x;
+ r += ") OR (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator! (const query_base& x)
+ {
+ query_base r ("NOT (");
+ r += x;
+ r += ")";
+ return r;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/query.hxx b/libodb-sqlite/odb/sqlite/query.hxx
new file mode 100644
index 0000000..c9cbfaa
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query.hxx
@@ -0,0 +1,1728 @@
+// file : odb/sqlite/query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_QUERY_HXX
+#define ODB_SQLITE_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx> // odb::query_column
+#include <odb/query.hxx>
+#include <odb/details/buffer.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/traits.hxx>
+#include <odb/sqlite/sqlite-types.hxx>
+#include <odb/sqlite/binding.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+#include <odb/sqlite/details/conversion.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ struct val_bind
+ {
+ typedef const T& type;
+
+ explicit
+ val_bind (type v): val (v) {}
+
+ type val;
+ };
+
+ template <typename T, std::size_t N>
+ struct val_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ val_bind (type v): val (v) {}
+
+ type val;
+ };
+
+ template <typename T>
+ struct ref_bind
+ {
+ typedef const T& type;
+
+ explicit
+ ref_bind (type r): ref (r) {}
+
+ const void*
+ ptr () const {return &ref;}
+
+ type ref;
+ };
+
+ template <typename T, std::size_t N>
+ struct ref_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ ref_bind (type r): ref (r) {}
+
+ // Allow implicit conversion from decayed ref_bind's.
+ //
+ ref_bind (ref_bind<T*> r): ref (r.ref) {}
+ ref_bind (ref_bind<const T*> r): ref (r.ref) {}
+
+ const void*
+ ptr () const {return ref;}
+
+ type ref;
+ };
+
+ template <typename T, database_type_id ID>
+ struct val_bind_typed: val_bind<T>
+ {
+ explicit
+ val_bind_typed (typename val_bind<T>::type v): val_bind<T> (v) {}
+ };
+
+ template <typename T, database_type_id ID>
+ struct ref_bind_typed: ref_bind<T>
+ {
+ explicit
+ ref_bind_typed (typename ref_bind<T>::type r): ref_bind<T> (r) {}
+ };
+
+ struct LIBODB_SQLITE_EXPORT query_param: details::shared_base
+ {
+ virtual
+ ~query_param ();
+
+ bool
+ reference () const
+ {
+ return value_ != 0;
+ }
+
+ virtual bool
+ init () = 0;
+
+ virtual void
+ bind (sqlite::bind*) = 0;
+
+ protected:
+ query_param (const void* value)
+ : value_ (value)
+ {
+ }
+
+ protected:
+ const void* value_;
+ };
+
+ class query_base;
+
+ class LIBODB_SQLITE_EXPORT query_params: public details::shared_base
+ {
+ public:
+ typedef sqlite::binding binding_type;
+
+ void
+ init ();
+
+ binding_type&
+ binding () {return binding_;}
+
+ private:
+ friend class query_base;
+
+ query_params (): binding_ (0, 0) {}
+ query_params (const query_params&);
+
+ query_params&
+ operator= (const query_params&);
+
+ query_params&
+ operator+= (const query_params&);
+
+ void
+ add (details::shared_ptr<query_param>);
+
+ private:
+ typedef std::vector<details::shared_ptr<query_param> > params;
+
+ params params_;
+ std::vector<sqlite::bind> bind_;
+ binding_type binding_;
+ };
+
+ //
+ //
+ template <typename T, database_type_id ID>
+ struct query_column;
+
+ class LIBODB_SQLITE_EXPORT query_base
+ {
+ public:
+ struct clause_part
+ {
+ enum kind_type
+ {
+ kind_column,
+ kind_param,
+ kind_native,
+ kind_bool
+ };
+
+ clause_part (kind_type k): kind (k), bool_part (false) {}
+ clause_part (kind_type k, const std::string& p)
+ : kind (k), part (p), bool_part (false) {}
+ clause_part (bool p): kind (kind_bool), bool_part (p) {}
+
+ kind_type kind;
+ std::string part; // If kind is param, then part is conversion expr.
+ bool bool_part;
+ };
+
+ query_base ()
+ : parameters_ (new (details::shared) query_params)
+ {
+ }
+
+ // True or false literal.
+ //
+ explicit
+ query_base (bool v)
+ : parameters_ (new (details::shared) query_params)
+ {
+ append (v);
+ }
+
+ explicit
+ query_base (const char* native)
+ : parameters_ (new (details::shared) query_params)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ explicit
+ query_base (const std::string& native)
+ : parameters_ (new (details::shared) query_params)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ query_base (const char* table, const char* column)
+ : parameters_ (new (details::shared) query_params)
+ {
+ append (table, column);
+ }
+
+ template <typename T>
+ explicit
+ query_base (val_bind<T> v)
+ : parameters_ (new (details::shared) query_params)
+ {
+ *this += v;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (val_bind_typed<T, ID> v)
+ : parameters_ (new (details::shared) query_params)
+ {
+ *this += v;
+ }
+
+ template <typename T>
+ explicit
+ query_base (ref_bind<T> r)
+ : parameters_ (new (details::shared) query_params)
+ {
+ *this += r;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (ref_bind_typed<T, ID> r)
+ : parameters_ (new (details::shared) query_params)
+ {
+ *this += r;
+ }
+
+ template <database_type_id ID>
+ query_base (const query_column<bool, ID>&);
+
+ // Translate common query representation to SQLite native. Defined
+ // in query-dynamic.cxx
+ //
+ query_base (const odb::query_base&);
+
+ // Copy c-tor and assignment.
+ //
+ query_base (const query_base&);
+
+ query_base&
+ operator= (const query_base&);
+
+ public:
+ std::string
+ clause () const;
+
+ const char*
+ clause_prefix () const;
+
+ // Initialize the by-reference parameters from bound variables.
+ //
+ void
+ init_parameters () const;
+
+ binding&
+ parameters_binding () const;
+
+ const details::shared_ptr<query_params>&
+ parameters () const;
+
+ public:
+ bool
+ empty () const
+ {
+ return clause_.empty ();
+ }
+
+ static const query_base true_expr;
+
+ bool
+ const_true () const
+ {
+ return clause_.size () == 1 &&
+ clause_.front ().kind == clause_part::kind_bool &&
+ clause_.front ().bool_part;
+ }
+
+ void
+ optimize ();
+
+ public:
+ template <typename T>
+ static val_bind<T>
+ _val (const T& x)
+ {
+ return val_bind<T> (x);
+ }
+
+ template <database_type_id ID, typename T>
+ static val_bind_typed<T, ID>
+ _val (const T& x)
+ {
+ return val_bind_typed<T, ID> (x);
+ }
+
+ template <typename T>
+ static ref_bind<T>
+ _ref (const T& x)
+ {
+ return ref_bind<T> (x);
+ }
+
+ template <database_type_id ID, typename T>
+ static ref_bind_typed<T, ID>
+ _ref (const T& x)
+ {
+ return ref_bind_typed<T, ID> (x);
+ }
+
+ // Some compilers (notably VC++), when deducing const T& from const
+ // array do not strip const from the array type. As a result, in the
+ // above signatures we get, for example, T = const char[4] instead
+ // of T = char[4], which is what we want. So to "fix" such compilers,
+ // we will have to provide the following specializations of the above
+ // functions.
+ //
+ template <typename T, std::size_t N>
+ static val_bind<T[N]>
+ _val (const T (&x) [N])
+ {
+ return val_bind<T[N]> (x);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static val_bind_typed<T[N], ID>
+ _val (const T (&x) [N])
+ {
+ return val_bind_typed<T[N], ID> (x);
+ }
+
+ template <typename T, std::size_t N>
+ static ref_bind<T[N]>
+ _ref (const T (&x) [N])
+ {
+ return ref_bind<T[N]> (x);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static ref_bind_typed<T[N], ID>
+ _ref (const T (&x) [N])
+ {
+ return ref_bind_typed<T[N], ID> (x);
+ }
+
+ public:
+ query_base&
+ operator+= (const query_base&);
+
+ query_base&
+ operator+= (const std::string& q)
+ {
+ append (q);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (val_bind<T> v)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ v, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (val_bind_typed<T, ID> v)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (v, 0);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (ref_bind<T> r)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ r, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (ref_bind_typed<T, ID> r)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (r, 0);
+ return *this;
+ }
+
+ // Implementation details.
+ //
+ public:
+ template <typename T, database_type_id ID>
+ void
+ append (val_bind<T>, const char* conv);
+
+ template <typename T, database_type_id ID>
+ void
+ append (ref_bind<T>, const char* conv);
+
+ void
+ append (details::shared_ptr<query_param>, const char* conv);
+
+ void
+ append (bool v)
+ {
+ clause_.push_back (clause_part (v));
+ }
+
+ void
+ append (const std::string& native);
+
+ void
+ append (const char* native) // Clashes with append(bool).
+ {
+ append (std::string (native));
+ }
+
+ void
+ append (const char* table, const char* column);
+
+ private:
+ typedef std::vector<clause_part> clause_type;
+
+ clause_type clause_;
+ details::shared_ptr<query_params> parameters_;
+ };
+
+ inline query_base
+ operator+ (const query_base& x, const query_base& y)
+ {
+ query_base r (x);
+ r += y;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, val_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, val_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, ref_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, ref_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const query_base& q, const std::string& s)
+ {
+ query_base r (q);
+ r += s;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const std::string& s, const query_base& q)
+ {
+ query_base r (s);
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, val_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, val_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, ref_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, ref_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ LIBODB_SQLITE_EXPORT query_base
+ operator&& (const query_base&, const query_base&);
+
+ LIBODB_SQLITE_EXPORT query_base
+ operator|| (const query_base&, const query_base&);
+
+ LIBODB_SQLITE_EXPORT query_base
+ operator! (const query_base&);
+
+ // query_column
+ //
+ struct query_column_base
+ {
+ // Note that we keep shallow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column_base (const char* table,
+ const char* column,
+ const char* conv)
+ : table_ (table), column_ (column), conversion_ (conv)
+ {
+ }
+
+ const char*
+ table () const
+ {
+ return table_;
+ }
+
+ const char*
+ column () const
+ {
+ return column_;
+ }
+
+ // Can be NULL.
+ //
+ const char*
+ conversion () const
+ {
+ return conversion_;
+ }
+
+ protected:
+ const char* table_;
+ const char* column_;
+ const char* conversion_;
+ };
+
+ template <typename T, database_type_id ID>
+ struct query_column: query_column_base
+ {
+ typedef typename decay_traits<T>::type decayed_type;
+
+ // Note that we keep shallow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column (const char* table, const char* column, const char* conv)
+ : query_column_base (table, column, conv) {}
+
+ // Implementation is in query-dynamic.ixx.
+ //
+ query_column (odb::query_column<T>&,
+ const char* table, const char* column, const char* conv);
+
+ // is_null, is_not_null
+ //
+ public:
+ query_base
+ is_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NULL";
+ return q;
+ }
+
+ query_base
+ is_not_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NOT NULL";
+ return q;
+ }
+
+ // in
+ //
+ public:
+ query_base
+ in (decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type,
+ decayed_type) const;
+
+ template <typename I>
+ query_base
+ in_range (I begin, I end) const;
+
+ // like
+ //
+ public:
+ query_base
+ like (decayed_type pattern) const
+ {
+ return like (val_bind<T> (pattern));
+ }
+
+ query_base
+ like (val_bind<T> pattern) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)));
+ }
+
+ query_base
+ like (ref_bind<T> pattern) const;
+
+ query_base
+ like (decayed_type pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (pattern), escape);
+ }
+
+ query_base
+ like (val_bind<T> pattern, decayed_type escape) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)), escape);
+ }
+
+ query_base
+ like (ref_bind<T> pattern, decayed_type escape) const;
+
+ // =
+ //
+ public:
+ query_base
+ equal (decayed_type v) const
+ {
+ return equal (val_bind<T> (v));
+ }
+
+ query_base
+ equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ equal (val_bind<T2> v) const
+ {
+ return equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator== (const query_column& c, decayed_type v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (decayed_type v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, val_bind<T> v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (val_bind<T> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (const query_column& c, val_bind<T2> v)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (val_bind<T2> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, ref_bind<T> r)
+ {
+ return c.equal (r);
+ }
+
+ friend query_base
+ operator== (ref_bind<T> r, const query_column& c)
+ {
+ return c.equal (r);
+ }
+
+ // !=
+ //
+ public:
+ query_base
+ unequal (decayed_type v) const
+ {
+ return unequal (val_bind<T> (v));
+ }
+
+ query_base
+ unequal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ unequal (val_bind<T2> v) const
+ {
+ return unequal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ unequal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator!= (const query_column& c, decayed_type v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (decayed_type v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, val_bind<T> v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (val_bind<T> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (const query_column& c, val_bind<T2> v)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (val_bind<T2> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, ref_bind<T> r)
+ {
+ return c.unequal (r);
+ }
+
+ friend query_base
+ operator!= (ref_bind<T> r, const query_column& c)
+ {
+ return c.unequal (r);
+ }
+
+ // <
+ //
+ public:
+ query_base
+ less (decayed_type v) const
+ {
+ return less (val_bind<T> (v));
+ }
+
+ query_base
+ less (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less (val_bind<T2> v) const
+ {
+ return less (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator< (const query_column& c, decayed_type v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (decayed_type v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, val_bind<T> v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (val_bind<T> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (const query_column& c, val_bind<T2> v)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, ref_bind<T> r)
+ {
+ return c.less (r);
+ }
+
+ friend query_base
+ operator< (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater (r);
+ }
+
+ // >
+ //
+ public:
+ query_base
+ greater (decayed_type v) const
+ {
+ return greater (val_bind<T> (v));
+ }
+
+ query_base
+ greater (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater (val_bind<T2> v) const
+ {
+ return greater (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator> (const query_column& c, decayed_type v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (decayed_type v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, val_bind<T> v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (val_bind<T> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (val_bind<T2> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater (r);
+ }
+
+ friend query_base
+ operator> (ref_bind<T> r, const query_column& c)
+ {
+ return c.less (r);
+ }
+
+ // <=
+ //
+ public:
+ query_base
+ less_equal (decayed_type v) const
+ {
+ return less_equal (val_bind<T> (v));
+ }
+
+ query_base
+ less_equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less_equal (val_bind<T2> v) const
+ {
+ return less_equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less_equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator<= (const query_column& c, decayed_type v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (decayed_type v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, val_bind<T> v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (val_bind<T> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (const query_column& c, val_bind<T2> v)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, ref_bind<T> r)
+ {
+ return c.less_equal (r);
+ }
+
+ friend query_base
+ operator<= (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater_equal (r);
+ }
+
+ // >=
+ //
+ public:
+ query_base
+ greater_equal (decayed_type v) const
+ {
+ return greater_equal (val_bind<T> (v));
+ }
+
+ query_base
+ greater_equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater_equal (val_bind<T2> v) const
+ {
+ return greater_equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater_equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator>= (const query_column& c, decayed_type v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (decayed_type v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, val_bind<T> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (val_bind<T> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (val_bind<T2> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater_equal (r);
+ }
+
+ friend query_base
+ operator>= (ref_bind<T> r, const query_column& c)
+ {
+ return c.less_equal (r);
+ }
+
+ // Column comparison.
+ //
+ public:
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator== (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () ==
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator!= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () !=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "!=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator< (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator> (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator<= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator>= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+ };
+
+ // Provide operator+() for using columns to construct native
+ // query fragments (e.g., ORDER BY).
+ //
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const std::string& s)
+ {
+ query_base q (c.table (), c.column ());
+ q += s;
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, const query_column<T, ID>& c)
+ {
+ query_base q (s);
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const query_base& q)
+ {
+ query_base r (c.table (), c.column ());
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, const query_column<T, ID>& c)
+ {
+ query_base r (q);
+ r.append (c.table (), c.column ());
+ return r;
+ }
+
+ //
+ //
+ template <typename T, database_type_id>
+ struct query_param_impl;
+
+ // INTEGER
+ //
+ template <typename T>
+ struct query_param_impl<T, id_integer>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (sqlite::bind* b)
+ {
+ b->type = sqlite::bind::integer;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_integer>::set_image (image_, is_null, v);
+ }
+
+ private:
+ long long image_;
+ };
+
+ // REAL
+ //
+ template <typename T>
+ struct query_param_impl<T, id_real>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (sqlite::bind* b)
+ {
+ b->type = sqlite::bind::real;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_real>::set_image (image_, is_null, v);
+ }
+
+ private:
+ double image_;
+ };
+
+ // TEXT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_text>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (sqlite::bind* b)
+ {
+ b->type = image_traits<T, id_text>::bind_value;
+ b->buffer = buffer_.data ();
+ b->size = &size_;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t cap (buffer_.capacity ());
+ value_traits<T, id_text>::set_image (buffer_, size_, is_null, v);
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ std::size_t size_;
+ };
+
+ // BLOB
+ //
+ template <typename T>
+ struct query_param_impl<T, id_blob>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (sqlite::bind* b)
+ {
+ b->type = sqlite::bind::blob;
+ b->buffer = buffer_.data ();
+ b->size = &size_;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t cap (buffer_.capacity ());
+ value_traits<T, id_blob>::set_image (buffer_, size_, is_null, v);
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ std::size_t size_;
+ };
+
+ // TEXT STREAM (reduce to id_text).
+ //
+ template <typename T>
+ struct query_param_impl<T, id_text_stream>: query_param_impl<T, id_text>
+ {
+ query_param_impl (ref_bind<T> r) : query_param_impl<T, id_text> (r) {}
+ query_param_impl (val_bind<T> v) : query_param_impl<T, id_text> (v) {}
+ };
+
+ // BLOB STREAM (reduce to id_blob).
+ //
+ template <typename T>
+ struct query_param_impl<T, id_blob_stream>: query_param_impl<T, id_blob>
+ {
+ query_param_impl (ref_bind<T> r) : query_param_impl<T, id_blob> (r) {}
+ query_param_impl (val_bind<T> v) : query_param_impl<T, id_blob> (v) {}
+ };
+ }
+}
+
+// odb::sqlite::query and odb::query specialization for SQLite.
+//
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ class query: public query_base,
+ public query_selector<T, id_sqlite>::columns_type
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : query_base (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : query_base (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : query_base (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (val_bind<T2> v)
+ : query_base (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (ref_bind<T2> r)
+ : query_base (r)
+ {
+ }
+
+ query (const query_base& q)
+ : query_base (q)
+ {
+ }
+
+ template <database_type_id ID>
+ query (const query_column<bool, ID>& qc)
+ : query_base (qc)
+ {
+ }
+
+ query (const odb::query_base& q)
+ : query_base (q)
+ {
+ }
+ };
+
+ namespace core
+ {
+ using sqlite::query;
+ }
+ }
+
+ // Derive odb::query from odb::sqlite::query so that it can be
+ // implicitly converted in sqlite::database::query() calls.
+ //
+ template <typename T>
+ class query<T, sqlite::query_base>: public sqlite::query<T>
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : sqlite::query<T> (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : sqlite::query<T> (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : sqlite::query<T> (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (sqlite::val_bind<T2> v)
+ : sqlite::query<T> (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (sqlite::ref_bind<T2> r)
+ : sqlite::query<T> (r)
+ {
+ }
+
+ query (const sqlite::query_base& q)
+ : sqlite::query<T> (q)
+ {
+ }
+
+ template <sqlite::database_type_id ID>
+ query (const sqlite::query_column<bool, ID>& qc)
+ : sqlite::query<T> (qc)
+ {
+ }
+ };
+}
+
+#include <odb/sqlite/query.ixx>
+#include <odb/sqlite/query.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_QUERY_HXX
diff --git a/libodb-sqlite/odb/sqlite/query.ixx b/libodb-sqlite/odb/sqlite/query.ixx
new file mode 100644
index 0000000..00e9b66
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query.ixx
@@ -0,0 +1,46 @@
+// file : odb/sqlite/query.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace sqlite
+ {
+ inline void query_base::
+ init_parameters () const
+ {
+ return parameters_->init ();
+ }
+
+ inline binding& query_base::
+ parameters_binding () const
+ {
+ return parameters_->binding ();
+ }
+
+ inline const details::shared_ptr<query_params>& query_base::
+ parameters () const
+ {
+ return parameters_;
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (val_bind<T> v, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (v)),
+ conv);
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (ref_bind<T> r, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (r)),
+ conv);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/query.txx b/libodb-sqlite/odb/sqlite/query.txx
new file mode 100644
index 0000000..f381ff0
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/query.txx
@@ -0,0 +1,168 @@
+// file : odb/sqlite/query.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // query_base
+ //
+
+ template <database_type_id ID>
+ query_base::
+ query_base (const query_column<bool, ID>& c)
+ : parameters_ (new (details::shared) query_params)
+ {
+ // Cannot use IS TRUE here since database type can be a non-
+ // integral type.
+ //
+ append (c.table (), c.column ());
+ append ("=");
+ append<bool, ID> (val_bind<bool> (true), c.conversion ());
+ }
+
+ //
+ // query_column
+ //
+
+ // in
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3,
+ decayed_type v4) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3, decayed_type v4,
+ decayed_type v5) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v5), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ template <typename I>
+ query_base query_column<T, ID>::
+ in_range (I begin, I end) const
+ {
+ if (begin != end)
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+
+ for (I i (begin); i != end; ++i)
+ {
+ if (i != begin)
+ q += ",";
+
+ q.append<T, ID> (val_bind<T> (*i), conversion_);
+ }
+
+ q += ")";
+ return q;
+ }
+ else
+ return query_base (false);
+ }
+
+ // like
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/section-statements.hxx b/libodb-sqlite/odb/sqlite/section-statements.hxx
new file mode 100644
index 0000000..e6a5da6
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/section-statements.hxx
@@ -0,0 +1,198 @@
+// file : odb/sqlite/section-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_SECTION_STATEMENTS_HXX
+#define ODB_SQLITE_SECTION_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/sqlite-types.hxx>
+#include <odb/sqlite/binding.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class connection;
+
+ // Template argument is the section traits type.
+ //
+ template <typename T, typename ST>
+ class section_statements
+ {
+ public:
+ typedef ST traits;
+
+ typedef typename traits::image_type image_type;
+ typedef typename traits::id_image_type id_image_type;
+
+ typedef sqlite::select_statement select_statement_type;
+ typedef sqlite::update_statement update_statement_type;
+
+ typedef sqlite::connection connection_type;
+
+ section_statements (connection_type&,
+ image_type&, id_image_type&,
+ binding& id, binding& idv);
+
+ connection_type&
+ connection () {return conn_;}
+
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ image_type&
+ image () {return image_;}
+
+ const binding&
+ id_binding () {return id_binding_;}
+
+ // Id and optimistic concurrency version (if any).
+ //
+ const binding&
+ idv_binding () {return idv_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ //
+ // Statements.
+ //
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ traits::select_statement,
+ traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_binding_,
+ select_image_binding_));
+
+ return *select_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ traits::update_statement,
+ traits::versioned, // Process if versioned.
+ update_image_binding_));
+
+ return *update_;
+ }
+
+ public:
+ static const std::size_t id_column_count = traits::id_column_count;
+ static const std::size_t managed_optimistic_load_column_count =
+ traits::managed_optimistic_load_column_count;
+ static const std::size_t managed_optimistic_update_column_count =
+ traits::managed_optimistic_update_column_count;
+ static const std::size_t select_column_count = traits::load_column_count;
+ static const std::size_t update_column_count =
+ traits::update_column_count;
+
+ private:
+ section_statements (const section_statements&);
+ section_statements& operator= (const section_statements&);
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+
+ // These come from object_statements.
+ //
+ image_type& image_;
+ binding& id_binding_;
+ binding& idv_binding_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+
+ static const std::size_t select_bind_count =
+ select_column_count != 0 || managed_optimistic_load_column_count != 0
+ ? select_column_count + managed_optimistic_load_column_count
+ : 1;
+
+ binding select_image_binding_;
+ bind select_image_bind_[select_bind_count];
+ bool select_image_truncated_[select_bind_count];
+
+ // Update binding.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+
+ static const std::size_t update_bind_count =
+ update_column_count != 0 || managed_optimistic_update_column_count != 0
+ ? update_column_count + id_column_count +
+ managed_optimistic_update_column_count
+ : 1;
+
+ binding update_image_binding_;
+ bind update_image_bind_[update_bind_count];
+
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<update_statement_type> update_;
+ };
+ }
+}
+
+#include <odb/sqlite/section-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_SECTION_STATEMENTS_HXX
diff --git a/libodb-sqlite/odb/sqlite/section-statements.txx b/libodb-sqlite/odb/sqlite/section-statements.txx
new file mode 100644
index 0000000..ff588b3
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/section-statements.txx
@@ -0,0 +1,40 @@
+// file : odb/sqlite/section-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T, typename ST>
+ section_statements<T, ST>::
+ section_statements (connection_type& conn,
+ image_type& im, id_image_type&,
+ binding& id, binding& idv)
+ : conn_ (conn),
+ svm_ (0),
+ image_ (im),
+ id_binding_ (id),
+ idv_binding_ (idv),
+ select_image_binding_ (select_image_bind_,
+ select_column_count +
+ managed_optimistic_load_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_update_column_count)
+ {
+ select_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+ for (std::size_t i (0); i < select_bind_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/simple-object-result.hxx b/libodb-sqlite/odb/sqlite/simple-object-result.hxx
new file mode 100644
index 0000000..d68af8e
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/simple-object-result.hxx
@@ -0,0 +1,88 @@
+// file : odb/sqlite/simple-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_SIMPLE_OBJECT_RESULT_HXX
+#define ODB_SQLITE_SIMPLE_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/simple-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx> // query_base, query_params
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/traits-calls.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ class object_result_impl: public odb::object_result_impl<T>
+ {
+ public:
+ typedef odb::object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_sqlite> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~object_result_impl ();
+
+ object_result_impl (const query_base&,
+ const details::shared_ptr<select_statement>&,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ load_image ();
+
+ private:
+ // We need to hold on to the query parameters because SQLite uses
+ // the parameter buffers to find each next row.
+ //
+ details::shared_ptr<query_params> params_;
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ };
+ }
+}
+
+#include <odb/sqlite/simple-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_SIMPLE_OBJECT_RESULT_HXX
diff --git a/libodb-sqlite/odb/sqlite/simple-object-result.txx b/libodb-sqlite/odb/sqlite/simple-object-result.txx
new file mode 100644
index 0000000..f27b226
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/simple-object-result.txx
@@ -0,0 +1,158 @@
+// file : odb/sqlite/simple-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/sqlite/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ object_result_impl<T>::
+ ~object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ params_.reset ();
+ statement_.reset ();
+ }
+
+ template <typename T>
+ object_result_impl<T>::
+ object_result_impl (const query_base& q,
+ const details::shared_ptr<select_statement>& s,
+ statements_type& sts,
+ const schema_version_migration* svm)
+ : base_type (sts.connection ()),
+ params_ (q.parameters ()),
+ statement_ (s),
+ statements_ (sts),
+ tc_ (svm)
+ {
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ load (object_type& obj, bool fetch)
+ {
+ if (fetch)
+ load_image ();
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!statements_.locked ());
+ typename statements_type::auto_lock l (statements_);
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+
+ typename object_traits::image_type& i (statements_.image ());
+ tc_.init (obj, i, &this->db_);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ object_traits::init (idi, object_traits::id (i));
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, obj, false);
+ statements_.load_delayed (tc_.version ());
+ l.unlock ();
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ typename object_result_impl<T>::id_type
+ object_result_impl<T>::
+ load_id ()
+ {
+ load_image ();
+ return object_traits::id (statements_.image ());
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (!statement_->next ())
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ load_image ()
+ {
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ statement_->reload ();
+ }
+ }
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t object_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/simple-object-statements.cxx b/libodb-sqlite/odb/sqlite/simple-object-statements.cxx
new file mode 100644
index 0000000..1eb07db
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/simple-object-statements.cxx
@@ -0,0 +1,15 @@
+// file : odb/sqlite/simple-object-statements.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ object_statements_base::
+ ~object_statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/simple-object-statements.hxx b/libodb-sqlite/odb/sqlite/simple-object-statements.hxx
new file mode 100644
index 0000000..b60fe6c
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/simple-object-statements.hxx
@@ -0,0 +1,591 @@
+// file : odb/sqlite/simple-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_SIMPLE_OBJECT_STATEMENTS_HXX
+#define ODB_SQLITE_SIMPLE_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cassert>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/sqlite-types.hxx>
+#include <odb/sqlite/binding.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/statements-base.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // The extra_statement_cache class is only defined (and used) in
+ // the generated source file. However, object_statements may be
+ // referenced from another source file in the case of a polymorphic
+ // hierarchy (though in this case the extra statement cache is
+ // not used). As a result, we cannot have a by-value member and
+ // instead will store a pointer and lazily allocate the cache if
+ // and when needed. We will also need to store a pointer to the
+ // deleter function which will be initialized during allocation
+ // (at that point we know that the cache class is defined).
+ //
+ template <typename T, typename I, typename ID>
+ struct extra_statement_cache_ptr
+ {
+ typedef I image_type;
+ typedef ID id_image_type;
+ typedef sqlite::connection connection_type;
+
+ extra_statement_cache_ptr (): p_ (0) {}
+ ~extra_statement_cache_ptr ()
+ {
+ if (p_ != 0)
+ (this->*deleter_) (0, 0, 0, 0, 0);
+ }
+
+ T&
+ get (connection_type& c,
+ image_type& im, id_image_type& idim,
+ binding& id, binding* idv)
+ {
+ if (p_ == 0)
+ allocate (&c, &im, &idim, &id, (idv != 0 ? idv : &id));
+
+ return *p_;
+ }
+
+ private:
+ void
+ allocate (connection_type*,
+ image_type*, id_image_type*,
+ binding*, binding*);
+
+ private:
+ T* p_;
+ void (extra_statement_cache_ptr::*deleter_) (
+ connection_type*, image_type*, id_image_type*, binding*, binding*);
+ };
+
+ template <typename T, typename I, typename ID>
+ void extra_statement_cache_ptr<T, I, ID>::
+ allocate (connection_type* c,
+ image_type* im, id_image_type* idim,
+ binding* id, binding* idv)
+ {
+ // To reduce object code size, this function acts as both allocator
+ // and deleter.
+ //
+ if (p_ == 0)
+ {
+ p_ = new T (*c, *im, *idim, *id, *idv);
+ deleter_ = &extra_statement_cache_ptr<T, I, ID>::allocate;
+ }
+ else
+ delete p_;
+ }
+
+ //
+ // Implementation for objects with object id.
+ //
+
+ class LIBODB_SQLITE_EXPORT object_statements_base: public statements_base
+ {
+ public:
+ // Locking.
+ //
+ void
+ lock ()
+ {
+ assert (!locked_);
+ locked_ = true;
+ }
+
+ void
+ unlock ()
+ {
+ assert (locked_);
+ locked_ = false;
+ }
+
+ bool
+ locked () const
+ {
+ return locked_;
+ }
+
+ struct auto_unlock
+ {
+ // Unlocks the statement on construction and re-locks it on
+ // destruction.
+ //
+ auto_unlock (object_statements_base&);
+ ~auto_unlock ();
+
+ private:
+ auto_unlock (const auto_unlock&);
+ auto_unlock& operator= (const auto_unlock&);
+
+ private:
+ object_statements_base& s_;
+ };
+
+ public:
+ virtual
+ ~object_statements_base ();
+
+ protected:
+ object_statements_base (connection_type& conn)
+ : statements_base (conn), locked_ (false)
+ {
+ }
+
+ protected:
+ bool locked_;
+ };
+
+ template <typename T, bool optimistic>
+ struct optimistic_data;
+
+ template <typename T>
+ struct optimistic_data<T, true>
+ {
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_sqlite> object_traits;
+
+ optimistic_data (bind*);
+
+ binding*
+ id_image_binding () {return &id_image_binding_;}
+
+ // The id + optimistic column binding.
+ //
+ binding id_image_binding_;
+
+ details::shared_ptr<delete_statement> erase_;
+ };
+
+ template <typename T>
+ struct optimistic_data<T, false>
+ {
+ optimistic_data (bind*) {}
+
+ binding*
+ id_image_binding () {return 0;}
+ };
+
+ template <typename T>
+ class object_statements: public object_statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_sqlite> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+ typedef typename object_traits::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::pointer_cache_traits
+ pointer_cache_traits;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef sqlite::insert_statement insert_statement_type;
+ typedef sqlite::select_statement select_statement_type;
+ typedef sqlite::update_statement update_statement_type;
+ typedef sqlite::delete_statement delete_statement_type;
+
+ // Automatic lock.
+ //
+ struct auto_lock
+ {
+ // Lock the statements unless they are already locked in which
+ // case subsequent calls to locked() will return false.
+ //
+ auto_lock (object_statements&);
+
+ // Unlock the statemens if we are holding the lock and clear
+ // the delayed loads. This should only happen in case an
+ // exception is thrown. In normal circumstances, the user
+ // should call unlock() explicitly.
+ //
+ ~auto_lock ();
+
+ // Return true if this auto_lock instance holds the lock.
+ //
+ bool
+ locked () const;
+
+ // Unlock the statemens.
+ //
+ void
+ unlock ();
+
+ private:
+ auto_lock (const auto_lock&);
+ auto_lock& operator= (const auto_lock&);
+
+ private:
+ object_statements& s_;
+ bool locked_;
+ };
+
+
+ public:
+ object_statements (connection_type&);
+
+ virtual
+ ~object_statements ();
+
+ // Delayed loading.
+ //
+ typedef void (*loader_function) (odb::database&,
+ const id_type&,
+ object_type&,
+ const schema_version_migration*);
+
+ void
+ delay_load (const id_type& id,
+ object_type& obj,
+ const typename pointer_cache_traits::position_type& p,
+ loader_function l = 0)
+ {
+ delayed_.push_back (delayed_load (id, obj, p, l));
+ }
+
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (locked ());
+
+ if (!delayed_.empty ())
+ load_delayed_<object_statements> (svm);
+ }
+
+ void
+ clear_delayed ()
+ {
+ if (!delayed_.empty ())
+ clear_delayed_ ();
+ }
+
+ // Object image.
+ //
+ image_type&
+ image () {return image_;}
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_image_version () const { return update_id_image_version_;}
+
+ void
+ update_id_image_version (std::size_t v) {update_id_image_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Object id image and binding.
+ //
+ id_image_type&
+ id_image () {return id_image_;}
+
+ std::size_t
+ id_image_version () const {return id_image_version_;}
+
+ void
+ id_image_version (std::size_t v) {id_image_version_ = v;}
+
+ binding&
+ id_image_binding () {return id_image_binding_;}
+
+ // Optimistic id + managed column image binding. It points to
+ // the same suffix as id binding and they are always updated
+ // at the same time.
+ //
+ binding&
+ optimistic_id_image_binding () {return od_.id_image_binding_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ {
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ (object_traits::auto_id ? &id_image_binding_ : 0)));
+ }
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement ()
+ {
+ if (find_ == 0)
+ {
+ find_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statement,
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_image_binding_,
+ select_image_binding_));
+ }
+
+ return *find_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ {
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
+ update_image_binding_));
+ }
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ {
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement,
+ id_image_binding_));
+ }
+
+ return *erase_;
+ }
+
+ delete_statement_type&
+ optimistic_erase_statement ()
+ {
+ if (od_.erase_ == 0)
+ {
+ od_.erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::optimistic_erase_statement,
+ od_.id_image_binding_));
+ }
+
+ return *od_.erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ image_, id_image_,
+ id_image_binding_, od_.id_image_binding ());
+ }
+
+ public:
+ // select = total - separate_load
+ // insert = total - inverse - managed_optimistic
+ // update = total - inverse - managed_optimistic - id - readonly
+ // - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ object_traits::separate_load_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count -
+ object_traits::managed_optimistic_column_count;
+
+ static const std::size_t update_column_count =
+ insert_column_count -
+ id_column_count -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ object_statements (const object_statements&);
+ object_statements& operator= (const object_statements&);
+
+ protected:
+ template <typename STS>
+ void
+ load_delayed_ (const schema_version_migration*);
+
+ void
+ clear_delayed_ ();
+
+ protected:
+ template <typename T1>
+ friend class polymorphic_derived_object_statements;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ image_type image_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ bind select_image_bind_[select_column_count];
+ bool select_image_truncated_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+
+ // Update binding. Note that the id suffix is bound to id_image_
+ // below instead of image_ which makes this binding effectively
+ // bound to two images. As a result, we have to track versions
+ // for both of them. If this object uses optimistic concurrency,
+ // then the binding for the managed column (version, timestamp,
+ // etc) comes after the id and the image for such a column is
+ // stored as part of the id image.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_image_version_;
+ binding update_image_binding_;
+ bind update_image_bind_[update_column_count + id_column_count +
+ managed_optimistic_column_count];
+
+ // Id image binding (only used as a parameter). Uses the suffix in
+ // the update bind.
+ //
+ id_image_type id_image_;
+ std::size_t id_image_version_;
+ binding id_image_binding_;
+
+ // Extra data for objects with optimistic concurrency support.
+ //
+ optimistic_data<T, managed_optimistic_column_count != 0> od_;
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_;
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+
+ // Delayed loading.
+ //
+ struct delayed_load
+ {
+ typedef typename pointer_cache_traits::position_type position_type;
+
+ delayed_load () {}
+ delayed_load (const id_type& i,
+ object_type& o,
+ const position_type& p,
+ loader_function l)
+ : id (i), obj (&o), pos (p), loader (l)
+ {
+ }
+
+ id_type id;
+ object_type* obj;
+ position_type pos;
+ loader_function loader;
+ };
+
+ typedef std::vector<delayed_load> delayed_loads;
+ delayed_loads delayed_;
+
+ // Delayed vectors swap guard. See the load_delayed_() function for
+ // details.
+ //
+ struct swap_guard
+ {
+ swap_guard (object_statements& os, delayed_loads& dl)
+ : os_ (os), dl_ (dl)
+ {
+ dl_.swap (os_.delayed_);
+ }
+
+ ~swap_guard ()
+ {
+ os_.clear_delayed ();
+ dl_.swap (os_.delayed_);
+ }
+
+ private:
+ object_statements& os_;
+ delayed_loads& dl_;
+ };
+ };
+ }
+}
+
+#include <odb/sqlite/simple-object-statements.ixx>
+#include <odb/sqlite/simple-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_SIMPLE_OBJECT_STATEMENTS_HXX
diff --git a/libodb-sqlite/odb/sqlite/simple-object-statements.ixx b/libodb-sqlite/odb/sqlite/simple-object-statements.ixx
new file mode 100644
index 0000000..6756c06
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/simple-object-statements.ixx
@@ -0,0 +1,68 @@
+// file : odb/sqlite/simple-object-statements.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // auto_unlock
+ //
+ inline object_statements_base::auto_unlock::
+ auto_unlock (object_statements_base& s)
+ : s_ (s)
+ {
+ s_.unlock ();
+ }
+
+ inline object_statements_base::auto_unlock::
+ ~auto_unlock ()
+ {
+ s_.lock ();
+ }
+
+ //
+ // auto_lock
+ //
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ auto_lock (object_statements& s)
+ : s_ (s)
+ {
+ if (!s_.locked ())
+ {
+ s_.lock ();
+ locked_ = true;
+ }
+ else
+ locked_ = false;
+ }
+
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ ~auto_lock ()
+ {
+ if (locked_)
+ {
+ s_.unlock ();
+ s_.clear_delayed ();
+ }
+ }
+
+ template <typename T>
+ inline bool object_statements<T>::auto_lock::
+ locked () const
+ {
+ return locked_;
+ }
+
+ template <typename T>
+ inline void object_statements<T>::auto_lock::
+ unlock ()
+ {
+ assert (locked_);
+ s_.unlock ();
+ locked_ = false;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/simple-object-statements.txx b/libodb-sqlite/odb/sqlite/simple-object-statements.txx
new file mode 100644
index 0000000..b80944d
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/simple-object-statements.txx
@@ -0,0 +1,146 @@
+// file : odb/sqlite/simple-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/traits-calls.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // optimistic_data
+ //
+
+ template <typename T>
+ optimistic_data<T, true>::
+ optimistic_data (bind* b)
+ : id_image_binding_ (
+ b,
+ object_traits::id_column_count +
+ object_traits::managed_optimistic_column_count)
+ {
+ }
+
+ //
+ // object_statements
+ //
+
+ template <typename T>
+ object_statements<T>::
+ ~object_statements ()
+ {
+ }
+
+ template <typename T>
+ object_statements<T>::
+ object_statements (connection_type& conn)
+ : object_statements_base (conn),
+ select_image_binding_ (select_image_bind_, select_column_count),
+ insert_image_binding_ (insert_image_bind_, insert_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_column_count),
+ id_image_binding_ (update_image_bind_ + update_column_count,
+ id_column_count),
+ od_ (update_image_bind_ + update_column_count)
+ {
+ image_.version = 0;
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_image_version_ = 0;
+
+ id_image_.version = 0;
+ id_image_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+
+ template <typename T>
+ template <typename STS>
+ void object_statements<T>::
+ load_delayed_ (const schema_version_migration* svm)
+ {
+ database& db (connection ().database ());
+
+ delayed_loads dls;
+ swap_guard sg (*this, dls);
+
+ while (!dls.empty ())
+ {
+ delayed_load l (dls.back ());
+ typename pointer_cache_traits::insert_guard ig (l.pos);
+ dls.pop_back ();
+
+ if (l.loader == 0)
+ {
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (static_cast<STS&> (*this), &l.id))
+ throw object_not_persistent ();
+
+ object_traits::callback (db, *l.obj, callback_event::pre_load);
+
+ // Our calls to init/load below can result in additional delayed
+ // loads being added to the delayed_ vector. We need to process
+ // those before we call the post callback.
+ //
+ tc.init (*l.obj, image (), &db);
+
+ // Load containers, etc.
+ //
+ tc.load_ (static_cast<STS&> (*this), *l.obj, false);
+
+ if (!delayed_.empty ())
+ load_delayed_<STS> (svm);
+
+ // Temporarily unlock the statement for the post_load call so that
+ // it can load objects of this type recursively. This is safe to do
+ // because we have completely loaded the current object. Also the
+ // delayed_ list is clear before the unlock and should be clear on
+ // re-lock (since a callback can only call public API functions
+ // which will make sure all the delayed loads are processed before
+ // returning).
+ //
+ {
+ auto_unlock u (*this);
+ object_traits::callback (db, *l.obj, callback_event::post_load);
+ }
+ }
+ else
+ l.loader (db, l.id, *l.obj, svm);
+
+ pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+ }
+
+ template <typename T>
+ void object_statements<T>::
+ clear_delayed_ ()
+ {
+ // Remove the objects from the session cache.
+ //
+ for (typename delayed_loads::iterator i (delayed_.begin ()),
+ e (delayed_.end ()); i != e; ++i)
+ {
+ pointer_cache_traits::erase (i->pos);
+ }
+
+ delayed_.clear ();
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/sqlite-types.hxx b/libodb-sqlite/odb/sqlite/sqlite-types.hxx
new file mode 100644
index 0000000..b9839bf
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/sqlite-types.hxx
@@ -0,0 +1,57 @@
+// file : odb/sqlite/sqlite-types.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_SQLITE_TYPES_HXX
+#define ODB_SQLITE_SQLITE_TYPES_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // The SQLite parameter/result binding. This data structures is modelled
+ // after MYSQL_BIND from MySQL.
+ //
+ struct bind
+ {
+ enum buffer_type
+ {
+ integer, // Buffer is long long; size, capacity, truncated are unused.
+ real, // Buffer is double; size, capacity, truncated are unused.
+ text, // Buffer is a UTF-8 char array.
+ text16, // Buffer is a UTF-16 2-byte char array (sizes in bytes).
+ blob, // Buffer is a char array.
+ stream // Buffer is stream_buffers. Size specifies the BLOB size
+ // (input only). Capacity and truncated unused.
+ };
+
+ buffer_type type;
+ void* buffer;
+ std::size_t* size;
+ std::size_t capacity;
+ bool* is_null;
+ bool* truncated;
+ };
+
+ // The "out" values should be set in set_image() to point to
+ // variables that will be receiving the data. The "in" values
+ // are used in set_value() and contain the data that needs to
+ // be copied over.
+ //
+ struct stream_buffers
+ {
+ union {std::string* out; const char* in;} db;
+ union {std::string* out; const char* in;} table;
+ union {std::string* out; const char* in;} column;
+ union {long long* out; long long in;} rowid;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_SQLITE_TYPES_HXX
diff --git a/libodb-sqlite/odb/sqlite/statement-cache.hxx b/libodb-sqlite/odb/sqlite/statement-cache.hxx
new file mode 100644
index 0000000..31ca685
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/statement-cache.hxx
@@ -0,0 +1,60 @@
+// file : odb/sqlite/statement-cache.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_STATEMENT_CACHE_HXX
+#define ODB_SQLITE_STATEMENT_CACHE_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <typeinfo>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/type-info.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/statements-base.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class LIBODB_SQLITE_EXPORT statement_cache
+ {
+ public:
+ statement_cache (connection& conn)
+ : conn_ (conn),
+ version_seq_ (conn_.database ().schema_version_sequence ()) {}
+
+ template <typename T>
+ typename object_traits_impl<T, id_sqlite>::statements_type&
+ find_object ();
+
+ template <typename T>
+ view_statements<T>&
+ find_view ();
+
+ private:
+ typedef std::map<const std::type_info*,
+ details::shared_ptr<statements_base>,
+ details::type_info_comparator> map;
+
+ connection& conn_;
+ unsigned int version_seq_;
+ map map_;
+ };
+ }
+}
+
+#include <odb/sqlite/statement-cache.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_STATEMENT_CACHE_HXX
diff --git a/libodb-sqlite/odb/sqlite/statement-cache.txx b/libodb-sqlite/odb/sqlite/statement-cache.txx
new file mode 100644
index 0000000..c089e32
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/statement-cache.txx
@@ -0,0 +1,60 @@
+// file : odb/sqlite/statement-cache.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/database.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ typename object_traits_impl<T, id_sqlite>::statements_type&
+ statement_cache::
+ find_object ()
+ {
+ typedef
+ typename object_traits_impl<T, id_sqlite>::statements_type
+ statements_type;
+
+ // Clear the cache if the database version has changed. This
+ // makes sure we don't re-use statements that correspond to
+ // the old schema.
+ //
+ if (version_seq_ != conn_.database ().schema_version_sequence ())
+ {
+ map_.clear ();
+ version_seq_ = conn_.database ().schema_version_sequence ();
+ }
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<statements_type&> (*i->second);
+
+ details::shared_ptr<statements_type> p (
+ new (details::shared) statements_type (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+
+ template <typename T>
+ view_statements<T>& statement_cache::
+ find_view ()
+ {
+ // We don't cache any statements for views so no need to clear
+ // the cache.
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<view_statements<T>&> (*i->second);
+
+ details::shared_ptr<view_statements<T> > p (
+ new (details::shared) view_statements<T> (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/statement.cxx b/libodb-sqlite/odb/sqlite/statement.cxx
new file mode 100644
index 0000000..b1b0f58
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/statement.cxx
@@ -0,0 +1,988 @@
+// file : odb/sqlite/statement.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/tracer.hxx>
+#include <odb/exceptions.hxx> // object_not_persistent
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/error.hxx>
+
+#include <odb/sqlite/details/config.hxx> // LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ // LIBODB_SQLITE_HAVE_COLUMN_METADATA
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // statement
+ //
+
+ statement::
+ ~statement ()
+ {
+ if (stmt_ != 0)
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->deallocate (conn_, *this);
+ }
+
+ if (next_ != this)
+ list_remove ();
+
+ stmt_.reset ();
+ }
+ }
+
+ void statement::
+ clear ()
+ {
+ reset ();
+ }
+
+ void statement::
+ init (const char* text,
+ std::size_t text_size,
+ statement_kind sk,
+ const binding* proc,
+ bool optimize)
+ {
+ active_ = false;
+
+ string tmp1;
+ if (proc != 0)
+ {
+ switch (sk)
+ {
+ case statement_select:
+ process_select (tmp1,
+ text,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '"', '"',
+ optimize);
+ break;
+ case statement_insert:
+ process_insert (tmp1,
+ text,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '?',
+ '$');
+ break;
+ case statement_update:
+ process_update (tmp1,
+ text,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '?',
+ '$');
+ break;
+ case statement_delete:
+ case statement_generic:
+ assert (false);
+ }
+
+ text = tmp1.c_str ();
+ text_size = tmp1.size ();
+ }
+
+ string tmp2;
+ if (conn_.statement_translator_ != 0)
+ {
+ conn_.statement_translator_ (tmp2, text, text_size, conn_);
+
+ if (!tmp2.empty ())
+ {
+ text = tmp2.c_str ();
+ text_size = tmp2.size ();
+ }
+ }
+
+#if SQLITE_VERSION_NUMBER < 3005003
+ text_.assign (text, text_size);
+#endif
+
+ // Empty statement.
+ //
+ if (*text == '\0')
+ return;
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ {
+ // Temporarily store the statement text in prev_ so that
+ // text() which may be called by the tracer can access it.
+ // Dirty but efficient.
+ //
+#if SQLITE_VERSION_NUMBER >= 3005003
+ prev_ = reinterpret_cast<active_object*> (const_cast<char*> (text));
+#endif
+ t->prepare (conn_, *this);
+#if SQLITE_VERSION_NUMBER >= 3005003
+ prev_ = 0;
+#endif
+ }
+ }
+
+ int e;
+ sqlite3_stmt* stmt (0);
+
+ // sqlite3_prepare_v2() is only available since SQLite 3.3.9
+ // but is buggy until 3.3.11.
+ //
+#if SQLITE_VERSION_NUMBER >= 3003011
+ while ((e = sqlite3_prepare_v2 (conn_.handle (),
+ text,
+ static_cast<int> (text_size),
+ &stmt,
+ 0)) == SQLITE_LOCKED)
+ {
+ conn_.wait ();
+ }
+#else
+ e = sqlite3_prepare (conn_.handle (),
+ text,
+ static_cast<int> (text_size),
+ &stmt,
+ 0);
+#endif
+
+ if (e != SQLITE_OK)
+ translate_error (e, conn_);
+
+ stmt_.reset (stmt);
+ }
+
+ const char* statement::
+ text () const
+ {
+ // sqlite3_sql() is only available since 3.5.3.
+ //
+#if SQLITE_VERSION_NUMBER >= 3005003
+ if (stmt_ == 0)
+ // See init() above for details on what's going on here.
+ //
+ return prev_ != 0 ? reinterpret_cast<const char*> (prev_) : "";
+ else
+ return sqlite3_sql (stmt_);
+#else
+ return text_.c_str ();
+#endif
+ }
+
+ bool statement::
+ bind_param (const bind* p, size_t n)
+ {
+ int e (SQLITE_OK);
+ bool r (false);
+
+ // SQLite parameters are counted from 1.
+ //
+ for (size_t i (0), j (1); e == SQLITE_OK && i < n; ++i)
+ {
+ const bind& b (p[i]);
+
+ if (b.buffer == 0) // Skip NULL entries.
+ continue;
+
+ int c (static_cast<int> (j++));
+
+ if (b.is_null != 0 && *b.is_null)
+ {
+ e = sqlite3_bind_null (stmt_, c);
+ continue;
+ }
+
+ switch (b.type)
+ {
+ case bind::integer:
+ {
+ long long v (*static_cast<long long*> (b.buffer));
+
+ e = sqlite3_bind_int64 (stmt_,
+ c,
+ // Prior to 3.5.0, sqlite3_int64 was called sqlite_int64.
+#if SQLITE_VERSION_NUMBER >= 3005000
+ static_cast<sqlite3_int64> (v)
+#else
+ static_cast<sqlite_int64> (v)
+#endif
+ );
+ break;
+ }
+ case bind::real:
+ {
+ double v (*static_cast<double*> (b.buffer));
+ e = sqlite3_bind_double (stmt_, c, v);
+ break;
+ }
+ case bind::text:
+ {
+ e = sqlite3_bind_text (stmt_,
+ c,
+ static_cast<const char*> (b.buffer),
+ static_cast<int> (*b.size),
+ SQLITE_STATIC);
+ break;
+ }
+ case bind::text16:
+ {
+ e = sqlite3_bind_text16 (stmt_,
+ c,
+ b.buffer,
+ static_cast<int> (*b.size),
+ SQLITE_STATIC);
+ break;
+ }
+ case bind::blob:
+ {
+ e = sqlite3_bind_blob (stmt_,
+ c,
+ b.buffer,
+ static_cast<int> (*b.size),
+ SQLITE_STATIC);
+ break;
+ }
+ case bind::stream:
+ {
+#if SQLITE_VERSION_NUMBER >= 3004000
+ e = sqlite3_bind_zeroblob (stmt_,
+ c,
+ static_cast<int> (*b.size));
+ r = true;
+#else
+ assert (false);
+#endif
+ break;
+ }
+ }
+ }
+
+ if (e != SQLITE_OK)
+ translate_error (e, conn_);
+
+ return r;
+ }
+
+ bool statement::
+ bind_result (const bind* p, size_t count, bool truncated)
+ {
+ bool r (true);
+ int col_count (sqlite3_data_count (stmt_));
+
+ int col (0);
+ for (size_t i (0); i != count && col != col_count; ++i)
+ {
+ const bind& b (p[i]);
+
+ if (b.buffer == 0) // Skip NULL entries.
+ continue;
+
+ int c (col++);
+
+ if (b.type == bind::stream)
+ col++; // Skip ROWID value that follows.
+
+ if (truncated && (b.truncated == 0 || !*b.truncated))
+ continue;
+
+ if (b.truncated != 0)
+ *b.truncated = false;
+
+ // Check for NULL unless we are reloading a truncated result.
+ //
+ if (!truncated)
+ {
+ *b.is_null = sqlite3_column_type (stmt_, c) == SQLITE_NULL;
+
+ if (*b.is_null)
+ continue;
+ }
+
+ switch (b.type)
+ {
+ case bind::integer:
+ {
+ *static_cast<long long*> (b.buffer) =
+ static_cast<long long> (sqlite3_column_int64 (stmt_, c));
+ break;
+ }
+ case bind::real:
+ {
+ *static_cast<double*> (b.buffer) =
+ sqlite3_column_double (stmt_, c);
+ break;
+ }
+ case bind::text:
+ case bind::text16:
+ case bind::blob:
+ {
+ // SQLite documentation recommends that we first call *_text(),
+ // *_text16(), or *_blob() function in order to force the type
+ // conversion, if any.
+ //
+ const void* d;
+
+ if (b.type != bind::text16)
+ {
+ d = b.type == bind::text
+ ? sqlite3_column_text (stmt_, c)
+ : sqlite3_column_blob (stmt_, c);
+ *b.size = static_cast<size_t> (sqlite3_column_bytes (stmt_, c));
+ }
+ else
+ {
+ d = sqlite3_column_text16 (stmt_, c);
+ *b.size = static_cast<size_t> (
+ sqlite3_column_bytes16 (stmt_, c));
+ }
+
+ if (*b.size > b.capacity)
+ {
+ if (b.truncated != 0)
+ *b.truncated = true;
+
+ r = false;
+ continue;
+ }
+
+ memcpy (b.buffer, d, *b.size);
+ break;
+ }
+ case bind::stream:
+ {
+ stream_buffers& sb (*static_cast<stream_buffers*> (b.buffer));
+
+ // SQLite documentation states that these are valid until the
+ // statement is finalized (or reprepared). For our case, we
+ // only need it to stay alive until we call set_value() which
+ // we do while executing the statement (i.e., we don't copy
+ // images for later processing).
+ //
+#ifdef LIBODB_SQLITE_HAVE_COLUMN_METADATA
+ sb.db.in = sqlite3_column_database_name (stmt_, c);
+ sb.table.in = sqlite3_column_table_name (stmt_, c);
+ sb.column.in = sqlite3_column_origin_name (stmt_, c);
+#else
+ assert (false);
+#endif
+
+ // The ROWID comes in the following column.
+ //
+ sb.rowid.in = static_cast<long long> (
+ sqlite3_column_int64 (stmt_, c + 1));
+
+ break;
+ }
+ }
+ }
+
+ // Make sure that the number of columns in the result returned by
+ // the database matches the number that we expect. A common cause
+ // of this assertion is a native view with a number of data members
+ // not matching the number of columns in the SELECT-list.
+ //
+ assert (col == col_count);
+
+ return r;
+ }
+
+ void statement::
+ stream_param (const bind* p, size_t n, const stream_data& d)
+ {
+ // Code similar to bind_param().
+ //
+ for (size_t i (0), j (1); i < n; ++i)
+ {
+ const bind& b (p[i]);
+
+ if (b.buffer == 0) // Skip NULL entries.
+ continue;
+
+ int c (static_cast<int> (j++));
+
+ if ((b.is_null != 0 && *b.is_null) || b.type != bind::stream)
+ continue;
+
+ // Get column name.
+ //
+ const char* col (sqlite3_bind_parameter_name (stmt_, c));
+ assert (col != 0); // Statement doesn't contain column name.
+
+ stream_buffers& sb (*static_cast<stream_buffers*> (b.buffer));
+
+ *sb.db.out = d.db;
+ *sb.table.out = d.table;
+ *sb.column.out = col + 1; // Skip '$'.
+ *sb.rowid.out = d.rowid;
+ }
+ }
+
+ inline void
+ update_hook (void* v, const char* db, const char* table, long long rowid)
+ {
+ statement::stream_data& d (*static_cast<statement::stream_data*> (v));
+ d.db = db;
+ d.table = table;
+ d.rowid = rowid;
+ }
+
+ extern "C" void
+ odb_sqlite_update_hook (void* v,
+ int,
+ const char* db,
+ const char* table,
+#if SQLITE_VERSION_NUMBER >= 3005000
+ sqlite3_int64 rowid
+#else
+ sqlite_int64 rowid
+#endif
+ )
+ {
+ update_hook (v, db, table, static_cast<long long> (rowid));
+ }
+
+ // generic_statement
+ //
+
+ generic_statement::
+ generic_statement (connection_type& conn, const string& text)
+ : statement (conn,
+ text, statement_generic,
+ 0, false),
+ result_set_ (stmt_ ? sqlite3_column_count (stmt_) != 0: false)
+ {
+ }
+
+ generic_statement::
+ generic_statement (connection_type& conn, const char* text)
+ : statement (conn,
+ text, statement_generic,
+ 0, false),
+ result_set_ (stmt_ ? sqlite3_column_count (stmt_) != 0: false)
+ {
+ }
+
+ generic_statement::
+ generic_statement (connection_type& conn,
+ const char* text,
+ size_t text_size)
+ : statement (conn,
+ text, text_size, statement_generic,
+ 0, false),
+ result_set_ (stmt_ ? sqlite3_column_count (stmt_) != 0: false)
+ {
+ }
+
+ unsigned long long generic_statement::
+ execute ()
+ {
+ if (stmt_ == 0) // Empty statement or comment.
+ return 0;
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ unsigned long long r (0);
+
+ int e;
+ sqlite3* h (conn_.handle ());
+
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ // Only the first call to sqlite3_step() can return SQLITE_LOCKED.
+ //
+ while ((e = sqlite3_step (stmt_)) == SQLITE_LOCKED)
+ {
+ if (sqlite3_extended_errcode (h) != SQLITE_LOCKED_SHAREDCACHE)
+ break;
+
+ sqlite3_reset (stmt_);
+ conn_.wait ();
+ }
+#else
+ e = sqlite3_step (stmt_);
+#endif
+
+ for (; e == SQLITE_ROW; e = sqlite3_step (stmt_))
+ r++;
+
+ // sqlite3_step() will return a detailed error code only if we used
+ // sqlite3_prepare_v2(). Otherwise, sqlite3_reset() returns the
+ // error.
+ //
+#if SQLITE_VERSION_NUMBER >= 3003011
+ sqlite3_reset (stmt_);
+
+ if (e != SQLITE_DONE)
+ translate_error (e, conn_);
+#else
+ e = sqlite3_reset (stmt_);
+
+ if (e != SQLITE_OK)
+ {
+ // If the schema has changed, try to re-prepare and re-execute the
+ // statement. That's what newer versions of SQLite do automatically.
+ //
+ if (e == SQLITE_SCHEMA)
+ {
+ sqlite3_stmt* stmt (0);
+ e = sqlite3_prepare (h,
+ text_.c_str (),
+ static_cast<int> (text_.size () + 1),
+ &stmt,
+ 0);
+
+ if (e != SQLITE_OK)
+ translate_error (e, conn_);
+
+ stmt_.reset (stmt);
+ return execute (); // Try again by recursively calling ourselves.
+ }
+ else
+ translate_error (e, conn_);
+ }
+#endif
+
+ if (!result_set_)
+ r = static_cast<unsigned long long> (sqlite3_changes (h));
+
+ return r;
+ }
+
+ // select_statement
+ //
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ bool optimize,
+ binding& param,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ param_ (&param),
+ result_ (result)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& param,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ param_ (&param),
+ result_ (result)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ bool optimize,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ param_ (0),
+ result_ (result)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ param_ (0),
+ result_ (result)
+ {
+ }
+
+ void select_statement::
+ execute ()
+ {
+ if (active ())
+ reset ();
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ done_ = false;
+
+ if (param_ != 0)
+ bind_param (param_->bind, param_->count);
+
+ active (true);
+ }
+
+ void select_statement::
+ free_result ()
+ {
+ reset ();
+ done_ = true;
+ }
+
+ bool select_statement::
+ next ()
+ {
+ if (!done_)
+ {
+ int e;
+
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ sqlite3* h (conn_.handle ());
+ while ((e = sqlite3_step (stmt_)) == SQLITE_LOCKED)
+ {
+ if (sqlite3_extended_errcode (h) != SQLITE_LOCKED_SHAREDCACHE)
+ break;
+
+ sqlite3_reset (stmt_);
+ conn_.wait ();
+ }
+#else
+ e = sqlite3_step (stmt_);
+#endif
+
+ if (e != SQLITE_ROW)
+ {
+ done_ = true;
+
+ // sqlite3_step() will return a detailed error code only if we used
+ // sqlite3_prepare_v2(). Otherwise, sqlite3_reset() returns the
+ // error.
+ //
+#if SQLITE_VERSION_NUMBER >= 3003011
+ reset ();
+
+ if (e != SQLITE_DONE)
+#else
+ e = reset ();
+
+ if (e != SQLITE_OK)
+#endif
+ translate_error (e, conn_);
+ }
+ }
+
+ return !done_;
+ }
+
+ select_statement::result select_statement::
+ load ()
+ {
+ if (done_)
+ return no_data;
+
+ return bind_result (result_.bind, result_.count) ? success : truncated;
+ }
+
+ void select_statement::
+ reload ()
+ {
+ assert (!done_);
+
+ if (!bind_result (result_.bind, result_.count, true))
+ assert (false);
+ }
+
+ // insert_statement
+ //
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ binding& param,
+ binding* returning)
+ : statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false),
+ param_ (param),
+ returning_ (returning)
+ {
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param,
+ binding* returning)
+ : statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false),
+ param_ (param),
+ returning_ (returning)
+ {
+ }
+
+ bool insert_statement::
+ execute ()
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ sqlite3* h (conn_.handle ());
+ bool stream (bind_param (param_.bind, param_.count));
+
+ stream_data sd;
+ if (stream)
+ sqlite3_update_hook (h, &odb_sqlite_update_hook, &sd);
+
+ int e;
+
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ while ((e = sqlite3_step (stmt_)) == SQLITE_LOCKED)
+ {
+ if (sqlite3_extended_errcode (h) != SQLITE_LOCKED_SHAREDCACHE)
+ break;
+
+ sqlite3_reset (stmt_);
+ conn_.wait ();
+ }
+#else
+ e = sqlite3_step (stmt_);
+#endif
+
+ if (stream)
+ sqlite3_update_hook (h, 0, 0); // Clear the hook.
+
+ // sqlite3_step() will return a detailed error code only if we used
+ // sqlite3_prepare_v2(). Otherwise, sqlite3_reset() returns the
+ // error.
+ //
+#if SQLITE_VERSION_NUMBER >= 3003011
+ sqlite3_reset (stmt_);
+
+ if (e != SQLITE_DONE)
+#else
+ e = sqlite3_reset (stmt_);
+
+ if (e != SQLITE_OK)
+#endif
+ {
+ // SQLITE_CONSTRAINT error code covers more than just a duplicate
+ // primary key. Unfortunately, there is nothing more precise that
+ // we can use (even sqlite3_errmsg() returns generic "constraint
+ // failed"). But an auto-assigned object id should never cause a
+ // duplicate primary key.
+ //
+ if (returning_ == 0 && e == SQLITE_CONSTRAINT)
+ return false;
+ else
+ translate_error (e, conn_);
+ }
+
+ // Stream parameters, if any.
+ //
+ if (stream)
+ stream_param (param_.bind, param_.count, sd);
+
+ if (returning_ != 0)
+ {
+ bind& b (returning_->bind[0]);
+
+ *b.is_null = false;
+ *static_cast<long long*> (b.buffer) =
+ static_cast<long long> (
+ sqlite3_last_insert_rowid (h));
+ }
+
+ return true;
+ }
+
+ // update_statement
+ //
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ binding& param)
+ : statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false),
+ param_ (param)
+ {
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param)
+ : statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false),
+ param_ (param)
+ {
+ }
+
+ unsigned long long update_statement::
+ execute ()
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ sqlite3* h (conn_.handle ());
+ bool stream (bind_param (param_.bind, param_.count));
+
+ stream_data sd;
+ if (stream)
+ sqlite3_update_hook (h, &odb_sqlite_update_hook, &sd);
+
+ int e;
+
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ while ((e = sqlite3_step (stmt_)) == SQLITE_LOCKED)
+ {
+ if (sqlite3_extended_errcode (h) != SQLITE_LOCKED_SHAREDCACHE)
+ break;
+
+ sqlite3_reset (stmt_);
+ conn_.wait ();
+ }
+#else
+ e = sqlite3_step (stmt_);
+#endif
+
+ if (stream)
+ sqlite3_update_hook (h, 0, 0); // Clear the hook.
+
+ // sqlite3_step() will return a detailed error code only if we used
+ // sqlite3_prepare_v2(). Otherwise, sqlite3_reset() returns the
+ // error.
+ //
+#if SQLITE_VERSION_NUMBER >= 3003011
+ sqlite3_reset (stmt_);
+
+ if (e != SQLITE_DONE)
+#else
+ e = sqlite3_reset (stmt_);
+
+ if (e != SQLITE_OK)
+#endif
+ translate_error (e, conn_);
+
+ int r (sqlite3_changes (h));
+
+ // Stream parameters, if any.
+ //
+ if (stream && r != 0)
+ stream_param (param_.bind, param_.count, sd);
+
+ return static_cast<unsigned long long> (r);
+ }
+
+ // delete_statement
+ //
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const string& text,
+ binding& param)
+ : statement (conn,
+ text, statement_delete,
+ 0, false),
+ param_ (param)
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const char* text,
+ binding& param)
+ : statement (conn,
+ text, statement_delete,
+ 0, false),
+ param_ (param)
+ {
+ }
+
+ unsigned long long delete_statement::
+ execute ()
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.main_connection ().transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ bind_param (param_.bind, param_.count);
+
+ int e;
+ sqlite3* h (conn_.handle ());
+
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ while ((e = sqlite3_step (stmt_)) == SQLITE_LOCKED)
+ {
+ if (sqlite3_extended_errcode (h) != SQLITE_LOCKED_SHAREDCACHE)
+ break;
+
+ sqlite3_reset (stmt_);
+ conn_.wait ();
+ }
+#else
+ e = sqlite3_step (stmt_);
+#endif
+
+ // sqlite3_step() will return a detailed error code only if we used
+ // sqlite3_prepare_v2(). Otherwise, sqlite3_reset() returns the
+ // error.
+ //
+#if SQLITE_VERSION_NUMBER >= 3003011
+ sqlite3_reset (stmt_);
+
+ if (e != SQLITE_DONE)
+#else
+ e = sqlite3_reset (stmt_);
+
+ if (e != SQLITE_OK)
+#endif
+ translate_error (e, conn_);
+
+ return static_cast<unsigned long long> (sqlite3_changes (h));
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/statement.hxx b/libodb-sqlite/odb/sqlite/statement.hxx
new file mode 100644
index 0000000..9eeea7b
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/statement.hxx
@@ -0,0 +1,392 @@
+// file : odb/sqlite/statement.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_STATEMENT_HXX
+#define ODB_SQLITE_STATEMENT_HXX
+
+#include <odb/pre.hxx>
+
+#include <sqlite3.h>
+
+#include <string>
+#include <cstddef> // std::size_t
+#include <cstring> // std::strlen, std::memcpy
+#include <cassert>
+
+#include <odb/statement.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/binding.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/auto-handle.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class connection;
+
+ class LIBODB_SQLITE_EXPORT statement: public odb::statement,
+ public active_object
+ {
+ public:
+ typedef sqlite::connection connection_type;
+
+ virtual
+ ~statement () = 0;
+
+ sqlite3_stmt*
+ handle () const
+ {
+ return stmt_;
+ }
+
+ virtual const char*
+ text () const;
+
+ virtual connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // A statement can be empty. This is used to handle situations
+ // where a SELECT or UPDATE statement ends up not having any
+ // columns after processing. An empty statement cannot be
+ // executed.
+ //
+ bool
+ empty () const
+ {
+ return stmt_ == 0;
+ }
+
+ protected:
+ // We keep two versions to take advantage of std::string COW.
+ //
+ statement (connection_type& conn,
+ const std::string& text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize)
+ : active_object (conn)
+ {
+ init (text.c_str (), text.size (), sk, process, optimize);
+ }
+
+ statement (connection_type& conn,
+ const char* text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize)
+ : active_object (conn)
+ {
+ init (text, std::strlen (text), sk, process, optimize);
+ }
+
+ statement (connection_type& conn,
+ const char* text,
+ std::size_t text_size,
+ statement_kind sk,
+ const binding* process,
+ bool optimize)
+ : active_object (conn)
+ {
+ init (text, text_size, sk, process, optimize);
+ }
+
+ protected:
+ // Return true if we bound any stream parameters.
+ //
+ bool
+ bind_param (const bind*, std::size_t count);
+
+ // Extract row columns into the bound buffers. If the truncated
+ // argument is true, then only truncated columns are extracted.
+ // Return true if all the data was extracted successfully and
+ // false if one or more columns were truncated.
+ //
+ bool
+ bind_result (const bind*, std::size_t count, bool truncated = false);
+
+ // Stream (so to speak) parameters.
+ //
+ struct stream_data
+ {
+ std::string db;
+ std::string table;
+ long long rowid;
+ };
+
+ void
+ stream_param (const bind*, std::size_t count, const stream_data&);
+
+ friend void
+ update_hook (void*, const char*, const char*, long long);
+
+ // Active state.
+ //
+ protected:
+ bool
+ active () const
+ {
+ return active_;
+ }
+
+ void
+ active (bool active)
+ {
+ assert (active);
+
+ if (!active_)
+ {
+ list_add ();
+ active_ = true;
+ }
+ }
+
+ int
+ reset ()
+ {
+ int r (SQLITE_OK);
+
+ if (active_)
+ {
+ r = sqlite3_reset (stmt_);
+ list_remove ();
+ active_ = false;
+ }
+
+ return r;
+ }
+
+ // The active_object interface.
+ //
+ virtual void
+ clear ();
+
+ protected:
+ auto_handle<sqlite3_stmt> stmt_;
+
+#if SQLITE_VERSION_NUMBER < 3005003
+ std::string text_;
+#endif
+
+ bool active_;
+
+ private:
+ void
+ init (const char* text,
+ std::size_t text_size,
+ statement_kind,
+ const binding* process,
+ bool optimize);
+ };
+
+ class LIBODB_SQLITE_EXPORT generic_statement: public statement
+ {
+ public:
+ generic_statement (connection_type&, const std::string& text);
+ generic_statement (connection_type&, const char* text);
+ generic_statement (connection_type&,
+ const char* text,
+ std::size_t text_size);
+
+ unsigned long long
+ execute ();
+
+ private:
+ generic_statement (const generic_statement&);
+ generic_statement& operator= (const generic_statement&);
+
+ private:
+ bool result_set_;
+ };
+
+ class LIBODB_SQLITE_EXPORT select_statement: public statement
+ {
+ public:
+ select_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& param,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& param,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& result);
+
+ // Common select interface expected by the generated code.
+ //
+ public:
+ enum result
+ {
+ success,
+ no_data,
+ truncated
+ };
+
+ void
+ execute ();
+
+ // Load next row columns into bound buffers.
+ //
+ result
+ fetch ()
+ {
+ return next () ? load () : no_data;
+ }
+
+ // Reload truncated columns into bound buffers.
+ //
+ void
+ refetch ()
+ {
+ reload ();
+ }
+
+ // Free the result set.
+ //
+ void
+ free_result ();
+
+ // More fine-grained SQLite-specific interface that splits fetch()
+ // into next() and load().
+ //
+ public:
+ // Return false if there is no more rows. You should call next()
+ // until it returns false or, alternatively, call free_result ().
+ // Otherwise the statement will remain unfinished.
+ //
+ bool
+ next ();
+
+ result
+ load ();
+
+ void
+ reload ();
+
+ private:
+ select_statement (const select_statement&);
+ select_statement& operator= (const select_statement&);
+
+ private:
+ bool done_;
+ binding* param_;
+ binding& result_;
+ };
+
+ struct auto_result
+ {
+ explicit auto_result (select_statement& s): s_ (s) {}
+ ~auto_result () {s_.free_result ();}
+
+ private:
+ auto_result (const auto_result&);
+ auto_result& operator= (const auto_result&);
+
+ private:
+ select_statement& s_;
+ };
+
+ class LIBODB_SQLITE_EXPORT insert_statement: public statement
+ {
+ public:
+ insert_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ binding& param,
+ binding* returning);
+
+ insert_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ binding& param,
+ binding* returning);
+
+ // Return true if successful and false if the row is a duplicate.
+ // All other errors are reported by throwing exceptions.
+ //
+ bool
+ execute ();
+
+ private:
+ insert_statement (const insert_statement&);
+ insert_statement& operator= (const insert_statement&);
+
+ private:
+ binding& param_;
+ binding* returning_;
+ };
+
+ class LIBODB_SQLITE_EXPORT update_statement: public statement
+ {
+ public:
+ update_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ binding& param);
+
+ update_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ binding& param);
+
+ unsigned long long
+ execute ();
+
+ private:
+ update_statement (const update_statement&);
+ update_statement& operator= (const update_statement&);
+
+ private:
+ binding& param_;
+ };
+
+ class LIBODB_SQLITE_EXPORT delete_statement: public statement
+ {
+ public:
+ delete_statement (connection_type& conn,
+ const std::string& text,
+ binding& param);
+
+ delete_statement (connection_type& conn,
+ const char* text,
+ binding& param);
+
+ unsigned long long
+ execute ();
+
+ private:
+ delete_statement (const delete_statement&);
+ delete_statement& operator= (const delete_statement&);
+
+ private:
+ binding& param_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_STATEMENT_HXX
diff --git a/libodb-sqlite/odb/sqlite/statements-base.cxx b/libodb-sqlite/odb/sqlite/statements-base.cxx
new file mode 100644
index 0000000..bde8c55
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/statements-base.cxx
@@ -0,0 +1,15 @@
+// file : odb/sqlite/statements-base.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/statements-base.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ statements_base::
+ ~statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/statements-base.hxx b/libodb-sqlite/odb/sqlite/statements-base.hxx
new file mode 100644
index 0000000..5851d1b
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/statements-base.hxx
@@ -0,0 +1,63 @@
+// file : odb/sqlite/statements-base.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_STATEMENTS_BASE_HXX
+#define ODB_SQLITE_STATEMENTS_BASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/schema-version.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/database.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class LIBODB_SQLITE_EXPORT statements_base: public details::shared_base
+ {
+ public:
+ typedef sqlite::connection connection_type;
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Schema version. database::schema_version_migration() is thread-
+ // safe which means it is also slow. Cache the result in statements
+ // so we can avoid the mutex lock. This is thread-safe since if the
+ // version is updated, then the statements cache will be expired.
+ //
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ public:
+ virtual
+ ~statements_base ();
+
+ protected:
+ statements_base (connection_type& conn): conn_ (conn), svm_ (0) {}
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_STATEMENTS_BASE_HXX
diff --git a/libodb-sqlite/odb/sqlite/stream.cxx b/libodb-sqlite/odb/sqlite/stream.cxx
new file mode 100644
index 0000000..8420ba2
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/stream.cxx
@@ -0,0 +1,120 @@
+// file : odb/sqlite/stream.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <sqlite3.h>
+
+#if SQLITE_VERSION_NUMBER >= 3004000
+
+#include <odb/sqlite/stream.hxx>
+
+#include <stdexcept> // invalid_argument
+
+#include <odb/sqlite/error.hxx>
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ stream::
+ stream (const char* db,
+ const char* table,
+ const char* column,
+ long long rowid,
+ bool rw)
+ : active_object (transaction::current ().connection ())
+ {
+ int e (sqlite3_blob_open (conn_.handle (),
+ db,
+ table,
+ column,
+ static_cast<sqlite_int64> (rowid),
+ rw,
+ &h_));
+
+ if (e != SQLITE_OK)
+ translate_error (e, conn_);
+
+ list_add (); // Add ourselves to the active objects list.
+ }
+
+ size_t stream::
+ size () const
+ {
+ return static_cast<size_t> (sqlite3_blob_bytes (h_));
+ }
+
+ void stream::
+ read (void* buf, size_t n, size_t o)
+ {
+ int e (sqlite3_blob_read (
+ h_, buf, static_cast<int> (n), static_cast<int> (o)));
+
+ if (e != SQLITE_OK)
+ {
+ if (e == SQLITE_ERROR)
+ throw invalid_argument ("read past end");
+ else
+ translate_error (e, conn_);
+ }
+ }
+
+ void stream::
+ write (const void* buf, size_t n, size_t o)
+ {
+ int e (sqlite3_blob_write (
+ h_, buf, static_cast<int> (n), static_cast<int> (o)));
+
+ if (e != SQLITE_OK)
+ {
+ if (e == SQLITE_ERROR)
+ throw invalid_argument ("write past end");
+ else
+ translate_error (e, conn_);
+ }
+ }
+
+ void stream::
+ close (bool check)
+ {
+ if (h_ != 0)
+ {
+ list_remove ();
+
+ int e (sqlite3_blob_close (h_));
+ h_ = 0; // No use trying again.
+
+ if (check && e != SQLITE_OK)
+ translate_error (e, conn_);
+ }
+ }
+
+#if SQLITE_VERSION_NUMBER >= 3007004
+ void stream::
+ reopen (long long rowid)
+ {
+ int e (sqlite3_blob_reopen (h_, rowid));
+
+ if (e != SQLITE_OK)
+ // According to the SQLite documentation, the handle is now
+ // considered aborted, which means we still need to close it.
+ //
+ translate_error (e, conn_);
+ }
+#endif
+
+ void stream::
+ clear ()
+ {
+ // This call can be part of the stack unwinding, so don't check
+ // for the errors.
+ //
+ close (false);
+ }
+ }
+}
+
+#endif // SQLITE_VERSION_NUMBER >= 3004000
diff --git a/libodb-sqlite/odb/sqlite/stream.hxx b/libodb-sqlite/odb/sqlite/stream.hxx
new file mode 100644
index 0000000..6ee76cb
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/stream.hxx
@@ -0,0 +1,85 @@
+// file : odb/sqlite/stream.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_STREAM_HXX
+#define ODB_SQLITE_STREAM_HXX
+
+#include <odb/pre.hxx>
+
+#include <sqlite3.h>
+
+#include <cstddef> // std::size_t
+
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ // SQLite incremental BLOB/TEXT I/O stream. Available since
+ // SQLite 3.4.0.
+ //
+ class LIBODB_SQLITE_EXPORT stream: public active_object
+ {
+ public:
+ // @@ TODO: db is actually what we now (and SQLite in other places)
+ // call schema (see database::schema(), ATTACH DATABASE). So we
+ // should probably rename this at some point for consistency.
+ //
+ stream (const char* db,
+ const char* table,
+ const char* column,
+ long long rowid,
+ bool rw);
+
+ std::size_t
+ size () const;
+
+ // The following two functions throw std::invalid_argument if
+ // offset + n is past size().
+ //
+ void
+ read (void* buf, std::size_t n, std::size_t offset = 0);
+
+ void
+ write (const void* buf, std::size_t n, std::size_t offset = 0);
+
+ sqlite3_blob*
+ handle () const {return h_;}
+
+ // Close without reporting errors, if any.
+ //
+ virtual
+ ~stream () {close (false);}
+
+ // Close, by default with reporting errors, if any.
+ //
+ void
+ close (bool check = true);
+
+ // Open the same BLOB but in a different row. Can be faster
+ // than creating a new stream instance. Note that the stream
+ // must be in the open state prior to calling this function.
+ // Only available since SQLite 3.7.4.
+ //
+#if SQLITE_VERSION_NUMBER >= 3007004
+ void
+ reopen (long long rowid);
+#endif
+
+ protected:
+ // The active_object interface.
+ //
+ virtual void
+ clear ();
+
+ private:
+ sqlite3_blob* h_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_STREAM_HXX
diff --git a/libodb-sqlite/odb/sqlite/text-stream.hxx b/libodb-sqlite/odb/sqlite/text-stream.hxx
new file mode 100644
index 0000000..7a9b467
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/text-stream.hxx
@@ -0,0 +1,31 @@
+// file : odb/sqlite/text-stream.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_TEXT_STREAM_HXX
+#define ODB_SQLITE_TEXT_STREAM_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/sqlite/text.hxx>
+#include <odb/sqlite/stream.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class text_stream: public stream
+ {
+ public:
+ text_stream (const text& b, bool rw)
+ : stream (b.db ().c_str (),
+ b.table ().c_str (),
+ b.column ().c_str (),
+ b.rowid (),
+ rw) {}
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_TEXT_STREAM_HXX
diff --git a/libodb-sqlite/odb/sqlite/text.hxx b/libodb-sqlite/odb/sqlite/text.hxx
new file mode 100644
index 0000000..3f681fb
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/text.hxx
@@ -0,0 +1,69 @@
+// file : odb/sqlite/text.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_TEXT_HXX
+#define ODB_SQLITE_TEXT_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+// Carefully allow this header to be included into the ODB compilation.
+//
+#ifndef ODB_COMPILER
+# include <odb/sqlite/forward.hxx>
+# include <odb/sqlite/details/export.hxx>
+#endif
+
+namespace odb
+{
+ namespace sqlite
+ {
+#ifdef ODB_COMPILER
+ #pragma db sqlite:type("TEXT STREAM")
+ class text
+#else
+ class text
+#endif
+ {
+ public:
+ // TEXT size to provision for. Set before calling persist() or update().
+ //
+ explicit
+ text (std::size_t size = 0): size_ (size) {}
+
+ std::size_t size () const {return size_;}
+ void size (std::size_t s) {size_ = s;}
+
+ const std::string& db () const {return db_;}
+ const std::string& table () const {return table_;}
+ const std::string& column () const {return column_;}
+ long long rowid () const {return rowid_;}
+
+ void
+ clear ()
+ {
+ size_ = 0;
+ db_.clear ();
+ table_.clear ();
+ column_.clear ();
+ rowid_ = 0;
+ }
+
+ private:
+#ifndef ODB_COMPILER
+ friend struct default_value_traits<text, id_text_stream>;
+#endif
+ std::size_t size_;
+ mutable std::string db_;
+ mutable std::string table_;
+ mutable std::string column_;
+ mutable long long rowid_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_TEXT_HXX
diff --git a/libodb-sqlite/odb/sqlite/tracer.cxx b/libodb-sqlite/odb/sqlite/tracer.cxx
new file mode 100644
index 0000000..49f6b00
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/tracer.cxx
@@ -0,0 +1,60 @@
+// file : odb/sqlite/tracer.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/tracer.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/statement.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ tracer::
+ ~tracer ()
+ {
+ }
+
+ void tracer::
+ prepare (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ execute (connection& c, const statement& s)
+ {
+ execute (c, s.text ());
+ }
+
+ void tracer::
+ deallocate (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ prepare (odb::connection& c, const odb::statement& s)
+ {
+ prepare (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const odb::statement& s)
+ {
+ execute (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const char* s)
+ {
+ execute (static_cast<connection&> (c), s);
+ }
+
+ void tracer::
+ deallocate (odb::connection& c, const odb::statement& s)
+ {
+ deallocate (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/tracer.hxx b/libodb-sqlite/odb/sqlite/tracer.hxx
new file mode 100644
index 0000000..b12573b
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/tracer.hxx
@@ -0,0 +1,61 @@
+// file : odb/sqlite/tracer.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_TRACER_HXX
+#define ODB_SQLITE_TRACER_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/tracer.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class LIBODB_SQLITE_EXPORT tracer: private odb::tracer
+ {
+ public:
+ virtual
+ ~tracer ();
+
+ virtual void
+ prepare (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const char* statement) = 0;
+
+ virtual void
+ deallocate (connection&, const statement&);
+
+ private:
+ // Allow these classes to convert sqlite::tracer to odb::tracer.
+ //
+ friend class database;
+ friend class connection;
+ friend class transaction;
+
+ virtual void
+ prepare (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const char* statement);
+
+ virtual void
+ deallocate (odb::connection&, const odb::statement&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_TRACER_HXX
diff --git a/libodb-sqlite/odb/sqlite/traits-calls.hxx b/libodb-sqlite/odb/sqlite/traits-calls.hxx
new file mode 100644
index 0000000..9d5b59f
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/traits-calls.hxx
@@ -0,0 +1,214 @@
+// file : odb/sqlite/traits-calls.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_TRAITS_CALLS_HXX
+#define ODB_SQLITE_TRAITS_CALLS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/sqlite-types.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // object_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = object_traits_impl<T, id_sqlite>::versioned>
+ struct object_traits_calls;
+
+ template <typename T>
+ struct object_traits_calls<T, false>
+ {
+ typedef object_traits_impl<T, id_sqlite> traits;
+ typedef typename traits::image_type image_type;
+ typedef sqlite::bind bind_type;
+
+ object_traits_calls (const schema_version_migration*) {}
+
+ const schema_version_migration*
+ version () const {return 0;}
+
+ static bool
+ grow (image_type& i, bool* t)
+ {
+ return traits::grow (i, t);
+ }
+
+ static void
+ bind (bind_type* b, image_type& i, statement_kind sk)
+ {
+ traits::bind (b, i, sk);
+ }
+
+ // Poly-derived version.
+ //
+ static void
+ bind (bind_type* b,
+ const bind_type* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk)
+ {
+ traits::bind (b, id, id_size, i, sk);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+
+ static bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id)
+ {
+ return traits::find_ (sts, id);
+ }
+
+ static void
+ load_ (typename traits::statements_type& sts, T& o, bool reload)
+ {
+ return traits::load_ (sts, o, reload);
+ }
+ };
+
+ template <typename T>
+ struct object_traits_calls<T, true>
+ {
+ typedef object_traits_impl<T, id_sqlite> traits;
+ typedef typename traits::image_type image_type;
+ typedef sqlite::bind bind_type;
+
+ object_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ const schema_version_migration*
+ version () const {return &svm_;}
+
+ bool
+ grow (image_type& i, bool* t) const
+ {
+ return traits::grow (i, t, svm_);
+ }
+
+ void
+ bind (bind_type* b, image_type& i, statement_kind sk) const
+ {
+ traits::bind (b, i, sk, svm_);
+ }
+
+ // Poly-derived version.
+ //
+ void
+ bind (bind_type* b,
+ const bind_type* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk) const
+ {
+ traits::bind (b, id, id_size, i, sk, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id) const
+ {
+ return traits::find_ (sts, id, svm_);
+ }
+
+ void
+ load_ (typename traits::statements_type& sts, T& o, bool reload) const
+ {
+ return traits::load_ (sts, o, reload, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+
+ //
+ // view_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = view_traits_impl<T, id_sqlite>::versioned>
+ struct view_traits_calls;
+
+ template <typename T>
+ struct view_traits_calls<T, false>
+ {
+ typedef view_traits_impl<T, id_sqlite> traits;
+ typedef typename traits::image_type image_type;
+ typedef sqlite::bind bind_type;
+
+ view_traits_calls (const schema_version_migration*) {}
+
+ static bool
+ grow (image_type& i, bool* t)
+ {
+ return traits::grow (i, t);
+ }
+
+ static void
+ bind (bind_type* b, image_type& i)
+ {
+ traits::bind (b, i);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+ };
+
+ template <typename T>
+ struct view_traits_calls<T, true>
+ {
+ typedef view_traits_impl<T, id_sqlite> traits;
+ typedef typename traits::image_type image_type;
+ typedef sqlite::bind bind_type;
+
+ view_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ bool
+ grow (image_type& i, bool* t) const
+ {
+ return traits::grow (i, t, svm_);
+ }
+
+ void
+ bind (bind_type* b, image_type& i) const
+ {
+ traits::bind (b, i, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_TRAITS_CALLS_HXX
diff --git a/libodb-sqlite/odb/sqlite/traits.cxx b/libodb-sqlite/odb/sqlite/traits.cxx
new file mode 100644
index 0000000..a47455d
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/traits.cxx
@@ -0,0 +1,219 @@
+// file : odb/sqlite/traits.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/traits.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace sqlite
+ {
+ using details::buffer;
+
+ //
+ // default_value_traits<std::string>
+ //
+ void default_value_traits<string, id_text>::
+ set_image (buffer& b, size_t& n, bool& is_null, const string& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v.c_str (), n);
+ }
+
+ //
+ // c_string_value_traits
+ //
+ void c_string_value_traits::
+ set_image (buffer& b, size_t& n, bool& is_null, const char* v)
+ {
+ is_null = false;
+ n = strlen (v);
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+
+ //
+ // c_array_value_traits_base
+ //
+ void c_array_value_traits_base::
+ set_value (char* const& v,
+ const details::buffer& b,
+ size_t n,
+ bool is_null,
+ size_t N)
+ {
+ if (!is_null)
+ {
+ n = n < N ? n : N;
+
+ if (n != 0)
+ memcpy (v, b.data (), n);
+ }
+ else
+ n = 0;
+
+ if (n != N) // Append '\0' if there is space.
+ v[n] = '\0';
+ }
+
+ void c_array_value_traits_base::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const char* v,
+ size_t N)
+ {
+ is_null = false;
+
+ // Figure out the length. We cannot use strlen since it may
+ // not be 0-terminated (strnlen is not standard).
+ //
+ for (n = 0; n != N && v[n] != '\0'; ++n) ;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+
+ //
+ // default_value_traits<std::wstring>
+ //
+#ifdef _WIN32
+ void default_value_traits<wstring, id_text>::
+ set_image (buffer& b, size_t& n, bool& is_null, const wstring& v)
+ {
+ is_null = false;
+ n = v.size () * 2;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v.c_str (), n);
+ }
+
+ //
+ // c_wstring_value_traits
+ //
+ void c_wstring_value_traits::
+ set_image (buffer& b, size_t& n, bool& is_null, const wchar_t* v)
+ {
+ is_null = false;
+ n = wcslen (v) * 2;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+
+ //
+ // c_warray_value_traits_base
+ //
+ void c_warray_value_traits_base::
+ set_value (wchar_t* const& v,
+ const details::buffer& b,
+ size_t n,
+ bool is_null,
+ size_t N)
+ {
+ if (!is_null)
+ {
+ n /= 2;
+ n = n < N ? n : N;
+
+ if (n != 0)
+ memcpy (v, b.data (), n * sizeof (wchar_t));
+ }
+ else
+ n = 0;
+
+ if (n != N) // Append '\0' if there is space.
+ v[n] = L'\0';
+ }
+
+ void c_warray_value_traits_base::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const wchar_t* v,
+ size_t N)
+ {
+ is_null = false;
+
+ // Figure out the length. We cannot use wcslen since it may
+ // not be 0-terminated (wcsnlen is not standard).
+ //
+ for (n = 0; n != N && v[n] != L'\0'; ++n) ;
+
+ n *= 2;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+#endif // _WIN32
+
+ //
+ // default_value_traits<vector<char>, id_blob>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<char>, id_blob>::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ memcpy (b.data (), &v.front (), n);
+ }
+
+ //
+ // default_value_traits<vector<unsigned char>, id_blob>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<unsigned char>, id_blob>::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ memcpy (b.data (), &v.front (), n);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/traits.hxx b/libodb-sqlite/odb/sqlite/traits.hxx
new file mode 100644
index 0000000..a8cf578
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/traits.hxx
@@ -0,0 +1,1099 @@
+// file : odb/sqlite/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_TRAITS_HXX
+#define ODB_SQLITE_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <string>
+#include <vector>
+#include <limits> // std::numeric_limits
+#include <cstddef> // std::size_t
+#include <cstring> // std::memcpy, std::memset, std::strlen
+
+#ifdef ODB_CXX11
+# include <array>
+#endif
+
+#include <odb/traits.hxx>
+#include <odb/wrapper-traits.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/wrapper-p.hxx>
+
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/sqlite-types.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+#include <odb/sqlite/text.hxx>
+#include <odb/sqlite/blob.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ //
+ // image_traits
+ //
+
+ template <typename T, database_type_id>
+ struct image_traits;
+
+ template <typename T>
+ struct image_traits<T, id_integer> {typedef long long image_type;};
+
+ template <typename T>
+ struct image_traits<T, id_real> {typedef double image_type;};
+
+ template <typename T>
+ struct image_traits<T, id_text>
+ {
+ typedef details::buffer image_type;
+
+ // By default the text is in UTF-8.
+ //
+ static const bind::buffer_type bind_value = bind::text;
+ };
+
+ template <typename T>
+ struct image_traits<T, id_blob> {typedef details::buffer image_type;};
+
+ template <typename T>
+ struct image_traits<T, id_text_stream>
+ {
+ typedef stream_buffers image_type;
+ };
+
+ template <typename T>
+ struct image_traits<T, id_blob_stream>
+ {
+ typedef stream_buffers image_type;
+ };
+
+ //
+ // value_traits
+ //
+
+ template <typename W, database_type_id, bool null_handler>
+ struct wrapped_value_traits;
+
+ template <typename T, database_type_id>
+ struct default_value_traits;
+
+ template <typename T, database_type_id, bool w = details::wrapper_p<T>::r>
+ struct select_traits;
+
+ template <typename T, database_type_id ID>
+ struct select_traits<T, ID, false>
+ {
+ typedef default_value_traits<T, ID> type;
+ };
+
+ template <typename W, database_type_id ID>
+ struct select_traits<W, ID, true>
+ {
+ typedef
+ wrapped_value_traits<W, ID, wrapper_traits<W>::null_handler>
+ type;
+ };
+
+ template <typename T, database_type_id ID>
+ class value_traits: public select_traits<T, ID>::type
+ {
+ };
+
+ // The wrapped_value_traits specializations should be able to handle
+ // any value type which means we have to have every possible signature
+ // of the set_value() and set_image() functions.
+ //
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, false>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<wrapped_type, ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // TEXT and BLOB.
+ //
+ static void
+ set_value (W& v, const details::buffer& b, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::buffer& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // TEXT and BLOB STREAM.
+ //
+ static void
+ set_value (W& v, const stream_buffers& b, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (stream_buffers& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, true>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<wrapped_type, ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // TEXT and BLOB.
+ //
+ static void
+ set_value (W& v, const details::buffer& b, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::buffer& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // TEXT and BLOB STREAM.
+ //
+ static void
+ set_value (W& v, const stream_buffers& b, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (stream_buffers& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename T, database_type_id ID>
+ struct default_value_traits
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef typename image_traits<T, ID>::image_type image_type;
+
+ static void
+ set_value (T& v, const image_type& i, bool is_null)
+ {
+ if (!is_null)
+ v = T (i);
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, T v)
+ {
+ is_null = false;
+ i = image_type (v);
+ }
+ };
+
+ // Float & double specialization. SQLite converts NaNs to NULLs so
+ // we convert NULLs to NaNs for consistency.
+ //
+ template <typename T>
+ struct real_value_traits
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef double image_type;
+
+ static void
+ set_value (T& v, double i, bool is_null)
+ {
+ if (!is_null)
+ v = T (i);
+ else
+ v = std::numeric_limits<T>::quiet_NaN ();
+ }
+
+ static void
+ set_image (double& i, bool& is_null, T v)
+ {
+ is_null = false;
+ i = image_type (v);
+ }
+ };
+
+ template <>
+ struct default_value_traits<float, id_real>: real_value_traits<float> {};
+
+ template <>
+ struct default_value_traits<double, id_real>: real_value_traits<double> {};
+
+ // std::string specialization.
+ //
+ template <>
+ struct LIBODB_SQLITE_EXPORT default_value_traits<std::string, id_text>
+ {
+ typedef std::string value_type;
+ typedef std::string query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (std::string& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ v.assign (b.data (), n);
+ else
+ v.erase ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const std::string&);
+ };
+
+ // char*/const char* specialization
+ //
+ // Specialization for const char* which only supports initialization
+ // of an image from the value but not the other way around. This way
+ // we can pass such values to the queries.
+ //
+ struct LIBODB_SQLITE_EXPORT c_string_value_traits
+ {
+ typedef const char* value_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const char*);
+ };
+
+ template <>
+ struct default_value_traits<char*, id_text>: c_string_value_traits {};
+
+ template <>
+ struct default_value_traits<const char*, id_text>:
+ c_string_value_traits {};
+
+ // char[N] specialization.
+ //
+ struct LIBODB_SQLITE_EXPORT c_array_value_traits_base
+ {
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null,
+ std::size_t N);
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v,
+ std::size_t N);
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_text>
+ {
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (v, b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, v, N);
+ }
+ };
+
+ // std::array<char, N> (string) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_text>
+ {
+ typedef std::array<char, N> value_type;
+ typedef std::array<char, N> query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (v.data (), b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, v.data (), N);
+ }
+ };
+#endif
+
+ // char specialization.
+ //
+ template <>
+ struct default_value_traits<char, id_text>
+ {
+ typedef char value_type;
+ typedef char query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (&v, b, n, is_null, 1);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ char v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, &v, 1);
+ }
+ };
+
+#ifdef _WIN32
+ // std::wstring specialization. Using UTF-16 binding.
+ //
+ struct wstring_image_traits
+ {
+ typedef details::buffer image_type;
+ static const bind::buffer_type bind_value = bind::text16;
+ };
+
+ template <>
+ struct image_traits<std::wstring, id_text>: wstring_image_traits {};
+
+ template <>
+ struct LIBODB_SQLITE_EXPORT default_value_traits<std::wstring, id_text>
+ {
+ typedef std::wstring value_type;
+ typedef std::wstring query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (std::wstring& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ v.assign (reinterpret_cast<const wchar_t*> (b.data ()), n / 2);
+ else
+ v.erase ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const std::wstring&);
+ };
+
+ // wchar_t*/const wchar_t* specialization.
+ //
+ struct LIBODB_SQLITE_EXPORT c_wstring_value_traits
+ {
+ typedef const wchar_t* value_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const wchar_t*);
+ };
+
+ template <>
+ struct image_traits<wchar_t*, id_text>: wstring_image_traits {};
+
+ template <>
+ struct default_value_traits<wchar_t*, id_text>: c_wstring_value_traits {};
+
+ template <>
+ struct image_traits<const wchar_t*, id_text>: wstring_image_traits {};
+
+ template <>
+ struct default_value_traits<const wchar_t*, id_text>:
+ c_wstring_value_traits {};
+
+ // wchar_t[N] specialization.
+ //
+ struct LIBODB_SQLITE_EXPORT c_warray_value_traits_base
+ {
+ static void
+ set_value (wchar_t* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null,
+ std::size_t N);
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const wchar_t* v,
+ std::size_t N);
+ };
+
+ template <std::size_t N>
+ struct image_traits<wchar_t[N], id_text>: wstring_image_traits {};
+
+ template <std::size_t N>
+ struct default_value_traits<wchar_t[N], id_text>
+ {
+ typedef wchar_t* value_type;
+ typedef wchar_t query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (wchar_t* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_warray_value_traits_base::set_value (v, b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const wchar_t* v)
+ {
+ c_warray_value_traits_base::set_image (b, n, is_null, v, N);
+ }
+ };
+
+ // std::array<wchar_t, N> (string) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct image_traits<std::array<wchar_t, N>, id_text>:
+ wstring_image_traits {};
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<wchar_t, N>, id_text>
+ {
+ typedef std::array<wchar_t, N> value_type;
+ typedef std::array<wchar_t, N> query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_warray_value_traits_base::set_value (v.data (), b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_warray_value_traits_base::set_image (b, n, is_null, v.data (), N);
+ }
+ };
+#endif
+
+ // wchar_t specialization.
+ //
+ template <>
+ struct image_traits<wchar_t, id_text>: wstring_image_traits {};
+
+ template <>
+ struct default_value_traits<wchar_t, id_text>
+ {
+ typedef wchar_t value_type;
+ typedef wchar_t query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (wchar_t& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_warray_value_traits_base::set_value (&v, b, n, is_null, 1);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ wchar_t v)
+ {
+ c_warray_value_traits_base::set_image (b, n, is_null, &v, 1);
+ }
+ };
+#endif // _WIN32
+
+ // std::vector<char> (buffer) specialization.
+ //
+ template <>
+ struct LIBODB_SQLITE_EXPORT default_value_traits<
+ std::vector<char>, id_blob>
+ {
+ public:
+ typedef std::vector<char> value_type;
+ typedef std::vector<char> query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ v.assign (b.data (), b.data () + n);
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const value_type&);
+ };
+
+ // std::vector<unsigned char> (buffer) specialization.
+ //
+ template <>
+ struct LIBODB_SQLITE_EXPORT default_value_traits<
+ std::vector<unsigned char>, id_blob>
+ {
+ public:
+ typedef std::vector<unsigned char> value_type;
+ typedef std::vector<unsigned char> query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ {
+ const unsigned char* d (
+ reinterpret_cast<const unsigned char*> (b.data ()));
+ v.assign (d, d + n);
+ }
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const value_type&);
+ };
+
+ // char[N] (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_blob>
+ {
+ public:
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b.data (), (n < N ? n : N));
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v, n);
+ }
+ };
+
+ // unsigned char[N] (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<unsigned char[N], id_blob>
+ {
+ public:
+ typedef unsigned char* value_type;
+ typedef unsigned char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (unsigned char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b.data (), (n < N ? n : N));
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const unsigned char* v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v, n);
+ }
+ };
+
+#ifdef ODB_CXX11
+ // std::array<char, N> (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_blob>
+ {
+ public:
+ typedef std::array<char, N> value_type;
+ typedef value_type query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b.data (), (n < N ? n : N));
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data (), n);
+ }
+ };
+
+ // std::array<unsigned char, N> (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<unsigned char, N>, id_blob>
+ {
+ public:
+ typedef std::array<unsigned char, N> value_type;
+ typedef value_type query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b.data (), (n < N ? n : N));
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data (), n);
+ }
+ };
+#endif
+
+ // text (stream) specialization.
+ //
+ template <>
+ struct default_value_traits<text, id_text_stream>
+ {
+ public:
+ typedef text value_type;
+ typedef std::string query_type;
+ typedef stream_buffers image_type;
+
+ static void
+ set_value (text& v, const stream_buffers& b, std::size_t, bool is_null)
+ {
+ if (!is_null)
+ {
+ v.db_ = b.db.in;
+ v.table_ = b.table.in;
+ v.column_ = b.column.in;
+ v.rowid_ = b.rowid.in;
+ }
+ }
+
+ static void
+ set_image (stream_buffers& b,
+ std::size_t& n,
+ bool& is_null,
+ const text& v)
+ {
+ is_null = false;
+ n = v.size_;
+
+ b.db.out = &v.db_;
+ b.table.out = &v.table_;
+ b.column.out = &v.column_;
+ b.rowid.out = &v.rowid_;
+ }
+ };
+
+ // blob (stream) specialization.
+ //
+ template <>
+ struct default_value_traits<blob, id_blob_stream>
+ {
+ public:
+ typedef blob value_type;
+ typedef std::vector<char> query_type;
+ typedef stream_buffers image_type;
+
+ static void
+ set_value (blob& v, const stream_buffers& b, std::size_t, bool is_null)
+ {
+ if (!is_null)
+ {
+ v.db_ = b.db.in;
+ v.table_ = b.table.in;
+ v.column_ = b.column.in;
+ v.rowid_ = b.rowid.in;
+ }
+ }
+
+ static void
+ set_image (stream_buffers& b,
+ std::size_t& n,
+ bool& is_null,
+ const blob& v)
+ {
+ is_null = false;
+ n = v.size_;
+
+ b.db.out = &v.db_;
+ b.table.out = &v.table_;
+ b.column.out = &v.column_;
+ b.rowid.out = &v.rowid_;
+ }
+ };
+
+ //
+ // type_traits
+ //
+
+ template <typename T>
+ struct default_type_traits;
+
+ template <typename T>
+ class type_traits: public default_type_traits<T> {};
+
+ // Integral types.
+ //
+ template <>
+ struct default_type_traits<bool>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<signed char>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<unsigned char>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<short>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<unsigned short>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<int>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<unsigned int>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<long>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<long long>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long long>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ // Float types.
+ //
+ template <>
+ struct default_type_traits<float>
+ {
+ static const database_type_id db_type_id = id_real;
+ };
+
+ template <>
+ struct default_type_traits<double>
+ {
+ static const database_type_id db_type_id = id_real;
+ };
+
+ // String types.
+ //
+ template <>
+ struct default_type_traits<std::string>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+
+ template <>
+ struct default_type_traits<char*>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+
+ template <>
+ struct default_type_traits<const char*>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+
+ template <std::size_t N>
+ struct default_type_traits<char[N]>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<char, N> >
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+#endif
+
+ template <>
+ struct default_type_traits<char>
+ {
+ static const database_type_id db_type_id = id_text;
+ };
+
+ // Binary types.
+ //
+ template <std::size_t N>
+ struct default_type_traits<unsigned char[N]>
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<char> >
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<unsigned char> >
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<unsigned char, N> >
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+#endif
+
+ template <>
+ struct default_type_traits<text>
+ {
+ static const database_type_id db_type_id = id_text_stream;
+ };
+
+ template <>
+ struct default_type_traits<blob>
+ {
+ static const database_type_id db_type_id = id_blob_stream;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_TRAITS_HXX
diff --git a/libodb-sqlite/odb/sqlite/transaction-impl.cxx b/libodb-sqlite/odb/sqlite/transaction-impl.cxx
new file mode 100644
index 0000000..6485f7e
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/transaction-impl.cxx
@@ -0,0 +1,172 @@
+// file : odb/sqlite/transaction-impl.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <sqlite3.h>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/connection.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/transaction-impl.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ transaction_impl::
+ transaction_impl (database_type& db, lock l)
+ : odb::transaction_impl (db), lock_ (l)
+ {
+ }
+
+ transaction_impl::
+ transaction_impl (connection_ptr c, lock l)
+ : odb::transaction_impl (c->database (), *c),
+ connection_ (c),
+ lock_ (l)
+ {
+ }
+
+ transaction_impl::
+ ~transaction_impl ()
+ {
+ }
+
+ void transaction_impl::
+ start ()
+ {
+ // Grab a connection if we don't already have one.
+ //
+ if (connection_ == 0)
+ {
+ connection_ = static_cast<database_type&> (database_).connection ();
+ odb::transaction_impl::connection_ = connection_.get ();
+ }
+
+ connection_type& mc (connection_->main_connection ());
+
+ switch (lock_)
+ {
+ case deferred:
+ {
+ mc.begin_statement ().execute ();
+ break;
+ }
+ case immediate:
+ {
+ mc.begin_immediate_statement ().execute ();
+ break;
+ }
+ case exclusive:
+ {
+ mc.begin_exclusive_statement ().execute ();
+ break;
+ }
+ }
+ }
+
+ // In SQLite, when a commit fails (e.g., because of the deferred
+ // foreign key constraint violation), the transaction may not
+ // be automatically rolled back. So we have to do it ourselves.
+ //
+ struct commit_guard
+ {
+ commit_guard (connection& c): c_ (&c) {}
+ void release () {c_ = 0;}
+
+ ~commit_guard ()
+ {
+ if (c_ != 0 && sqlite3_get_autocommit (c_->handle ()) == 0)
+ {
+ // This is happening while another exception is active.
+ //
+ try
+ {
+ c_->rollback_statement ().execute ();
+ }
+ catch (...) {}
+ }
+ }
+
+ private:
+ connection* c_;
+ };
+
+ void transaction_impl::
+ commit ()
+ {
+ connection_type& mc (connection_->main_connection ());
+
+ // Invalidate query results and reset active statements.
+ //
+ // Active statements will prevent COMMIT from completing (write
+ // statements) or releasing the locks (read statements). Normally, a
+ // statement is automatically reset on completion, however, if an
+ // exception is thrown, that may not happen.
+ //
+ // Note: must be done via the main connection.
+ //
+ mc.clear ();
+
+ {
+ commit_guard cg (mc);
+ mc.commit_statement ().execute ();
+ cg.release ();
+ }
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+
+ void transaction_impl::
+ rollback ()
+ {
+ connection_type& mc (connection_->main_connection ());
+
+ // Invalidate query results and reset active statements (the same
+ // reasoning as in commit()).
+ //
+ // Note: must be done via the main connection.
+ //
+ mc.clear ();
+
+ mc.rollback_statement ().execute ();
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+
+ odb::connection& transaction_impl::
+ connection (odb::database* pdb)
+ {
+ if (pdb == 0)
+ return *connection_;
+
+ // Pick the corresponding connection for main/attached database.
+ //
+ database_type& db (static_cast<database_type&> (*pdb));
+
+ assert (&db.main_database () ==
+ &static_cast<database_type&> (database_).main_database ());
+
+ return db.schema ().empty ()
+ ? connection_->main_connection ()
+ : *static_cast<attached_connection_factory&> (*db.factory_).attached_connection_;
+ }
+
+ // Store transaction tracer in the main connection.
+ //
+ void transaction_impl::
+ tracer (odb::tracer* t)
+ {
+ connection_->main_connection ().transaction_tracer_ = t;
+ }
+
+ odb::tracer* transaction_impl::
+ tracer () const
+ {
+ return connection_->main_connection ().transaction_tracer_;
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/transaction-impl.hxx b/libodb-sqlite/odb/sqlite/transaction-impl.hxx
new file mode 100644
index 0000000..d1a310b
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/transaction-impl.hxx
@@ -0,0 +1,66 @@
+// file : odb/sqlite/transaction-impl.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_TRANSACTION_IMPL_HXX
+#define ODB_SQLITE_TRANSACTION_IMPL_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class LIBODB_SQLITE_EXPORT transaction_impl: public odb::transaction_impl
+ {
+ public:
+ typedef sqlite::database database_type;
+ typedef sqlite::connection connection_type;
+
+ enum lock
+ {
+ deferred,
+ immediate,
+ exclusive
+ };
+
+ transaction_impl (database_type&, lock);
+ transaction_impl (connection_ptr, lock);
+
+ virtual
+ ~transaction_impl ();
+
+ virtual void
+ start ();
+
+ virtual void
+ commit ();
+
+ virtual void
+ rollback ();
+
+ virtual odb::connection&
+ connection (odb::database*);
+
+ virtual void
+ tracer (odb::tracer*);
+
+ virtual odb::tracer*
+ tracer () const;
+
+ private:
+ connection_ptr connection_;
+ lock lock_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_TRANSACTION_IMPL_HXX
diff --git a/libodb-sqlite/odb/sqlite/transaction.cxx b/libodb-sqlite/odb/sqlite/transaction.cxx
new file mode 100644
index 0000000..8b4ab23
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/transaction.cxx
@@ -0,0 +1,26 @@
+// file : odb/sqlite/transaction.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/sqlite/transaction.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ transaction& transaction::
+ current ()
+ {
+ // While the impl type can be of the concrete type, the transaction
+ // object can be created as either odb:: or odb::sqlite:: type. To
+ // work around that we are going to hard-cast one two the other
+ // relying on the fact that they have the same representation and
+ // no virtual functions. The former is checked in the tests.
+ //
+ odb::transaction& b (odb::transaction::current ());
+ assert (dynamic_cast<transaction_impl*> (&b.implementation ()) != 0);
+ return reinterpret_cast<transaction&> (b);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/transaction.hxx b/libodb-sqlite/odb/sqlite/transaction.hxx
new file mode 100644
index 0000000..5e8c141
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/transaction.hxx
@@ -0,0 +1,87 @@
+// file : odb/sqlite/transaction.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_TRANSACTION_HXX
+#define ODB_SQLITE_TRANSACTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx>
+#include <odb/sqlite/tracer.hxx>
+#include <odb/sqlite/details/export.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ class transaction_impl;
+
+ class LIBODB_SQLITE_EXPORT transaction: public odb::transaction
+ {
+ public:
+ typedef sqlite::database database_type;
+ typedef sqlite::connection connection_type;
+
+ explicit
+ transaction (transaction_impl*, bool make_current = true);
+
+ transaction ();
+
+ // Return the database this transaction is on.
+ //
+ database_type&
+ database ();
+
+ // Return the underlying database connection for this transaction.
+ //
+ connection_type&
+ connection ();
+
+ connection_type&
+ connection (odb::database&);
+
+ // Return current transaction or throw if there is no transaction
+ // in effect.
+ //
+ static transaction&
+ current ();
+
+ // Set the current thread's transaction.
+ //
+ static void
+ current (transaction&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef sqlite::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ using odb::transaction::tracer;
+
+ public:
+ transaction_impl&
+ implementation ();
+ };
+ }
+}
+
+#include <odb/sqlite/transaction.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_TRANSACTION_HXX
diff --git a/libodb-sqlite/odb/sqlite/transaction.ixx b/libodb-sqlite/odb/sqlite/transaction.ixx
new file mode 100644
index 0000000..de4bd3e
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/transaction.ixx
@@ -0,0 +1,57 @@
+// file : odb/sqlite/transaction.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/transaction-impl.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ inline transaction::
+ transaction (transaction_impl* impl, bool make_current)
+ : odb::transaction (impl, make_current)
+ {
+ }
+
+ inline transaction::
+ transaction ()
+ : odb::transaction ()
+ {
+ }
+
+ inline transaction_impl& transaction::
+ implementation ()
+ {
+ // We can use static_cast here since we have an instance of
+ // sqlite::transaction.
+ //
+ return static_cast<transaction_impl&> (
+ odb::transaction::implementation ());
+ }
+
+ inline transaction::database_type& transaction::
+ database ()
+ {
+ return static_cast<database_type&> (odb::transaction::database ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection ()
+ {
+ return static_cast<connection_type&> (odb::transaction::connection ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection (odb::database& db)
+ {
+ return static_cast<connection_type&> (odb::transaction::connection (db));
+ }
+
+ inline void transaction::
+ current (transaction& t)
+ {
+ odb::transaction::current (t);
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/version-build2-stub.hxx b/libodb-sqlite/odb/sqlite/version-build2-stub.hxx
new file mode 100644
index 0000000..a6bb4d0
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/version-build2-stub.hxx
@@ -0,0 +1,4 @@
+// file : odb/sqlite/version-build2-stub.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/sqlite/version.hxx>
diff --git a/libodb-sqlite/odb/sqlite/version-build2.hxx b/libodb-sqlite/odb/sqlite/version-build2.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/version-build2.hxx
diff --git a/libodb-sqlite/odb/sqlite/version-build2.hxx.in b/libodb-sqlite/odb/sqlite/version-build2.hxx.in
new file mode 100644
index 0000000..50fef2d
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/version-build2.hxx.in
@@ -0,0 +1,42 @@
+// file : odb/sqlite/version-build2.hxx.in
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef LIBODB_SQLITE_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_SQLITE_VERSION $libodb_sqlite.version.project_number$ULL
+#define LIBODB_SQLITE_VERSION_STR "$libodb_sqlite.version.project$"
+#define LIBODB_SQLITE_VERSION_ID "$libodb_sqlite.version.project_id$"
+
+#define LIBODB_SQLITE_VERSION_MAJOR $libodb_sqlite.version.major$
+#define LIBODB_SQLITE_VERSION_MINOR $libodb_sqlite.version.minor$
+#define LIBODB_SQLITE_VERSION_PATCH $libodb_sqlite.version.patch$
+
+#define LIBODB_SQLITE_PRE_RELEASE $libodb_sqlite.version.pre_release$
+
+#define LIBODB_SQLITE_SNAPSHOT $libodb_sqlite.version.snapshot_sn$ULL
+#define LIBODB_SQLITE_SNAPSHOT_ID "$libodb_sqlite.version.snapshot_id$"
+
+#include <odb/version.hxx>
+
+$libodb.check(LIBODB_VERSION, LIBODB_SNAPSHOT)$
+
+#endif // LIBODB_SQLITE_VERSION
diff --git a/libodb-sqlite/odb/sqlite/version.hxx b/libodb-sqlite/odb/sqlite/version.hxx
new file mode 100644
index 0000000..ad468de
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/version.hxx
@@ -0,0 +1,47 @@
+// file : odb/sqlite/version.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifdef LIBODB_SQLITE_BUILD2
+# include <odb/sqlite/version-build2.hxx>
+#else
+
+#ifndef ODB_SQLITE_VERSION_HXX
+#define ODB_SQLITE_VERSION_HXX
+
+#include <odb/pre.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-sqlite version: odb interface version plus the bugfix
+// version.
+//
+#define LIBODB_SQLITE_VERSION 2049976
+#define LIBODB_SQLITE_VERSION_STR "2.5.0-b.26"
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_VERSION_HXX
+#endif // LIBODB_SQLITE_BUILD2
diff --git a/libodb-sqlite/odb/sqlite/view-result.hxx b/libodb-sqlite/odb/sqlite/view-result.hxx
new file mode 100644
index 0000000..ce3d747
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/view-result.hxx
@@ -0,0 +1,80 @@
+// file : odb/sqlite/view-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_VIEW_RESULT_HXX
+#define ODB_SQLITE_VIEW_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/view-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/forward.hxx> // query_base, query_params
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/traits-calls.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ class view_result_impl: public odb::view_result_impl<T>
+ {
+ public:
+ typedef odb::view_result_impl<T> base_type;
+
+ typedef typename base_type::view_type view_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef view_traits_impl<view_type, id_sqlite> view_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef view_statements<view_type> statements_type;
+
+ virtual
+ ~view_result_impl ();
+
+ view_result_impl (const query_base&,
+ const details::shared_ptr<select_statement>&,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (view_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ // We need to hold on to the query parameters because SQLite uses
+ // the parameter buffers to find each next row.
+ //
+ details::shared_ptr<query_params> params_;
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ view_traits_calls<view_type> tc_;
+ };
+ }
+}
+
+#include <odb/sqlite/view-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_VIEW_RESULT_HXX
diff --git a/libodb-sqlite/odb/sqlite/view-result.txx b/libodb-sqlite/odb/sqlite/view-result.txx
new file mode 100644
index 0000000..60efb81
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/view-result.txx
@@ -0,0 +1,114 @@
+// file : odb/sqlite/view-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/sqlite/view-statements.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ view_result_impl<T>::
+ ~view_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ params_.reset ();
+ statement_.reset ();
+ }
+
+ template <typename T>
+ view_result_impl<T>::
+ view_result_impl (const query_base& q,
+ const details::shared_ptr<select_statement>& s,
+ statements_type& sts,
+ const schema_version_migration* svm)
+ : base_type (sts.connection ()),
+ params_ (q.parameters ()),
+ statement_ (s),
+ statements_ (sts),
+ tc_ (svm)
+ {
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ load (view_type& view)
+ {
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ typename view_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.image_version ())
+ {
+ binding& b (statements_.image_binding ());
+ tc_.bind (b.bind, im);
+ statements_.image_version (im.version);
+ b.version++;
+ }
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ if (tc_.grow (im, statements_.image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.image_version ())
+ {
+ binding& b (statements_.image_binding ());
+ tc_.bind (b.bind, im);
+ statements_.image_version (im.version);
+ b.version++;
+ statement_->reload ();
+ }
+ }
+
+ view_traits::callback (this->db_, view, callback_event::pre_load);
+ tc_.init (view, im, &this->db_);
+ view_traits::callback (this->db_, view, callback_event::post_load);
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (!statement_->next ())
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t view_result_impl<T>::
+ size ()
+ {
+ throw result_not_cached ();
+ }
+ }
+}
diff --git a/libodb-sqlite/odb/sqlite/view-statements.hxx b/libodb-sqlite/odb/sqlite/view-statements.hxx
new file mode 100644
index 0000000..0bd79ee
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/view-statements.hxx
@@ -0,0 +1,88 @@
+// file : odb/sqlite/view-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SQLITE_VIEW_STATEMENTS_HXX
+#define ODB_SQLITE_VIEW_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/sqlite/version.hxx>
+#include <odb/sqlite/binding.hxx>
+#include <odb/sqlite/statement.hxx>
+#include <odb/sqlite/statements-base.hxx>
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ class view_statements: public statements_base
+ {
+ public:
+ typedef T view_type;
+ typedef view_traits_impl<view_type, id_sqlite> view_traits;
+ typedef typename view_traits::pointer_type pointer_type;
+ typedef typename view_traits::image_type image_type;
+
+ public:
+ view_statements (connection_type&);
+
+ virtual
+ ~view_statements ();
+
+ // View image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ std::size_t
+ image_version () const
+ {
+ return image_version_;
+ }
+
+ void
+ image_version (std::size_t v)
+ {
+ image_version_ = v;
+ }
+
+ binding&
+ image_binding ()
+ {
+ return image_binding_;
+ }
+
+ bool*
+ image_truncated ()
+ {
+ return image_truncated_;
+ }
+
+ private:
+ view_statements (const view_statements&);
+ view_statements& operator= (const view_statements&);
+
+ private:
+ image_type image_;
+ std::size_t image_version_;
+ binding image_binding_;
+ bind image_bind_[view_traits::column_count];
+ bool image_truncated_[view_traits::column_count];
+ };
+ }
+}
+
+#include <odb/sqlite/view-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SQLITE_VIEW_STATEMENTS_HXX
diff --git a/libodb-sqlite/odb/sqlite/view-statements.txx b/libodb-sqlite/odb/sqlite/view-statements.txx
new file mode 100644
index 0000000..0531a92
--- /dev/null
+++ b/libodb-sqlite/odb/sqlite/view-statements.txx
@@ -0,0 +1,33 @@
+// file : odb/sqlite/view-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace sqlite
+ {
+ template <typename T>
+ view_statements<T>::
+ ~view_statements ()
+ {
+ }
+
+ template <typename T>
+ view_statements<T>::
+ view_statements (connection_type& conn)
+ : statements_base (conn),
+ image_binding_ (image_bind_, view_traits::column_count)
+ {
+ image_.version = 0;
+ image_version_ = 0;
+
+ std::memset (image_bind_, 0, sizeof (image_bind_));
+ std::memset (image_truncated_, 0, sizeof (image_truncated_));
+
+ for (std::size_t i (0); i < view_traits::column_count; ++i)
+ image_bind_[i].truncated = image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-sqlite/tests/.gitignore b/libodb-sqlite/tests/.gitignore
new file mode 100644
index 0000000..e54525b
--- /dev/null
+++ b/libodb-sqlite/tests/.gitignore
@@ -0,0 +1 @@
+driver
diff --git a/libodb-sqlite/tests/basics/buildfile b/libodb-sqlite/tests/basics/buildfile
new file mode 100644
index 0000000..5d671d3
--- /dev/null
+++ b/libodb-sqlite/tests/basics/buildfile
@@ -0,0 +1,6 @@
+# file : tests/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libs = libodb-sqlite%lib{odb-sqlite}
+
+exe{driver}: {hxx cxx}{*} $libs
diff --git a/libodb-sqlite/tests/basics/driver.cxx b/libodb-sqlite/tests/basics/driver.cxx
new file mode 100644
index 0000000..b998574
--- /dev/null
+++ b/libodb-sqlite/tests/basics/driver.cxx
@@ -0,0 +1,52 @@
+// file : tests/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Basic test to make sure the library is usable. Functionality testing
+// is done in the odb-tests package.
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/sqlite/database.hxx>
+#include <odb/sqlite/exceptions.hxx>
+#include <odb/sqlite/transaction.hxx>
+
+using namespace odb::sqlite;
+
+int
+main ()
+{
+ {
+ std::ostringstream os;
+ database::print_usage (os);
+ assert (!os.str ().empty ());
+ }
+
+ database db (":memory:");
+
+ {
+ transaction t (db.begin ());
+ db.execute ("CREATE TABLE test (id INTEGER PRIMARY KEY, str TEXT)");
+ t.commit ();
+ }
+
+ {
+ transaction t (db.begin ());
+ db.execute ("INSERT INTO test VALUES (123, 'abc')");
+ t.commit ();
+ }
+
+ try
+ {
+ transaction t (db.begin ());
+ db.execute ("INSERT INTO test VALUES (123, 'ABC')");
+ assert (false);
+ }
+ catch (const database_exception&) {}
+
+ {
+ transaction t (db.begin ());
+ db.execute ("DROP TABLE test");
+ t.commit ();
+ }
+}
diff --git a/libodb-sqlite/tests/build/.gitignore b/libodb-sqlite/tests/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-sqlite/tests/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-sqlite/tests/build/bootstrap.build b/libodb-sqlite/tests/build/bootstrap.build
new file mode 100644
index 0000000..6ee38db
--- /dev/null
+++ b/libodb-sqlite/tests/build/bootstrap.build
@@ -0,0 +1,8 @@
+# file : tests/build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = # Unnamed subproject.
+
+using config
+using dist
+using test
diff --git a/libodb-sqlite/tests/build/root.build b/libodb-sqlite/tests/build/root.build
new file mode 100644
index 0000000..6c5a90b
--- /dev/null
+++ b/libodb-sqlite/tests/build/root.build
@@ -0,0 +1,23 @@
+# file : tests/build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+cxx{*}: extension = cxx
+
+if ($cxx.target.system == 'win32-msvc')
+ cxx.poptions += -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS
+
+if ($cxx.class == 'msvc')
+ cxx.coptions += /wd4251 /wd4275 /wd4800
+
+# Every exe{} in this subproject is by default a test.
+#
+exe{*}: test = true
+
+# Specify the test target for cross-testing.
+#
+test.target = $cxx.target
diff --git a/libodb-sqlite/tests/buildfile b/libodb-sqlite/tests/buildfile
new file mode 100644
index 0000000..57588a4
--- /dev/null
+++ b/libodb-sqlite/tests/buildfile
@@ -0,0 +1,4 @@
+# file : tests/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/}