summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2024-01-25 18:53:34 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2024-01-25 18:53:34 +0300
commit81ca6a1aed69885827b06f09fb7926e39b5c25a2 (patch)
tree095aec8e4250f99422f20f78d45fdbf8830a1102
parent4260db6bc3cb9fb3d47d2d4cf0a7b6d49cf1e919 (diff)
parent3a160a80c788d81e48acf19a2cf68f29cf125dae (diff)
Merge branch 'libodb-mysql' into multi-package
-rw-r--r--libodb-mysql/.gitignore25
-rw-r--r--libodb-mysql/GPLv2340
-rw-r--r--libodb-mysql/INSTALL6
-rw-r--r--libodb-mysql/LICENSE20
-rw-r--r--libodb-mysql/README20
-rw-r--r--libodb-mysql/build/.gitignore3
-rw-r--r--libodb-mysql/build/bootstrap.build10
-rw-r--r--libodb-mysql/build/export.build9
-rw-r--r--libodb-mysql/build/root.build45
-rw-r--r--libodb-mysql/buildfile9
-rw-r--r--libodb-mysql/manifest25
-rw-r--r--libodb-mysql/odb/mysql/auto-handle.hxx94
-rw-r--r--libodb-mysql/odb/mysql/binding.hxx45
-rw-r--r--libodb-mysql/odb/mysql/buildfile165
-rw-r--r--libodb-mysql/odb/mysql/connection-factory.cxx301
-rw-r--r--libodb-mysql/odb/mysql/connection-factory.hxx140
-rw-r--r--libodb-mysql/odb/mysql/connection.cxx218
-rw-r--r--libodb-mysql/odb/mysql/connection.hxx231
-rw-r--r--libodb-mysql/odb/mysql/connection.ixx44
-rw-r--r--libodb-mysql/odb/mysql/container-statements.hxx360
-rw-r--r--libodb-mysql/odb/mysql/container-statements.txx107
-rw-r--r--libodb-mysql/odb/mysql/database.cxx357
-rw-r--r--libodb-mysql/odb/mysql/database.hxx540
-rw-r--r--libodb-mysql/odb/mysql/database.ixx617
-rw-r--r--libodb-mysql/odb/mysql/details/build2/config-stub.h5
-rw-r--r--libodb-mysql/odb/mysql/details/build2/config-vc-stub.h5
-rw-r--r--libodb-mysql/odb/mysql/details/build2/config-vc.h15
-rw-r--r--libodb-mysql/odb/mysql/details/build2/config.h17
-rw-r--r--libodb-mysql/odb/mysql/details/config-vc.h12
-rw-r--r--libodb-mysql/odb/mysql/details/config.h.in17
-rw-r--r--libodb-mysql/odb/mysql/details/config.hxx32
-rw-r--r--libodb-mysql/odb/mysql/details/conversion.hxx58
-rw-r--r--libodb-mysql/odb/mysql/details/export.hxx78
-rw-r--r--libodb-mysql/odb/mysql/details/options.cli61
-rw-r--r--libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.cxx1125
-rw-r--r--libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.hxx570
-rw-r--r--libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.ixx384
-rw-r--r--libodb-mysql/odb/mysql/enum.cxx28
-rw-r--r--libodb-mysql/odb/mysql/enum.hxx129
-rw-r--r--libodb-mysql/odb/mysql/error.cxx76
-rw-r--r--libodb-mysql/odb/mysql/error.hxx34
-rw-r--r--libodb-mysql/odb/mysql/exceptions.cxx71
-rw-r--r--libodb-mysql/odb/mysql/exceptions.hxx87
-rw-r--r--libodb-mysql/odb/mysql/forward.hxx91
-rw-r--r--libodb-mysql/odb/mysql/mysql-types.hxx42
-rw-r--r--libodb-mysql/odb/mysql/mysql.hxx31
-rw-r--r--libodb-mysql/odb/mysql/no-id-object-result.hxx81
-rw-r--r--libodb-mysql/odb/mysql/no-id-object-result.txx178
-rw-r--r--libodb-mysql/odb/mysql/no-id-object-statements.hxx135
-rw-r--r--libodb-mysql/odb/mysql/no-id-object-statements.txx36
-rw-r--r--libodb-mysql/odb/mysql/polymorphic-object-result.hxx94
-rw-r--r--libodb-mysql/odb/mysql/polymorphic-object-result.txx370
-rw-r--r--libodb-mysql/odb/mysql/polymorphic-object-statements.hxx474
-rw-r--r--libodb-mysql/odb/mysql/polymorphic-object-statements.txx145
-rw-r--r--libodb-mysql/odb/mysql/prepared-query.cxx15
-rw-r--r--libodb-mysql/odb/mysql/prepared-query.hxx34
-rw-r--r--libodb-mysql/odb/mysql/query-const-expr.cxx14
-rw-r--r--libodb-mysql/odb/mysql/query-dynamic.cxx157
-rw-r--r--libodb-mysql/odb/mysql/query-dynamic.hxx32
-rw-r--r--libodb-mysql/odb/mysql/query-dynamic.ixx26
-rw-r--r--libodb-mysql/odb/mysql/query-dynamic.txx20
-rw-r--r--libodb-mysql/odb/mysql/query.cxx346
-rw-r--r--libodb-mysql/odb/mysql/query.hxx2277
-rw-r--r--libodb-mysql/odb/mysql/query.ixx34
-rw-r--r--libodb-mysql/odb/mysql/query.txx168
-rw-r--r--libodb-mysql/odb/mysql/section-statements.hxx200
-rw-r--r--libodb-mysql/odb/mysql/section-statements.txx40
-rw-r--r--libodb-mysql/odb/mysql/simple-object-result.hxx85
-rw-r--r--libodb-mysql/odb/mysql/simple-object-result.txx235
-rw-r--r--libodb-mysql/odb/mysql/simple-object-statements.cxx15
-rw-r--r--libodb-mysql/odb/mysql/simple-object-statements.hxx587
-rw-r--r--libodb-mysql/odb/mysql/simple-object-statements.ixx68
-rw-r--r--libodb-mysql/odb/mysql/simple-object-statements.txx146
-rw-r--r--libodb-mysql/odb/mysql/statement-cache.hxx60
-rw-r--r--libodb-mysql/odb/mysql/statement-cache.txx60
-rw-r--r--libodb-mysql/odb/mysql/statement.cxx814
-rw-r--r--libodb-mysql/odb/mysql/statement.hxx327
-rw-r--r--libodb-mysql/odb/mysql/statements-base.cxx15
-rw-r--r--libodb-mysql/odb/mysql/statements-base.hxx63
-rw-r--r--libodb-mysql/odb/mysql/tracer.cxx60
-rw-r--r--libodb-mysql/odb/mysql/tracer.hxx61
-rw-r--r--libodb-mysql/odb/mysql/traits-calls.hxx210
-rw-r--r--libodb-mysql/odb/mysql/traits.cxx145
-rw-r--r--libodb-mysql/odb/mysql/traits.hxx988
-rw-r--r--libodb-mysql/odb/mysql/transaction-impl.cxx108
-rw-r--r--libodb-mysql/odb/mysql/transaction-impl.hxx49
-rw-r--r--libodb-mysql/odb/mysql/transaction.cxx26
-rw-r--r--libodb-mysql/odb/mysql/transaction.hxx88
-rw-r--r--libodb-mysql/odb/mysql/transaction.ixx57
-rw-r--r--libodb-mysql/odb/mysql/version-build2-stub.hxx4
-rw-r--r--libodb-mysql/odb/mysql/version-build2.hxx0
-rw-r--r--libodb-mysql/odb/mysql/version-build2.hxx.in62
-rw-r--r--libodb-mysql/odb/mysql/version.hxx61
-rw-r--r--libodb-mysql/odb/mysql/view-result.hxx81
-rw-r--r--libodb-mysql/odb/mysql/view-result.txx178
-rw-r--r--libodb-mysql/odb/mysql/view-statements.hxx88
-rw-r--r--libodb-mysql/odb/mysql/view-statements.txx33
-rw-r--r--libodb-mysql/tests/.gitignore1
-rw-r--r--libodb-mysql/tests/basics/buildfile6
-rw-r--r--libodb-mysql/tests/basics/driver.cxx37
-rw-r--r--libodb-mysql/tests/build/.gitignore3
-rw-r--r--libodb-mysql/tests/build/bootstrap.build8
-rw-r--r--libodb-mysql/tests/build/root.build23
-rw-r--r--libodb-mysql/tests/buildfile4
104 files changed, 16431 insertions, 0 deletions
diff --git a/libodb-mysql/.gitignore b/libodb-mysql/.gitignore
new file mode 100644
index 0000000..1c363a0
--- /dev/null
+++ b/libodb-mysql/.gitignore
@@ -0,0 +1,25 @@
+# Compiler/linker output.
+#
+*.d
+*.t
+*.i
+*.i.*
+*.ii
+*.ii.*
+*.o
+*.obj
+*.gcm
+*.pcm
+*.ifc
+*.so
+*.dylib
+*.dll
+*.a
+*.lib
+*.exp
+*.pdb
+*.ilk
+*.exe
+*.exe.dlls/
+*.exe.manifest
+*.pc
diff --git a/libodb-mysql/GPLv2 b/libodb-mysql/GPLv2
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/libodb-mysql/GPLv2
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/libodb-mysql/INSTALL b/libodb-mysql/INSTALL
new file mode 100644
index 0000000..ef46275
--- /dev/null
+++ b/libodb-mysql/INSTALL
@@ -0,0 +1,6 @@
+The easiest way to build this package is with the bpkg package manager:
+
+$ bpkg build libodb-mysql
+
+But if you don't want to use the package manager, then you can also build it
+manually using the standard build2 build system.
diff --git a/libodb-mysql/LICENSE b/libodb-mysql/LICENSE
new file mode 100644
index 0000000..9d92da1
--- /dev/null
+++ b/libodb-mysql/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-mysql/README b/libodb-mysql/README
new file mode 100644
index 0000000..5e2315a
--- /dev/null
+++ b/libodb-mysql/README
@@ -0,0 +1,20 @@
+ODB is an object-relational mapping (ORM) system for C++. It provides
+tools, APIs, and library support that allow you to persist C++ objects
+to a relational database (RDBMS) without having to deal with tables,
+columns, or SQL and without manually writing any of the mapping code.
+For more information see:
+
+http://www.codesynthesis.com/products/odb/
+
+This package contains the MySQL ODB runtime library. Every application
+that includes code generated for the MySQL database will need to link
+to this library.
+
+See the NEWS file for the user-visible changes from the previous release.
+
+See the LICENSE file for distribution conditions.
+
+See the INSTALL file for prerequisites and installation instructions.
+
+Send questions, bug reports, or any other feedback to the
+odb-users@codesynthesis.com mailing list.
diff --git a/libodb-mysql/build/.gitignore b/libodb-mysql/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-mysql/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-mysql/build/bootstrap.build b/libodb-mysql/build/bootstrap.build
new file mode 100644
index 0000000..fb04583
--- /dev/null
+++ b/libodb-mysql/build/bootstrap.build
@@ -0,0 +1,10 @@
+# file : build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = libodb-mysql
+
+using version
+using config
+using dist
+using test
+using install
diff --git a/libodb-mysql/build/export.build b/libodb-mysql/build/export.build
new file mode 100644
index 0000000..459f96d
--- /dev/null
+++ b/libodb-mysql/build/export.build
@@ -0,0 +1,9 @@
+# file : build/export.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+$out_root/
+{
+ include odb/mysql/
+}
+
+export $out_root/odb/mysql/lib{odb-mysql}
diff --git a/libodb-mysql/build/root.build b/libodb-mysql/build/root.build
new file mode 100644
index 0000000..c98d520
--- /dev/null
+++ b/libodb-mysql/build/root.build
@@ -0,0 +1,45 @@
+# file : build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+config [bool] config.libodb_mysql.develop ?= false
+
+# Configure which database client library to use for build2 versions greater
+# than 0.12.0 and always use MySQL client library otherwise (due to the lack
+# of the project configuration variables support).
+#
+if ($build.version.number > 12000000000)
+{
+ # Whether to use the MySQL or MariaDB client library.
+ #
+ config [string] config.libodb_mysql.client_lib ?= 'mysql'
+
+ # Verify the config.libodb_mysql.client_lib configuration variable value and
+ # provide the short alias for it.
+ #
+ switch $config.libodb_mysql.client_lib
+ {
+ case 'mysql'
+ case 'mariadb'
+ client_lib = $config.libodb_mysql.client_lib
+
+ default
+ fail "invalid config.libodb_mysql.client_lib value '$config.libodb_mysql.client_lib'"
+ }
+}
+else
+ client_lib = 'mysql'
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+ixx{*}: extension = ixx
+txx{*}: extension = txx
+cxx{*}: extension = cxx
+
+if ($cxx.target.system == 'win32-msvc')
+ cxx.poptions += -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS
+
+if ($cxx.class == 'msvc')
+ cxx.coptions += /wd4251 /wd4275 /wd4800
diff --git a/libodb-mysql/buildfile b/libodb-mysql/buildfile
new file mode 100644
index 0000000..a04e206
--- /dev/null
+++ b/libodb-mysql/buildfile
@@ -0,0 +1,9 @@
+# file : buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/} doc{INSTALL NEWS README} legal{GPLv2 LICENSE} manifest
+
+# Don't install tests or the INSTALL file.
+#
+tests/: install = false
+doc{INSTALL}@./: install = false
diff --git a/libodb-mysql/manifest b/libodb-mysql/manifest
new file mode 100644
index 0000000..238b329
--- /dev/null
+++ b/libodb-mysql/manifest
@@ -0,0 +1,25 @@
+: 1
+name: libodb-mysql
+version: 2.5.0-b.26.z
+project: odb
+summary: MySQL ODB runtime library
+license: GPL-2.0-only
+license: other: proprietary ; Not free/open source.
+topics: C++, ORM, MySQL, MariaDB, SQL
+description-file: README
+changes-file: NEWS
+url: https://www.codesynthesis.com/products/odb/
+doc-url: https://www.codesynthesis.com/products/odb/doc/manual.xhtml
+src-url: https://git.codesynthesis.com/cgit/odb/libodb-mysql/
+email: odb-users@codesynthesis.com
+build-warning-email: odb-builds@codesynthesis.com
+builds: all
+builds: -wasm
+builds: -( +windows &gcc ) ; MinGW GCC is not supported by libmysqlclient.
+requires: c++11
+depends: * build2 >= 0.16.0-
+depends: * bpkg >= 0.16.0-
+#depends: libmysqlclient >= 5.0.3 | libmariadb ^10.2.2
+depends: libmysqlclient >= 5.0.3
+depends: libodb [2.5.0-b.26.1 2.5.0-b.27)
+depends: * cli ^1.2.0- ? ($config.libodb_mysql.develop)
diff --git a/libodb-mysql/odb/mysql/auto-handle.hxx b/libodb-mysql/odb/mysql/auto-handle.hxx
new file mode 100644
index 0000000..27e9f2b
--- /dev/null
+++ b/libodb-mysql/odb/mysql/auto-handle.hxx
@@ -0,0 +1,94 @@
+// file : odb/mysql/auto-handle.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_AUTO_HANDLE_HXX
+#define ODB_MYSQL_AUTO_HANDLE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/mysql.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename H>
+ struct handle_traits;
+
+ template <>
+ struct handle_traits<MYSQL>
+ {
+ static void
+ release (MYSQL* h)
+ {
+ mysql_close (h);
+ }
+ };
+
+ template <>
+ struct handle_traits<MYSQL_STMT>
+ {
+ static void
+ release (MYSQL_STMT* h)
+ {
+ mysql_stmt_close (h);
+ }
+ };
+
+ template <typename H>
+ class auto_handle
+ {
+ public:
+ auto_handle (H* h = 0)
+ : h_ (h)
+ {
+ }
+
+ ~auto_handle ()
+ {
+ if (h_ != 0)
+ handle_traits<H>::release (h_);
+ }
+
+ H*
+ get () const
+ {
+ return h_;
+ }
+
+ void
+ reset (H* h = 0)
+ {
+ if (h_ != 0)
+ handle_traits<H>::release (h_);
+
+ h_ = h;
+ }
+
+ H*
+ release ()
+ {
+ H* h (h_);
+ h_ = 0;
+ return h;
+ }
+
+ operator H* () const
+ {
+ return h_;
+ }
+
+ private:
+ auto_handle (const auto_handle&);
+ auto_handle& operator= (const auto_handle&);
+
+ private:
+ H* h_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_AUTO_HANDLE_HXX
diff --git a/libodb-mysql/odb/mysql/binding.hxx b/libodb-mysql/odb/mysql/binding.hxx
new file mode 100644
index 0000000..628233d
--- /dev/null
+++ b/libodb-mysql/odb/mysql/binding.hxx
@@ -0,0 +1,45 @@
+// file : odb/mysql/binding.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_BINDING_HXX
+#define ODB_MYSQL_BINDING_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/mysql-types.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class LIBODB_MYSQL_EXPORT binding
+ {
+ public:
+ binding (): bind (0), count (0), version (0) {}
+
+ binding (MYSQL_BIND* b, std::size_t n)
+ : bind (b), count (n), version (0)
+ {
+ }
+
+ MYSQL_BIND* bind;
+ std::size_t count;
+ std::size_t version;
+
+ private:
+ binding (const binding&);
+ binding& operator= (const binding&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_BINDING_HXX
diff --git a/libodb-mysql/odb/mysql/buildfile b/libodb-mysql/odb/mysql/buildfile
new file mode 100644
index 0000000..8fc2358
--- /dev/null
+++ b/libodb-mysql/odb/mysql/buildfile
@@ -0,0 +1,165 @@
+# file : odb/mysql/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+define cli: file
+cli{*}: extension = cli
+
+import int_libs = libodb%lib{odb}
+
+import int_libs += ($client_lib == 'mysql' \
+ ? libmysqlclient%lib{mysqlclient} \
+ : libmariadb%lib{mariadb})
+
+lib{odb-mysql}: {hxx ixx txx cxx}{* -version-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_MYSQL_BUILD2
+
+if ($client_lib == 'mariadb')
+ cxx.poptions += -DLIBODB_MYSQL_MARIADB
+
+obja{*}: cxx.poptions += -DLIBODB_MYSQL_STATIC_BUILD
+objs{*}: cxx.poptions += -DLIBODB_MYSQL_SHARED_BUILD
+
+# Export options.
+#
+lib{odb-mysql}:
+{
+ cxx.export.poptions = "-I$out_root" "-I$src_root" -DLIBODB_MYSQL_BUILD2
+ cxx.export.libs = $int_libs
+}
+
+if ($client_lib == 'mariadb')
+ lib{odb-mysql}: cxx.export.poptions += -DLIBODB_MYSQL_MARIADB
+
+liba{odb-mysql}: cxx.export.poptions += -DLIBODB_MYSQL_STATIC
+libs{odb-mysql}: cxx.export.poptions += -DLIBODB_MYSQL_SHARED
+
+# For pre-releases use the complete version to make sure they cannot be used
+# in place of another pre-release or the final version. See the version module
+# for details on the version.* variable values.
+#
+if $version.pre_release
+ lib{odb-mysql}: bin.lib.version = @"-$version.project_id"
+else
+ lib{odb-mysql}: bin.lib.version = @"-$version.major.$version.minor"
+
+develop = $config.libodb_mysql.develop
+
+## Consumption build ($develop == false).
+#
+
+# Use pregenerated versions in the consumption build.
+#
+lib{odb-mysql}: details/pregenerated/{hxx ixx cxx}{**}: include = (!$develop)
+
+if! $develop
+ cxx.poptions =+ "-I($src_base/details/pregenerated)" # Note: must come first.
+
+# Don't install pregenerated headers since they are only used internally in
+# the database implementation (also below).
+#
+details/pregenerated/{hxx ixx}{*}: install = false
+
+# Distribute pregenerated versions only in the consumption build.
+#
+details/pregenerated/{hxx ixx cxx}{*}: dist = (!$develop)
+
+#
+##
+
+
+## Development build ($develop == true).
+#
+
+lib{odb-mysql}: details/{hxx ixx cxx}{options}: include = $develop
+
+if $develop
+ import! [metadata] cli = cli%exe{cli}
+
+# In the development build distribute regenerated {hxx ixx cxx}{options},
+# remapping their locations to the paths of the pregenerated versions (which
+# are only distributed in the consumption build; see above). This way we make
+# sure that the distributed files are always up-to-date.
+#
+<details/{hxx ixx cxx}{options}>: details/cli{options} $cli
+{
+ install = false
+ dist = ($develop ? pregenerated/odb/mysql/details/ : false)
+
+ # Symlink the generated code in src for convenience of development.
+ #
+ backlink = true
+}
+%
+if $develop
+{{
+ options = --include-with-brackets --include-prefix odb/mysql/details \
+ --guard-prefix LIBODB_MYSQL_DETAILS --generate-file-scanner \
+ --cli-namespace odb::mysql::details::cli --long-usage \
+ --generate-specifier --no-combined-flags
+
+ $cli $options -o $out_base/details/ $path($<[0])
+
+ # If the result differs from the pregenerated version, copy it over.
+ #
+ d = [dir_path] $src_base/details/pregenerated/odb/mysql/details/
+
+ if diff $d/options.hxx $path($>[0]) >- && \
+ diff $d/options.ixx $path($>[1]) >- && \
+ diff $d/options.cxx $path($>[2]) >-
+ exit
+ end
+
+ cp $path($>[0]) $d/options.hxx
+ cp $path($>[1]) $d/options.ixx
+ cp $path($>[2]) $d/options.cxx
+}}
+
+# Install into the odb/mysql/ subdirectory of, say, /usr/include/
+# recreating subdirectories.
+#
+install_include = [dir_path] include/odb/mysql/
+
+{hxx ixx txx}{*}:
+{
+ install = $install_include
+ install.subdirs = true
+}
+
+# We want these to be picked up whether LIBODB_MYSQL_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-mysql/odb/mysql/connection-factory.cxx b/libodb-mysql/odb/mysql/connection-factory.cxx
new file mode 100644
index 0000000..d53a4f4
--- /dev/null
+++ b/libodb-mysql/odb/mysql/connection-factory.cxx
@@ -0,0 +1,301 @@
+// file : odb/mysql/connection-factory.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/config.hxx> // ODB_THREADS_*
+#include <odb/mysql/details/config.hxx> // LIBODB_MYSQL_THR_KEY_VISIBLE
+
+#if defined(ODB_THREADS_POSIX) && defined(LIBODB_MYSQL_THR_KEY_VISIBLE)
+# include <pthread.h>
+#endif
+
+#include <cstdlib> // abort
+
+#include <odb/details/tls.hxx>
+#include <odb/details/lock.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/connection-factory.hxx>
+#include <odb/mysql/exceptions.hxx>
+
+// This key is in the mysql client library. We use it to resolve the
+// following problem: Some pthread implementations zero-out slots that
+// don't have destructors during thread termination. As a result, when
+// our destructor gets called and we call mysql_thread_end(), the thread-
+// specific slot used by MySQL may have been reset to 0 and as a result
+// MySQL thinks the data has been freed.
+//
+// To work around this problem we are going to cache the MySQL's slot
+// value and if, during destruction, we see that it is 0, we will restore
+// the original value before calling mysql_thread_end(). This will work
+// fine for as long as the following conditions are met:
+//
+// 1. MySQL doesn't use the destructor itself.
+// 2. Nobody else tried to call mysql_thread_end() before us.
+//
+// Note: in 5.7 the key has been made static and is no longer accessible.
+//
+#if defined(ODB_THREADS_POSIX) && defined(LIBODB_MYSQL_THR_KEY_VISIBLE)
+extern pthread_key_t THR_KEY_mysys;
+#endif
+
+using namespace std;
+
+namespace odb
+{
+ using namespace details;
+
+ namespace mysql
+ {
+ namespace
+ {
+ static bool main_thread_init_;
+
+ struct mysql_thread_init
+ {
+#ifndef ODB_THREADS_NONE
+ mysql_thread_init ()
+ : init_ (false)
+ {
+ if (!main_thread_init_)
+ {
+ if (::mysql_thread_init ())
+ {
+ throw database_exception (
+ CR_UNKNOWN_ERROR, "?????", "thread initialization failed");
+ }
+
+ init_ = true;
+
+#if defined(ODB_THREADS_POSIX) && defined(LIBODB_MYSQL_THR_KEY_VISIBLE)
+ value_ = pthread_getspecific (THR_KEY_mysys);
+#endif
+ }
+ }
+
+ ~mysql_thread_init ()
+ {
+ if (init_)
+ {
+#if defined(ODB_THREADS_POSIX) && defined(LIBODB_MYSQL_THR_KEY_VISIBLE)
+ if (pthread_getspecific (THR_KEY_mysys) == 0)
+ pthread_setspecific (THR_KEY_mysys, value_);
+#endif
+ mysql_thread_end ();
+ }
+ }
+
+ private:
+ bool init_;
+#if defined(ODB_THREADS_POSIX) && defined(LIBODB_MYSQL_THR_KEY_VISIBLE)
+ void* value_;
+#endif
+#endif // ODB_THREADS_NONE
+ };
+
+ static ODB_TLS_OBJECT (mysql_thread_init) mysql_thread_init_;
+
+ struct mysql_process_init
+ {
+ mysql_process_init ()
+ {
+ // Force allocation of our thread-specific key before THR_KEY_mysys
+ // in MySQL. This will (hopefully) get us the desired order of TLS
+ // destructor calls (i.e., our destructor before zeroing-out the
+ // THR_KEY_mysys value). This is pretty much the only way (except
+ // maybe guessing the THR_KEY_mysys value) to get clean thread
+ // termination if THR_KEY_mysys symbol is hidden, as is the case
+ // in the Fedora build of libmysqlclient. See also the comment
+ // at the beginning of this file.
+ //
+ main_thread_init_ = true;
+ tls_get (mysql_thread_init_);
+ main_thread_init_ = false;
+
+ if (mysql_library_init (0 ,0, 0))
+ abort ();
+ }
+
+ ~mysql_process_init ()
+ {
+ mysql_library_end ();
+
+ // Finalize the main thread now in case TLS destruction
+ // doesn't happen for the main thread.
+ //
+ tls_free (mysql_thread_init_);
+ }
+ };
+
+ static mysql_process_init mysql_process_init_;
+ }
+
+ // new_connection_factory
+ //
+ connection_ptr new_connection_factory::
+ connect ()
+ {
+ tls_get (mysql_thread_init_);
+
+ return connection_ptr (new (shared) connection (*this));
+ }
+
+ // connection_pool_factory
+ //
+ connection_pool_factory::pooled_connection_ptr connection_pool_factory::
+ create ()
+ {
+ return pooled_connection_ptr (new (shared) pooled_connection (*this));
+ }
+
+ connection_pool_factory::
+ ~connection_pool_factory ()
+ {
+ // Wait for all the connections currently in use to return to
+ // the pool.
+ //
+ lock l (mutex_);
+ while (in_use_ != 0)
+ {
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ connection_ptr connection_pool_factory::
+ connect ()
+ {
+ tls_get (mysql_thread_init_);
+
+ // The outer loop checks whether the connection we were
+ // given is still valid.
+ //
+ while (true)
+ {
+ pooled_connection_ptr c;
+
+ lock l (mutex_);
+
+ // The inner loop tries to find a free connection.
+ //
+ while (true)
+ {
+ // See if we have a spare connection.
+ //
+ if (connections_.size () != 0)
+ {
+ c = connections_.back ();
+ connections_.pop_back ();
+
+ c->callback_ = &c->cb_;
+ in_use_++;
+ break;
+ }
+
+ // See if we can create a new one.
+ //
+ if(max_ == 0 || in_use_ < max_)
+ {
+ // For new connections we don't need to ping so we
+ // can return immediately.
+ //
+ c = create ();
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // Wait until someone releases a connection.
+ //
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+
+ l.unlock ();
+
+ if (!ping_ || c->ping ())
+ return c;
+ }
+
+ return pooled_connection_ptr (); // Never reached.
+ }
+
+ void connection_pool_factory::
+ database (database_type& db)
+ {
+ tls_get (mysql_thread_init_);
+
+ bool first (db_ == 0);
+
+ connection_factory::database (db);
+
+ if (!first)
+ return;
+
+ if (min_ > 0)
+ {
+ connections_.reserve (min_);
+
+ for(size_t i (0); i < min_; ++i)
+ connections_.push_back (create ());
+ }
+ }
+
+ bool connection_pool_factory::
+ release (pooled_connection* c)
+ {
+ c->clear ();
+ c->callback_ = 0;
+
+ lock l (mutex_);
+
+ // Determine if we need to keep or free this connection.
+ //
+ bool keep (!c->failed () &&
+ (waiters_ != 0 ||
+ min_ == 0 ||
+ (connections_.size () + in_use_ <= min_)));
+
+ in_use_--;
+
+ if (keep)
+ {
+ connections_.push_back (pooled_connection_ptr (inc_ref (c)));
+ connections_.back ()->recycle ();
+ }
+
+ if (waiters_ != 0)
+ cond_.signal ();
+
+ return !keep;
+ }
+
+ //
+ // connection_pool_factory::pooled_connection
+ //
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f)
+ : connection (f)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f, MYSQL* handle)
+ : connection (f, handle)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ bool connection_pool_factory::pooled_connection::
+ zero_counter (void* arg)
+ {
+ pooled_connection* c (static_cast<pooled_connection*> (arg));
+ return static_cast<connection_pool_factory&> (c->factory_).release (c);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/connection-factory.hxx b/libodb-mysql/odb/mysql/connection-factory.hxx
new file mode 100644
index 0000000..12bd4d2
--- /dev/null
+++ b/libodb-mysql/odb/mysql/connection-factory.hxx
@@ -0,0 +1,140 @@
+// file : odb/mysql/connection-factory.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_CONNECTION_FACTORY_HXX
+#define ODB_MYSQL_CONNECTION_FACTORY_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/connection.hxx>
+
+#include <odb/details/mutex.hxx>
+#include <odb/details/condition.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class LIBODB_MYSQL_EXPORT new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory () {}
+
+ virtual connection_ptr
+ connect ();
+
+ private:
+ new_connection_factory (const new_connection_factory&);
+ new_connection_factory& operator= (const new_connection_factory&);
+ };
+
+ class LIBODB_MYSQL_EXPORT connection_pool_factory:
+ public connection_factory
+ {
+ public:
+ // The max_connections argument specifies the maximum number of
+ // concurrent connections this pool will maintain. If this value
+ // is 0 then the pool will create a new connection every time all
+ // of the existing connections are in use.
+ //
+ // The min_connections argument specifies the minimum number of
+ // connections that should be maintained by the pool. If the
+ // number of connections maintained by the pool exceeds this
+ // number and there are no active waiters for a new connection,
+ // then the pool will release the excess connections. If this
+ // value is 0 then the pool will maintain all the connections
+ // that were ever created.
+ //
+ // The ping argument specifies whether to ping the connection to
+ // make sure it is still alive before returning it to the caller.
+ //
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0,
+ bool ping = true)
+ : max_ (max_connections),
+ min_ (min_connections),
+ ping_ (ping),
+ in_use_ (0),
+ waiters_ (0),
+ cond_ (mutex_)
+ {
+ // max_connections == 0 means unlimited.
+ //
+ assert (max_connections == 0 || max_connections >= min_connections);
+ }
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ database (database_type&);
+
+ virtual
+ ~connection_pool_factory ();
+
+ private:
+ connection_pool_factory (const connection_pool_factory&);
+ connection_pool_factory& operator= (const connection_pool_factory&);
+
+ protected:
+ class LIBODB_MYSQL_EXPORT pooled_connection: public connection
+ {
+ public:
+ pooled_connection (connection_pool_factory&);
+ pooled_connection (connection_pool_factory&, MYSQL*);
+
+ private:
+ static bool
+ zero_counter (void*);
+
+ private:
+ friend class connection_pool_factory;
+
+ shared_base::refcount_callback cb_;
+ };
+
+ friend class pooled_connection;
+
+ typedef details::shared_ptr<pooled_connection> pooled_connection_ptr;
+ typedef std::vector<pooled_connection_ptr> connections;
+
+ // This function is called whenever the pool needs to create a new
+ // connection.
+ //
+ virtual pooled_connection_ptr
+ create ();
+
+ protected:
+ // Return true if the connection should be deleted, false otherwise.
+ //
+ bool
+ release (pooled_connection*);
+
+ protected:
+ const std::size_t max_;
+ const std::size_t min_;
+ const bool ping_;
+
+ std::size_t in_use_; // Number of connections currently in use.
+ std::size_t waiters_; // Number of threads waiting for a connection.
+
+ connections connections_;
+
+ details::mutex mutex_;
+ details::condition cond_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_CONNECTION_FACTORY_HXX
diff --git a/libodb-mysql/odb/mysql/connection.cxx b/libodb-mysql/odb/mysql/connection.cxx
new file mode 100644
index 0000000..96baf3e
--- /dev/null
+++ b/libodb-mysql/odb/mysql/connection.cxx
@@ -0,0 +1,218 @@
+// file : odb/mysql/connection.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <new> // std::bad_alloc
+#include <string>
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/transaction.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/error.hxx>
+#include <odb/mysql/exceptions.hxx>
+#include <odb/mysql/statement-cache.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ connection::
+ connection (connection_factory& cf)
+ : odb::connection (cf), failed_ (false), active_ (0)
+ {
+ if (mysql_init (&mysql_) == 0)
+ throw bad_alloc ();
+
+ handle_.reset (&mysql_);
+
+ database_type& db (database ());
+
+ if (*db.charset () != '\0')
+ // Can only fail if we pass an unknown option.
+ //
+ mysql_options (handle_, MYSQL_SET_CHARSET_NAME, db.charset ());
+
+ // Force the CLIENT_FOUND_ROWS flag so that UPDATE returns the
+ // number of found rows, not the number of changed rows. This
+ // is necessary to distinguish between the object-not-persistent
+ // and nothing-changed conditions.
+ //
+ if (mysql_real_connect (handle_,
+ db.host (),
+ db.user (),
+ db.password (),
+ db.db (),
+ db.port (),
+ db.socket (),
+ db.client_flags () | CLIENT_FOUND_ROWS) == 0)
+ {
+ // We cannot use translate_error() here since there is no connection
+ // yet.
+ //
+ unsigned int e (mysql_errno (handle_));
+
+ if (e == CR_OUT_OF_MEMORY)
+ throw bad_alloc ();
+
+ throw database_exception (
+ e, mysql_sqlstate (handle_), mysql_error (handle_));
+ }
+
+ // Do this after we have established the connection.
+ //
+ statement_cache_.reset (new statement_cache_type (*this));
+ }
+
+ connection::
+ connection (connection_factory& cf, MYSQL* handle)
+ : odb::connection (cf),
+ failed_ (false),
+ handle_ (handle),
+ active_ (0),
+ statement_cache_ (new statement_cache_type (*this))
+ {
+ }
+
+ connection::
+ ~connection ()
+ {
+ active_ = 0;
+
+ // Destroy prepared query statements before freeing the connections.
+ //
+ recycle ();
+ clear_prepared_map ();
+
+ if (stmt_handles_.size () > 0)
+ free_stmt_handles ();
+ }
+
+ transaction_impl* connection::
+ begin ()
+ {
+ return new transaction_impl (connection_ptr (inc_ref (this)));
+ }
+
+ unsigned long long connection::
+ execute (const char* s, size_t n)
+ {
+ clear ();
+
+ {
+ odb::tracer* t;
+ if ((t = transaction_tracer ()) ||
+ (t = tracer ()) ||
+ (t = database ().tracer ()))
+ {
+ string str (s, n);
+ t->execute (*this, str.c_str ());
+ }
+ }
+
+ if (mysql_real_query (handle_, s, static_cast<unsigned long> (n)))
+ translate_error (*this);
+
+ // Get the affected row count, if any. If the statement has a result
+ // set (e.g., SELECT), we first need to call mysql_store_result().
+ //
+ unsigned long long r (0);
+
+ if (mysql_field_count (handle_) == 0)
+ r = static_cast<unsigned long long> (mysql_affected_rows (handle_));
+ else
+ {
+ if (MYSQL_RES* rs = mysql_store_result (handle_))
+ {
+ r = static_cast<unsigned long long> (mysql_num_rows (rs));
+ mysql_free_result (rs);
+ }
+ else
+ translate_error (*this);
+ }
+
+ return r;
+ }
+
+ bool connection::
+ ping ()
+ {
+ if (failed ())
+ return false;
+
+ if (!mysql_ping (handle_))
+ return true;
+
+ switch (mysql_errno (handle_))
+ {
+ case CR_SERVER_LOST:
+ case CR_SERVER_GONE_ERROR:
+ {
+ mark_failed ();
+ return false;
+ }
+ default:
+ {
+ translate_error (*this);
+ return false; // Never reached.
+ }
+ }
+ }
+
+ void connection::
+ clear_ ()
+ {
+ active_->cancel (); // Should clear itself from active_.
+ }
+
+ MYSQL_STMT* connection::
+ alloc_stmt_handle ()
+ {
+ MYSQL_STMT* stmt (mysql_stmt_init (handle_));
+
+ if (stmt == 0)
+ throw bad_alloc ();
+
+ return stmt;
+ }
+
+ void connection::
+ free_stmt_handle (auto_handle<MYSQL_STMT>& stmt)
+ {
+ if (active_ == 0)
+ stmt.reset ();
+ else
+ {
+ stmt_handles_.push_back (stmt); // May throw.
+ stmt.release ();
+ }
+ }
+
+ void connection::
+ free_stmt_handles ()
+ {
+ for (stmt_handles::iterator i (stmt_handles_.begin ()),
+ e (stmt_handles_.end ()); i != e; ++i)
+ {
+ mysql_stmt_close (*i);
+ }
+
+ stmt_handles_.clear ();
+ }
+
+ // connection_factory
+ //
+ connection_factory::
+ ~connection_factory ()
+ {
+ }
+
+ void connection_factory::
+ database (database_type& db)
+ {
+ odb::connection_factory::db_ = &db;
+ db_ = &db;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/connection.hxx b/libodb-mysql/odb/mysql/connection.hxx
new file mode 100644
index 0000000..31b24ed
--- /dev/null
+++ b/libodb-mysql/odb/mysql/connection.hxx
@@ -0,0 +1,231 @@
+// file : odb/mysql/connection.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_CONNECTION_HXX
+#define ODB_MYSQL_CONNECTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+
+#include <odb/connection.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/query.hxx>
+#include <odb/mysql/tracer.hxx>
+#include <odb/mysql/transaction-impl.hxx>
+#include <odb/mysql/auto-handle.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/unique-ptr.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class statement_cache;
+ class connection_factory;
+
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+
+ class LIBODB_MYSQL_EXPORT connection: public odb::connection
+ {
+ public:
+ typedef mysql::statement_cache statement_cache_type;
+ typedef mysql::database database_type;
+
+ virtual
+ ~connection ();
+
+ connection (connection_factory&);
+ connection (connection_factory&, MYSQL* handle);
+
+ database_type&
+ database ();
+
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ public:
+ using odb::connection::execute;
+
+ virtual unsigned long long
+ execute (const char* statement, std::size_t length);
+
+ // Query preparation.
+ //
+ public:
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const mysql::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef mysql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ using odb::connection::tracer;
+
+ public:
+ bool
+ failed () const
+ {
+ return failed_;
+ }
+
+ void
+ mark_failed ()
+ {
+ failed_ = true;
+ }
+
+ // Ping the server to make sure the connection is still alive. Return
+ // true if successful, mark the connection as failed and return false
+ // otherwise. This function can also throw database_exception.
+ //
+ bool
+ ping ();
+
+ public:
+ MYSQL*
+ handle ()
+ {
+ return handle_;
+ }
+
+ statement_cache_type&
+ statement_cache ()
+ {
+ return *statement_cache_;
+ }
+
+ public:
+ statement*
+ active ()
+ {
+ return active_;
+ }
+
+ void
+ active (statement* s)
+ {
+ active_ = s;
+
+ if (s == 0 && stmt_handles_.size () > 0)
+ free_stmt_handles ();
+ }
+
+ // Cancel and clear the active statement, if any.
+ //
+ void
+ clear ()
+ {
+ if (active_ != 0)
+ clear_ ();
+ }
+
+ public:
+ MYSQL_STMT*
+ alloc_stmt_handle ();
+
+ void
+ free_stmt_handle (auto_handle<MYSQL_STMT>&);
+
+ private:
+ connection (const connection&);
+ connection& operator= (const connection&);
+
+ private:
+ void
+ free_stmt_handles ();
+
+ void
+ clear_ ();
+
+ private:
+ friend class transaction_impl; // invalidate_results()
+
+ private:
+ bool failed_;
+
+ MYSQL mysql_;
+ auto_handle<MYSQL> handle_;
+
+ statement* active_;
+
+ // Keep statement_cache_ after handle_ so that it is destroyed before
+ // the connection is closed.
+ //
+ details::unique_ptr<statement_cache_type> statement_cache_;
+
+ // List of "delayed" statement handles to be freed next time there
+ // is no active statement.
+ //
+ typedef std::vector<MYSQL_STMT*> stmt_handles;
+ stmt_handles stmt_handles_;
+ };
+
+ class LIBODB_MYSQL_EXPORT connection_factory:
+ public odb::connection_factory
+ {
+ public:
+ typedef mysql::database database_type;
+
+ virtual void
+ database (database_type&);
+
+ database_type&
+ database () {return *db_;}
+
+ virtual connection_ptr
+ connect () = 0;
+
+ virtual
+ ~connection_factory ();
+
+ connection_factory (): db_ (0) {}
+
+ // Needed to break the circular connection_factory-database dependency
+ // (odb::connection_factory has the odb::database member).
+ //
+ protected:
+ database_type* db_;
+ };
+ }
+}
+
+#include <odb/mysql/connection.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_CONNECTION_HXX
diff --git a/libodb-mysql/odb/mysql/connection.ixx b/libodb-mysql/odb/mysql/connection.ixx
new file mode 100644
index 0000000..d2f09ac
--- /dev/null
+++ b/libodb-mysql/odb/mysql/connection.ixx
@@ -0,0 +1,44 @@
+// file : odb/mysql/connection.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mysql
+ {
+ inline database& connection::
+ database ()
+ {
+ return static_cast<connection_factory&> (factory_).database ();
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const mysql::query_base& q)
+ {
+ return query_<T, id_mysql>::call (*this, n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, mysql::query_base (q));
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/container-statements.hxx b/libodb-mysql/odb/mysql/container-statements.hxx
new file mode 100644
index 0000000..48e769d
--- /dev/null
+++ b/libodb-mysql/odb/mysql/container-statements.hxx
@@ -0,0 +1,360 @@
+// file : odb/mysql/container-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_CONTAINER_STATEMENTS_HXX
+#define ODB_MYSQL_CONTAINER_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/statement.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class connection;
+
+ // Template argument is the generated abstract container traits type.
+ // That is, it doesn't need to provide column counts and statements.
+ //
+ template <typename T>
+ class container_statements
+ {
+ public:
+ typedef T traits;
+
+ typedef typename traits::data_image_type data_image_type;
+ typedef typename traits::functions_type functions_type;
+
+ typedef mysql::insert_statement insert_statement_type;
+ typedef mysql::select_statement select_statement_type;
+ typedef mysql::delete_statement delete_statement_type;
+
+ typedef mysql::connection connection_type;
+
+ container_statements (connection_type&, binding& id_binding);
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Functions.
+ //
+ functions_type&
+ functions ()
+ {
+ return functions_;
+ }
+
+ // Schema version.
+ //
+ const schema_version_migration&
+ version_migration () const {return *svm_;}
+
+ void
+ version_migration (const schema_version_migration& svm) {svm_ = &svm;}
+
+ // Id image binding (external).
+ //
+ const binding&
+ id_binding ()
+ {
+ return id_binding_;
+ }
+
+ // Data image. The image is split into the id (that comes as a
+ // binding) and index/key plus value which are in data_image_type.
+ // The select binding is a subset of the full binding (no id).
+ //
+ data_image_type&
+ data_image ()
+ {
+ return data_image_;
+ }
+
+ MYSQL_BIND*
+ data_bind ()
+ {
+ return insert_image_binding_.bind;
+ }
+
+ bool
+ data_binding_test_version () const
+ {
+ return data_id_binding_version_ != id_binding_.version ||
+ data_image_version_ != data_image_.version ||
+ insert_image_binding_.version == 0;
+ }
+
+ void
+ data_binding_update_version ()
+ {
+ data_id_binding_version_ = id_binding_.version;
+ data_image_version_ = data_image_.version;
+ insert_image_binding_.version++;
+ select_image_binding_.version++;
+ }
+
+ my_bool*
+ select_image_truncated ()
+ {
+ return select_image_truncated_;
+ }
+
+ //
+ // Statements.
+ //
+
+ insert_statement_type&
+ insert_statement ()
+ {
+ if (insert_ == 0)
+ insert_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ insert_text_,
+ versioned_, // Process if versioned.
+ insert_image_binding_,
+ 0,
+ false));
+
+ return *insert_;
+ }
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ select_text_,
+ versioned_, // Process if versioned.
+ false, // Don't optimize.
+ id_binding_,
+ select_image_binding_,
+ false));
+
+ return *select_;
+ }
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (delete_ == 0)
+ delete_.reset (
+ new (details::shared) delete_statement_type (
+ conn_, delete_text_, id_binding_, false));
+
+ return *delete_;
+ }
+
+ private:
+ container_statements (const container_statements&);
+ container_statements& operator= (const container_statements&);
+
+ protected:
+ connection_type& conn_;
+ binding& id_binding_;
+
+ functions_type functions_;
+
+ data_image_type data_image_;
+ std::size_t data_image_version_;
+ std::size_t data_id_binding_version_;
+
+ binding insert_image_binding_;
+
+ binding select_image_binding_;
+ my_bool* select_image_truncated_;
+
+ const char* insert_text_;
+ const char* select_text_;
+ const char* delete_text_;
+
+ bool versioned_;
+ const schema_version_migration* svm_;
+
+ details::shared_ptr<insert_statement_type> insert_;
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<delete_statement_type> delete_;
+ };
+
+ template <typename T>
+ class smart_container_statements: public container_statements<T>
+ {
+ public:
+ typedef T traits;
+ typedef typename traits::cond_image_type cond_image_type;
+
+ typedef mysql::update_statement update_statement_type;
+ typedef mysql::delete_statement delete_statement_type;
+
+ typedef mysql::connection connection_type;
+
+ smart_container_statements (connection_type&, binding& id_binding);
+
+ // Condition image. The image is split into the id (that comes as
+ // a binding) and index/key/value which is in cond_image_type.
+ //
+ cond_image_type&
+ cond_image ()
+ {
+ return cond_image_;
+ }
+
+ MYSQL_BIND*
+ cond_bind ()
+ {
+ return cond_image_binding_.bind;
+ }
+
+ bool
+ cond_binding_test_version () const
+ {
+ return cond_id_binding_version_ != this->id_binding_.version ||
+ cond_image_version_ != cond_image_.version ||
+ cond_image_binding_.version == 0;
+ }
+
+ void
+ cond_binding_update_version ()
+ {
+ cond_id_binding_version_ = this->id_binding_.version;
+ cond_image_version_ = cond_image_.version;
+ cond_image_binding_.version++;
+ }
+
+ // Update image. The image is split as follows: value comes
+ // from the data image, id comes as binding, and index/key
+ // comes from the condition image.
+ //
+ MYSQL_BIND*
+ update_bind ()
+ {
+ return update_image_binding_.bind;
+ }
+
+ bool
+ update_binding_test_version () const
+ {
+ return update_id_binding_version_ != this->id_binding_.version ||
+ update_cond_image_version_ != cond_image_.version ||
+ update_data_image_version_ != this->data_image_.version ||
+ update_image_binding_.version == 0;
+ }
+
+ void
+ update_binding_update_version ()
+ {
+ update_id_binding_version_ = this->id_binding_.version;
+ update_cond_image_version_ = cond_image_.version;
+ update_data_image_version_ = this->data_image_.version;
+ update_image_binding_.version++;
+ }
+
+ //
+ // Statements.
+ //
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (this->delete_ == 0)
+ this->delete_.reset (
+ new (details::shared) delete_statement_type (
+ this->conn_,
+ this->delete_text_,
+ this->cond_image_binding_,
+ false));
+
+ return *this->delete_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ this->conn_,
+ update_text_,
+ this->versioned_, // Process if versioned.
+ update_image_binding_,
+ false));
+
+ return *update_;
+ }
+
+ protected:
+ cond_image_type cond_image_;
+ std::size_t cond_image_version_;
+ std::size_t cond_id_binding_version_;
+ binding cond_image_binding_;
+
+ std::size_t update_id_binding_version_;
+ std::size_t update_cond_image_version_;
+ std::size_t update_data_image_version_;
+ binding update_image_binding_;
+
+ const char* update_text_;
+
+ details::shared_ptr<update_statement_type> update_;
+ };
+
+ // Template argument is the generated concrete container traits type.
+ //
+ template <typename T>
+ class container_statements_impl: public T::statements_type
+ {
+ public:
+ typedef T traits;
+ typedef typename T::statements_type base;
+ typedef mysql::connection connection_type;
+
+ container_statements_impl (connection_type&, binding&);
+
+ private:
+ container_statements_impl (const container_statements_impl&);
+ container_statements_impl& operator= (const container_statements_impl&);
+
+ private:
+ MYSQL_BIND data_image_bind_[traits::data_column_count];
+ my_bool select_image_truncated_array_[traits::data_column_count -
+ traits::id_column_count];
+ };
+
+ template <typename T>
+ class smart_container_statements_impl: public container_statements_impl<T>
+ {
+ public:
+ typedef T traits;
+ typedef mysql::connection connection_type;
+
+ smart_container_statements_impl (connection_type&, binding&);
+
+ private:
+ MYSQL_BIND cond_image_bind_[traits::cond_column_count];
+ MYSQL_BIND update_image_bind_[traits::value_column_count +
+ traits::cond_column_count];
+ };
+ }
+}
+
+#include <odb/mysql/container-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_CONTAINER_STATEMENTS_HXX
diff --git a/libodb-mysql/odb/mysql/container-statements.txx b/libodb-mysql/odb/mysql/container-statements.txx
new file mode 100644
index 0000000..55bc5fe
--- /dev/null
+++ b/libodb-mysql/odb/mysql/container-statements.txx
@@ -0,0 +1,107 @@
+// file : odb/mysql/container-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mysql
+ {
+ // container_statements
+ //
+ template <typename T>
+ container_statements<T>::
+ container_statements (connection_type& conn, binding& id)
+ : conn_ (conn),
+ id_binding_ (id),
+ functions_ (this),
+ insert_image_binding_ (0, 0), // Initialized by impl.
+ select_image_binding_ (0, 0), // Initialized by impl.
+ svm_ (0)
+ {
+ functions_.insert_ = &traits::insert;
+ functions_.select_ = &traits::select;
+ functions_.delete__ = &traits::delete_;
+
+ data_image_.version = 0;
+ data_image_version_ = 0;
+ data_id_binding_version_ = 0;
+ }
+
+ // smart_container_statements
+ //
+ template <typename T>
+ smart_container_statements<T>::
+ smart_container_statements (connection_type& conn, binding& id)
+ : container_statements<T> (conn, id),
+ cond_image_binding_ (0, 0), // Initialized by impl.
+ update_image_binding_ (0, 0) // Initialized by impl.
+ {
+ this->functions_.update_ = &traits::update;
+
+ cond_image_.version = 0;
+ cond_image_version_ = 0;
+ cond_id_binding_version_ = 0;
+
+ update_id_binding_version_ = 0;
+ update_cond_image_version_ = 0;
+ update_data_image_version_ = 0;
+ }
+
+ // container_statements_impl
+ //
+ template <typename T>
+ container_statements_impl<T>::
+ container_statements_impl (connection_type& conn, binding& id)
+ : base (conn, id)
+ {
+ this->select_image_truncated_ = select_image_truncated_array_;
+
+ this->insert_image_binding_.bind = data_image_bind_;
+ this->insert_image_binding_.count = traits::data_column_count;
+
+ this->select_image_binding_.bind = data_image_bind_ +
+ traits::id_column_count;
+ this->select_image_binding_.count = traits::data_column_count -
+ traits::id_column_count;
+
+ std::memset (data_image_bind_, 0, sizeof (data_image_bind_));
+ std::memset (select_image_truncated_array_,
+ 0,
+ sizeof (select_image_truncated_array_));
+
+ for (std::size_t i (0);
+ i < traits::data_column_count - traits::id_column_count;
+ ++i)
+ data_image_bind_[i + traits::id_column_count].error =
+ select_image_truncated_array_ + i;
+
+ this->insert_text_ = traits::insert_statement;
+ this->select_text_ = traits::select_statement;
+ this->delete_text_ = traits::delete_statement;
+
+ this->versioned_ = traits::versioned;
+ }
+
+ // smart_container_statements_impl
+ //
+ template <typename T>
+ smart_container_statements_impl<T>::
+ smart_container_statements_impl (connection_type& conn, binding& id)
+ : container_statements_impl<T> (conn, id)
+ {
+ this->cond_image_binding_.bind = cond_image_bind_;
+ this->cond_image_binding_.count = traits::cond_column_count;
+
+ this->update_image_binding_.bind = update_image_bind_;
+ this->update_image_binding_.count = traits::value_column_count +
+ traits::cond_column_count;
+
+ std::memset (cond_image_bind_, 0, sizeof (cond_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+ this->update_text_ = traits::update_statement;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/database.cxx b/libodb-mysql/odb/mysql/database.cxx
new file mode 100644
index 0000000..6c0f1d6
--- /dev/null
+++ b/libodb-mysql/odb/mysql/database.cxx
@@ -0,0 +1,357 @@
+// file : odb/mysql/database.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <sstream>
+#include <cstring> // std::memset
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/connection-factory.hxx>
+#include <odb/mysql/transaction.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/exceptions.hxx>
+
+#include <odb/mysql/details/options.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ using odb::details::transfer_ptr;
+
+ database::
+ ~database ()
+ {
+ }
+
+ database::
+ database (const char* user,
+ const char* passwd,
+ const char* db,
+ const char* host,
+ unsigned int port,
+ const char* socket,
+ const char* charset,
+ unsigned long client_flags,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mysql),
+ user_ (user ? user : ""),
+ passwd_str_ (passwd ? passwd : ""),
+ passwd_ (passwd ? passwd_str_.c_str () : 0),
+ db_ (db ? db : ""),
+ host_ (host ? host : ""),
+ port_ (port),
+ socket_str_ (socket ? socket : ""),
+ socket_ (socket ? socket_str_.c_str () : 0),
+ charset_ (charset == 0 ? "" : charset),
+ client_flags_ (client_flags),
+ factory_ (factory.transfer ())
+ {
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const string& user,
+ const string& passwd,
+ const string& db,
+ const string& host,
+ unsigned int port,
+ const string* socket,
+ const string& charset,
+ unsigned long client_flags,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mysql),
+ user_ (user),
+ passwd_str_ (passwd),
+ passwd_ (passwd_str_.c_str ()),
+ db_ (db),
+ host_ (host),
+ port_ (port),
+ socket_str_ (socket ? *socket : ""),
+ socket_ (socket ? socket_str_.c_str () : 0),
+ charset_ (charset),
+ client_flags_ (client_flags),
+ factory_ (factory.transfer ())
+ {
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const string& user,
+ const string* passwd,
+ const string& db,
+ const string& host,
+ unsigned int port,
+ const string* socket,
+ const string& charset,
+ unsigned long client_flags,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mysql),
+ user_ (user),
+ passwd_str_ (passwd ? *passwd : ""),
+ passwd_ (passwd ? passwd_str_.c_str () : 0),
+ db_ (db),
+ host_ (host),
+ port_ (port),
+ socket_str_ (socket ? *socket : ""),
+ socket_ (socket ? socket_str_.c_str () : 0),
+ charset_ (charset),
+ client_flags_ (client_flags),
+ factory_ (factory.transfer ())
+ {
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const string& user,
+ const string& passwd,
+ const string& db,
+ const string& host,
+ unsigned int port,
+ const string& socket,
+ const string& charset,
+ unsigned long client_flags,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mysql),
+ user_ (user),
+ passwd_str_ (passwd),
+ passwd_ (passwd_str_.c_str ()),
+ db_ (db),
+ host_ (host),
+ port_ (port),
+ socket_str_ (socket),
+ socket_ (socket_str_.c_str ()),
+ charset_ (charset),
+ client_flags_ (client_flags),
+ factory_ (factory.transfer ())
+ {
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const string& user,
+ const string* passwd,
+ const string& db,
+ const string& host,
+ unsigned int port,
+ const string& socket,
+ const string& charset,
+ unsigned long client_flags,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mysql),
+ user_ (user),
+ passwd_str_ (passwd ? *passwd : ""),
+ passwd_ (passwd ? passwd_str_.c_str () : 0),
+ db_ (db),
+ host_ (host),
+ port_ (port),
+ socket_str_ (socket),
+ socket_ (socket_str_.c_str ()),
+ charset_ (charset),
+ client_flags_ (client_flags),
+ factory_ (factory.transfer ())
+ {
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (int& argc,
+ char* argv[],
+ bool erase,
+ const string& charset,
+ unsigned long client_flags,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_mysql),
+ passwd_ (0),
+ socket_ (0),
+ charset_ (charset),
+ client_flags_ (client_flags),
+ factory_ (factory.transfer ())
+ {
+ using namespace details;
+
+ try
+ {
+ cli::argv_file_scanner scan (argc, argv, "--options-file", erase);
+ options ops (scan, cli::unknown_mode::skip, cli::unknown_mode::skip);
+
+ user_ = ops.user ();
+
+ if (ops.password_specified ())
+ {
+ passwd_str_ = ops.password ();
+ passwd_ = passwd_str_.c_str ();
+ }
+
+ db_ = ops.database ();
+ host_ = ops.host ();
+ port_ = ops.port ();
+
+ if (ops.socket_specified ())
+ {
+ socket_str_ = ops.socket ();
+ socket_ = socket_str_.c_str ();
+ }
+ }
+ catch (const cli::exception& e)
+ {
+ ostringstream ostr;
+ ostr << e;
+ throw cli_exception (ostr.str ());
+ }
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ void database::
+ print_usage (ostream& os)
+ {
+ details::options::print_usage (os);
+ }
+
+ transaction_impl* database::
+ begin ()
+ {
+ return new transaction_impl (*this);
+ }
+
+ odb::connection* database::
+ connection_ ()
+ {
+ connection_ptr c (factory_->connect ());
+ return c.release ();
+ }
+
+ const database::schema_version_info& database::
+ load_schema_version (const string& name) const
+ {
+ schema_version_info& svi (schema_version_map_[name]);
+
+ // Construct the SELECT statement text.
+ //
+ string text ("SELECT `version`, `migration` FROM ");
+
+ if (!svi.version_table.empty ())
+ text += svi.version_table; // Already quoted.
+ else if (!schema_version_table_.empty ())
+ text += schema_version_table_; // Already quoted.
+ else
+ text += "`schema_version`";
+
+ text += " WHERE `name` = ?";
+
+ // Bind parameters and results.
+ //
+ unsigned long psize[1] = {static_cast<unsigned long> (name.size ())};
+ my_bool pnull[1] = {0};
+ MYSQL_BIND pbind[1];
+ binding param (pbind, 1);
+
+ memset (pbind, 0, sizeof (pbind));
+
+ pbind[0].buffer_type = MYSQL_TYPE_STRING;
+ pbind[0].buffer = const_cast<char*> (name.c_str ());
+ pbind[0].buffer_length = psize[0];
+ pbind[0].length = &psize[0];
+ pbind[0].is_null = &pnull[0];
+
+ param.version++;
+
+ signed char migration;
+ my_bool rnull[2];
+ MYSQL_BIND rbind[2];
+ binding result (rbind, 2);
+
+ memset (rbind, 0, sizeof (rbind));
+
+ rbind[0].buffer_type = MYSQL_TYPE_LONGLONG;
+ rbind[0].is_unsigned = 1;
+ rbind[0].buffer = &svi.version;
+ rbind[0].is_null = &rnull[0];
+
+ rbind[1].buffer_type = MYSQL_TYPE_TINY;
+ rbind[1].is_unsigned = 0;
+ rbind[1].buffer = &migration;
+ rbind[1].is_null = &rnull[1];
+
+ result.version++;
+
+ // If we are not in transaction, MySQL will use an implicit one
+ // (i.e., autocommit mode), which suits us just fine.
+ //
+ connection_ptr cp;
+ if (!transaction::has_current ())
+ cp = factory_->connect ();
+
+ mysql::connection& c (
+ cp != 0
+ ? *cp
+ : transaction::current ().connection (const_cast<database&> (*this)));
+
+ try
+ {
+ select_statement st (c,
+ text.c_str (),
+ false, // Don't process.
+ false, // Don't optimize.
+ param,
+ result,
+ false);
+ st.execute ();
+ auto_result ar (st);
+
+ switch (st.fetch ())
+ {
+ case select_statement::success:
+ {
+ svi.migration = migration != 0;
+ assert (st.fetch () == select_statement::no_data);
+ break;
+ }
+ case select_statement::no_data:
+ {
+ svi.version = 0; // No schema.
+ break;
+ }
+ case select_statement::truncated:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+ catch (const database_exception& e)
+ {
+ // Detect the case where there is no version table.
+ //
+ if (e.error () == ER_NO_SUCH_TABLE)
+ svi.version = 0; // No schema.
+ else
+ throw;
+ }
+
+ return svi;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/database.hxx b/libodb-mysql/odb/mysql/database.hxx
new file mode 100644
index 0000000..e73b7c9
--- /dev/null
+++ b/libodb-mysql/odb/mysql/database.hxx
@@ -0,0 +1,540 @@
+// file : odb/mysql/database.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_DATABASE_HXX
+#define ODB_MYSQL_DATABASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <iosfwd> // std::ostream
+
+#include <odb/database.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/transfer-ptr.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/query.hxx>
+#include <odb/mysql/tracer.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/connection-factory.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class transaction_impl;
+
+ class LIBODB_MYSQL_EXPORT database: public odb::database
+ {
+ public:
+ // In MySQL, NULL and empty string are treated as the same value
+ // for all the arguments except passwd and socket.
+ //
+ database (const char* user,
+ const char* passwd,
+ const char* db,
+ const char* host = 0,
+ unsigned int port = 0,
+ const char* socket = 0,
+ const char* charset = 0,
+ unsigned long client_flags = 0,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ database (const std::string& user,
+ const std::string& passwd,
+ const std::string& db,
+ const std::string& host = "",
+ unsigned int port = 0,
+ const std::string* socket = 0,
+ const std::string& charset = "",
+ unsigned long client_flags = 0,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ database (const std::string& user,
+ const std::string* passwd,
+ const std::string& db,
+ const std::string& host = "",
+ unsigned int port = 0,
+ const std::string* socket = 0,
+ const std::string& charset = "",
+ unsigned long client_flags = 0,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ database (const std::string& user,
+ const std::string& passwd,
+ const std::string& db,
+ const std::string& host,
+ unsigned int port,
+ const std::string& socket,
+ const std::string& charset = "",
+ unsigned long client_flags = 0,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ database (const std::string& user,
+ const std::string* passwd,
+ const std::string& db,
+ const std::string& host,
+ unsigned int port,
+ const std::string& socket,
+ const std::string& charset = "",
+ unsigned long client_flags = 0,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Extract the database parameters from the command line. The
+ // following options are recognized:
+ //
+ // --user
+ // --password
+ // --database
+ // --host
+ // --port
+ // --socket
+ // --options-file
+ //
+ // For more information, see the output of the print_usage() function
+ // below. If erase is true, the above options are removed from the
+ // argv array and the argc count is updated accordingly. This
+ // constructor may throw the cli_exception exception.
+ //
+ database (int& argc,
+ char* argv[],
+ bool erase = false,
+ const std::string& charset = "",
+ unsigned long client_flags = 0,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Move-constructible but not move-assignable.
+ //
+ // Note: noexcept is not specified since odb::database(odb::database&&)
+ // can throw.
+ //
+#ifdef ODB_CXX11
+ database (database&&);
+#endif
+
+ static void
+ print_usage (std::ostream&);
+
+ public:
+ const char*
+ user () const
+ {
+ return user_.c_str ();
+ }
+
+ const char*
+ password () const
+ {
+ return passwd_;
+ }
+
+ const char*
+ db () const
+ {
+ return db_.c_str ();
+ }
+
+ const char*
+ host () const
+ {
+ return host_.c_str ();
+ }
+
+ unsigned int
+ port () const
+ {
+ return port_;
+ }
+
+ const char*
+ socket () const
+ {
+ return socket_;
+ }
+
+ const char*
+ charset () const
+ {
+ return charset_.c_str ();
+ }
+
+ unsigned long
+ client_flags () const
+ {
+ return client_flags_;
+ }
+
+ // Object persistence API.
+ //
+ public:
+
+ // Make the object persistent.
+ //
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Load an object. Throw object_not_persistent if not found.
+ //
+ template <typename T>
+ typename object_traits<T>::pointer_type
+ load (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ void
+ load (const typename object_traits<T>::id_type& id, T& object);
+
+ // Load (or reload, if it is already loaded) a section of an object.
+ //
+ template <typename T>
+ void
+ load (T& object, section&);
+
+ // Reload an object.
+ //
+ template <typename T>
+ void
+ reload (T& object);
+
+ template <typename T>
+ void
+ reload (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ reload (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Loan an object if found. Return NULL/false if not found.
+ //
+ template <typename T>
+ typename object_traits<T>::pointer_type
+ find (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ bool
+ find (const typename object_traits<T>::id_type& id, T& object);
+
+ // Update the state of a modified objects.
+ //
+ template <typename T>
+ void
+ update (T& object);
+
+ template <typename T>
+ void
+ update (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ update (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Update a section of an object. Throws the section_not_loaded
+ // exception if the section is not loaded. Note also that this
+ // function does not clear the changed flag if it is set.
+ //
+ template <typename T>
+ void
+ update (const T& object, const section&);
+
+ // Make the object transient. Throw object_not_persistent if not
+ // found.
+ //
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ void
+ erase (T& object);
+
+ template <typename T>
+ void
+ erase (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Erase multiple objects matching a query predicate.
+ //
+ template <typename T>
+ unsigned long long
+ erase_query ();
+
+ template <typename T>
+ unsigned long long
+ erase_query (const char*);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const std::string&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const mysql::query_base&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const odb::query_base&);
+
+ // Query API.
+ //
+ template <typename T>
+ result<T>
+ query (bool cache = true);
+
+ template <typename T>
+ result<T>
+ query (const char*, bool cache = true);
+
+ template <typename T>
+ result<T>
+ query (const std::string&, bool cache = true);
+
+ template <typename T>
+ result<T>
+ query (const mysql::query_base&, bool cache = true);
+
+ template <typename T>
+ result<T>
+ query (const odb::query_base&, bool cache = true);
+
+ // Query one API.
+ //
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one ();
+
+ template <typename T>
+ bool
+ query_one (T& object);
+
+ template <typename T>
+ T
+ query_value ();
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const char*);
+
+ template <typename T>
+ bool
+ query_one (const char*, T& object);
+
+ template <typename T>
+ T
+ query_value (const char*);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const std::string&);
+
+ template <typename T>
+ bool
+ query_one (const std::string&, T& object);
+
+ template <typename T>
+ T
+ query_value (const std::string&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const mysql::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const mysql::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const mysql::query_base&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const odb::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const odb::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const odb::query_base&);
+
+ // Query preparation.
+ //
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const mysql::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // Transactions.
+ //
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ public:
+ connection_ptr
+ connection ();
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef mysql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::database::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::database::tracer (t);
+ }
+
+ using odb::database::tracer;
+
+ // Database schema version.
+ //
+ protected:
+ virtual const schema_version_info&
+ load_schema_version (const std::string& schema_name) const;
+
+ public:
+ // Database id constant (useful for meta-programming).
+ //
+ static const odb::database_id database_id = id_mysql;
+
+ public:
+ virtual
+ ~database ();
+
+ protected:
+ virtual odb::connection*
+ connection_ ();
+
+ private:
+ // Note: remember to update move ctor if adding any new members.
+ //
+ std::string user_;
+ std::string passwd_str_;
+ const char* passwd_;
+ std::string db_;
+ std::string host_;
+ unsigned int port_;
+ std::string socket_str_;
+ const char* socket_;
+ std::string charset_;
+ unsigned long client_flags_;
+ details::unique_ptr<connection_factory> factory_;
+ };
+ }
+}
+
+#include <odb/mysql/database.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_DATABASE_HXX
diff --git a/libodb-mysql/odb/mysql/database.ixx b/libodb-mysql/odb/mysql/database.ixx
new file mode 100644
index 0000000..f9b62dd
--- /dev/null
+++ b/libodb-mysql/odb/mysql/database.ixx
@@ -0,0 +1,617 @@
+// file : odb/mysql/database.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <utility> // move()
+
+#include <odb/mysql/transaction.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+#ifdef ODB_CXX11
+ inline database::
+ database (database&& db) // Has to be inline.
+ : odb::database (std::move (db)),
+ user_ (std::move (db.user_)),
+ passwd_str_ (std::move (db.passwd_str_)),
+ passwd_ (db.passwd_ != 0 ? passwd_str_.c_str () : 0),
+ db_ (std::move (db.db_)),
+ host_ (std::move (db.host_)),
+ port_ (db.port_),
+ socket_str_ (std::move (db.socket_str_)),
+ socket_ (db.socket_ != 0 ? socket_str_.c_str () : 0),
+ charset_ (std::move (db.charset_)),
+ client_flags_ (db.client_flags_),
+ factory_ (std::move (db.factory_))
+ {
+ factory_->database (*this); // New database instance.
+ }
+#endif
+
+ inline connection_ptr database::
+ connection ()
+ {
+ // Go through the virtual connection_() function instead of
+ // directly to allow overriding.
+ //
+ return connection_ptr (
+ static_cast<mysql::connection*> (connection_ ()));
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T& obj)
+ {
+ return persist_<T, id_mysql> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const T& obj)
+ {
+ return persist_<const T, id_mysql> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T>& p)
+ {
+ const P<T>& cr (p);
+ return persist<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ return persist<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const typename object_traits<T>::pointer_type& pobj)
+ {
+ return persist_<T, id_mysql> (pobj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ load (const typename object_traits<T>::id_type& id)
+ {
+ return load_<T, id_mysql> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ load (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return load_<T, id_mysql> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ load (T& obj, section& s)
+ {
+ return load_<T, id_mysql> (obj, s);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ find (const typename object_traits<T>::id_type& id)
+ {
+ return find_<T, id_mysql> (id);
+ }
+
+ template <typename T>
+ inline bool database::
+ find (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return find_<T, id_mysql> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T& obj)
+ {
+ reload_<T, id_mysql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T* p)
+ {
+ reload<T> (*p);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (const P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (const P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T>
+ inline void database::
+ reload (const typename object_traits<T>::pointer_type& pobj)
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ reload (odb::pointer_traits<pointer_type>::get_ref (pobj));
+ }
+
+ template <typename T>
+ inline void database::
+ update (T& obj)
+ {
+ update_<T, id_mysql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ update (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (P<T>& p)
+ {
+ const P<T>& cr (p);
+ update<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ update<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const typename object_traits<T>::pointer_type& pobj)
+ {
+ update_<T, id_mysql> (pobj);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const T& obj, const section& s)
+ {
+ update_<T, id_mysql> (obj, s);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::id_type& id)
+ {
+ return erase_<T, id_mysql> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T& obj)
+ {
+ return erase_<T, id_mysql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_mysql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (P<T>& p)
+ {
+ const P<T>& cr (p);
+ erase<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ erase<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::pointer_type& pobj)
+ {
+ erase_<T, id_mysql> (pobj);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query ()
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (mysql::query_base ());
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const char* q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const std::string& q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const mysql::query_base& q)
+ {
+ // T is always object_type.
+ //
+ return object_traits_impl<T, id_mysql>::erase_query (*this, q);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return erase_query<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (bool cache)
+ {
+ return query<T> (mysql::query_base (), cache);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const char* q, bool cache)
+ {
+ return query<T> (mysql::query_base (q), cache);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const std::string& q, bool cache)
+ {
+ return query<T> (mysql::query_base (q), cache);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const mysql::query_base& q, bool cache)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ result<T> r (query_<T, id_mysql>::call (*this, q));
+
+ if (cache)
+ r.cache ();
+
+ return r;
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const odb::query_base& q, bool cache)
+ {
+ // Translate to native query.
+ //
+ return query<T> (mysql::query_base (q), cache);
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one ()
+ {
+ return query_one<T> (mysql::query_base ());
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (T& o)
+ {
+ return query_one<T> (mysql::query_base (), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value ()
+ {
+ return query_value<T> (mysql::query_base ());
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const char* q)
+ {
+ return query_one<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const char* q, T& o)
+ {
+ return query_one<T> (mysql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const char* q)
+ {
+ return query_value<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const std::string& q)
+ {
+ return query_one<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const std::string& q, T& o)
+ {
+ return query_one<T> (mysql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const std::string& q)
+ {
+ return query_value<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const mysql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_mysql> (q);
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const mysql::query_base& q, T& o)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_mysql> (q, o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const mysql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_value_<T, id_mysql> (q);
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const odb::query_base& q, T& o)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (mysql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_value<T> (mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, mysql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const mysql::query_base& q)
+ {
+ // Throws if not in transaction.
+ //
+ mysql::connection& c (transaction::current ().connection (*this));
+ return c.prepare_query<T> (n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, mysql::query_base (q));
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/details/build2/config-stub.h b/libodb-mysql/odb/mysql/details/build2/config-stub.h
new file mode 100644
index 0000000..d9d08eb
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/build2/config-stub.h
@@ -0,0 +1,5 @@
+/* file : odb/mysql/details/build2/config-stub.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+#include <odb/mysql/details/config.h>
diff --git a/libodb-mysql/odb/mysql/details/build2/config-vc-stub.h b/libodb-mysql/odb/mysql/details/build2/config-vc-stub.h
new file mode 100644
index 0000000..e22c7c2
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/build2/config-vc-stub.h
@@ -0,0 +1,5 @@
+/* file : odb/mysql/details/build2/config-vc-stub.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+#include <odb/mysql/details/config-vc.h>
diff --git a/libodb-mysql/odb/mysql/details/build2/config-vc.h b/libodb-mysql/odb/mysql/details/build2/config-vc.h
new file mode 100644
index 0000000..3adb630
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/build2/config-vc.h
@@ -0,0 +1,15 @@
+/* file : odb/mysql/details/build2/config-vc.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* Configuration file for Windows/VC++ for the build2 build. */
+
+#ifndef ODB_MYSQL_DETAILS_CONFIG_VC_H
+#define ODB_MYSQL_DETAILS_CONFIG_VC_H
+
+/* Define LIBODB_MYSQL_BUILD2 for the installed case. */
+#ifndef LIBODB_MYSQL_BUILD2
+# define LIBODB_MYSQL_BUILD2
+#endif
+
+#endif /* ODB_MYSQL_DETAILS_CONFIG_VC_H */
diff --git a/libodb-mysql/odb/mysql/details/build2/config.h b/libodb-mysql/odb/mysql/details/build2/config.h
new file mode 100644
index 0000000..ef6a60f
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/build2/config.h
@@ -0,0 +1,17 @@
+/* file : odb/mysql/details/build2/config.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* Static configuration file for the build2 build. The installed case
+ (when LIBODB_MYSQL_BUILD2 is not necessarily defined) is the only
+ reason we have it. */
+
+#ifndef ODB_MYSQL_DETAILS_CONFIG_H
+#define ODB_MYSQL_DETAILS_CONFIG_H
+
+/* Define LIBODB_MYSQL_BUILD2 for the installed case. */
+#ifndef LIBODB_MYSQL_BUILD2
+# define LIBODB_MYSQL_BUILD2
+#endif
+
+#endif /* ODB_MYSQL_DETAILS_CONFIG_H */
diff --git a/libodb-mysql/odb/mysql/details/config-vc.h b/libodb-mysql/odb/mysql/details/config-vc.h
new file mode 100644
index 0000000..fb01864
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/config-vc.h
@@ -0,0 +1,12 @@
+/* file : odb/mysql/details/config-vc.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+#ifndef ODB_MYSQL_DETAILS_CONFIG_VC_H
+#define ODB_MYSQL_DETAILS_CONFIG_VC_H
+
+#if !defined(LIBODB_MYSQL_INCLUDE_SHORT) && !defined (LIBODB_MYSQL_INCLUDE_LONG)
+# define LIBODB_MYSQL_INCLUDE_SHORT 1
+#endif
+
+#endif /* ODB_MYSQL_DETAILS_CONFIG_VC_H */
diff --git a/libodb-mysql/odb/mysql/details/config.h.in b/libodb-mysql/odb/mysql/details/config.h.in
new file mode 100644
index 0000000..3b7869b
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/config.h.in
@@ -0,0 +1,17 @@
+/* file : odb/mysql/details/config.h.in
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* This file is automatically processed by configure. */
+
+#ifndef ODB_MYSQL_DETAILS_CONFIG_H
+#define ODB_MYSQL_DETAILS_CONFIG_H
+
+#undef LIBODB_MYSQL_STATIC_LIB
+
+#undef LIBODB_MYSQL_INCLUDE_SHORT
+#undef LIBODB_MYSQL_INCLUDE_LONG
+
+#undef LIBODB_MYSQL_THR_KEY_VISIBLE
+
+#endif /* ODB_MYSQL_DETAILS_CONFIG_H */
diff --git a/libodb-mysql/odb/mysql/details/config.hxx b/libodb-mysql/odb/mysql/details/config.hxx
new file mode 100644
index 0000000..eeeee5c
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/config.hxx
@@ -0,0 +1,32 @@
+// file : odb/mysql/details/config.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_DETAILS_CONFIG_HXX
+#define ODB_MYSQL_DETAILS_CONFIG_HXX
+
+// no pre
+
+#ifdef ODB_COMPILER
+# error libodb-mysql header included in odb-compiled header
+#elif !defined(LIBODB_MYSQL_BUILD2)
+# ifdef _MSC_VER
+# include <odb/mysql/details/config-vc.h>
+# else
+# include <odb/mysql/details/config.h>
+# endif
+#endif
+
+// LIBODB_MYSQL_BUILD2 macro can be defined either by the buildfile or by the
+// included odb/mysql/details/config*.h (see above).
+//
+#ifdef LIBODB_MYSQL_BUILD2
+# ifdef LIBODB_MYSQL_INCLUDE_SHORT
+# error mysql headers must be included with mysql/ prefix
+# elif !defined(LIBODB_MYSQL_INCLUDE_LONG)
+# define LIBODB_MYSQL_INCLUDE_LONG 1
+# endif
+#endif
+
+// no post
+
+#endif // ODB_MYSQL_DETAILS_CONFIG_HXX
diff --git a/libodb-mysql/odb/mysql/details/conversion.hxx b/libodb-mysql/odb/mysql/details/conversion.hxx
new file mode 100644
index 0000000..5d1f575
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/conversion.hxx
@@ -0,0 +1,58 @@
+// file : odb/mysql/details/conversion.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_DETAILS_CONVERSION_HXX
+#define ODB_MYSQL_DETAILS_CONVERSION_HXX
+
+#include <odb/mysql/traits.hxx>
+
+#include <odb/details/meta/answer.hxx>
+
+namespace odb
+{
+ // @@ Revise this.
+ //
+ namespace details {}
+
+ namespace mysql
+ {
+ namespace details
+ {
+ using namespace odb::details;
+
+ // Detect whether conversion is specified in type_traits.
+ //
+ template <typename T>
+ meta::yes
+ conversion_p_test (typename type_traits<T>::conversion*);
+
+ template <typename T>
+ meta::no
+ conversion_p_test (...);
+
+ template <typename T>
+ struct conversion_p
+ {
+ static const bool value =
+ sizeof (conversion_p_test<T> (0)) == sizeof (meta::yes);
+ };
+
+ template <typename T, bool = conversion_p<T>::value>
+ struct conversion;
+
+ template <typename T>
+ struct conversion<T, true>
+ {
+ static const char* to () {return type_traits<T>::conversion::to ();}
+ };
+
+ template <typename T>
+ struct conversion<T, false>
+ {
+ static const char* to () {return 0;}
+ };
+ }
+ }
+}
+
+#endif // ODB_MYSQL_DETAILS_CONVERSION_HXX
diff --git a/libodb-mysql/odb/mysql/details/export.hxx b/libodb-mysql/odb/mysql/details/export.hxx
new file mode 100644
index 0000000..e7c3871
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/export.hxx
@@ -0,0 +1,78 @@
+// file : odb/mysql/details/export.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_DETAILS_EXPORT_HXX
+#define ODB_MYSQL_DETAILS_EXPORT_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/mysql/details/config.hxx>
+
+// Normally we don't export class templates (but do complete specializations),
+// inline functions, and classes with only inline member functions. Exporting
+// classes that inherit from non-exported/imported bases (e.g., std::string)
+// will end up badly. The only known workarounds are to not inherit or to not
+// export. Also, MinGW GCC doesn't like seeing non-exported function being
+// used before their inline definition. The workaround is to reorder code. In
+// the end it's all trial and error.
+
+#ifdef LIBODB_MYSQL_BUILD2
+
+#if defined(LIBODB_MYSQL_STATIC) // Using static.
+# define LIBODB_MYSQL_EXPORT
+#elif defined(LIBODB_MYSQL_STATIC_BUILD) // Building static.
+# define LIBODB_MYSQL_EXPORT
+#elif defined(LIBODB_MYSQL_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBODB_MYSQL_EXPORT __declspec(dllimport)
+# else
+# define LIBODB_MYSQL_EXPORT
+# endif
+#elif defined(LIBODB_MYSQL_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBODB_MYSQL_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_MYSQL_EXPORT
+# endif
+#else
+// If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared but in case
+// of shared will be sub-optimal compared to having dllimport.
+//
+# define LIBODB_MYSQL_EXPORT // Using static or shared.
+#endif
+
+#else // LIBODB_MYSQL_BUILD2
+
+#ifdef LIBODB_MYSQL_STATIC_LIB
+# define LIBODB_MYSQL_EXPORT
+#else
+# ifdef _WIN32
+# ifdef _MSC_VER
+# ifdef LIBODB_MYSQL_DYNAMIC_LIB
+# define LIBODB_MYSQL_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_MYSQL_EXPORT __declspec(dllimport)
+# endif
+# else
+# ifdef LIBODB_MYSQL_DYNAMIC_LIB
+# ifdef DLL_EXPORT
+# define LIBODB_MYSQL_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_MYSQL_EXPORT
+# endif
+# else
+# define LIBODB_MYSQL_EXPORT __declspec(dllimport)
+# endif
+# endif
+# else
+# define LIBODB_MYSQL_EXPORT
+# endif
+#endif
+
+#endif // LIBODB_MYSQL_BUILD2
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_DETAILS_EXPORT_HXX
diff --git a/libodb-mysql/odb/mysql/details/options.cli b/libodb-mysql/odb/mysql/details/options.cli
new file mode 100644
index 0000000..845c292
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/options.cli
@@ -0,0 +1,61 @@
+// file : odb/mysql/details/options.cli
+// license : GNU GPL v2; see accompanying LICENSE file
+
+include <string>;
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace details
+ {
+ class options
+ {
+ std::string --user
+ {
+ "<name>",
+ "MySQL database user."
+ };
+
+ std::string --password
+ {
+ "<str>",
+ "MySQL database password"
+ };
+
+ std::string --database
+ {
+ "<name>",
+ "MySQL database name."
+ };
+
+ std::string --host
+ {
+ "<addr>",
+ "MySQL database host name or address (localhost by default)."
+ };
+
+ unsigned int --port = 0
+ {
+ "<integer>",
+ "MySQL database port number."
+ };
+
+ std::string --socket
+ {
+ "<name>",
+ "MySQL database socket name."
+ };
+
+ std::string --options-file
+ {
+ "<file>",
+ "Read additional options from <file>. Each option should appear on a
+ separate line optionally followed by space or equal sign (\cb{=})
+ and an option value. Empty lines and lines starting with \cb{#} are
+ ignored."
+ };
+ };
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.cxx b/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.cxx
new file mode 100644
index 0000000..1e15af6
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.cxx
@@ -0,0 +1,1125 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <odb/mysql/details/options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <utility>
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <fstream>
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // unknown_option
+ //
+ unknown_option::
+ ~unknown_option () throw ()
+ {
+ }
+
+ void unknown_option::
+ print (::std::ostream& os) const
+ {
+ os << "unknown option '" << option ().c_str () << "'";
+ }
+
+ const char* unknown_option::
+ what () const throw ()
+ {
+ return "unknown option";
+ }
+
+ // unknown_argument
+ //
+ unknown_argument::
+ ~unknown_argument () throw ()
+ {
+ }
+
+ void unknown_argument::
+ print (::std::ostream& os) const
+ {
+ os << "unknown argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unknown_argument::
+ what () const throw ()
+ {
+ return "unknown argument";
+ }
+
+ // missing_value
+ //
+ missing_value::
+ ~missing_value () throw ()
+ {
+ }
+
+ void missing_value::
+ print (::std::ostream& os) const
+ {
+ os << "missing value for option '" << option ().c_str () << "'";
+ }
+
+ const char* missing_value::
+ what () const throw ()
+ {
+ return "missing option value";
+ }
+
+ // invalid_value
+ //
+ invalid_value::
+ ~invalid_value () throw ()
+ {
+ }
+
+ void invalid_value::
+ print (::std::ostream& os) const
+ {
+ os << "invalid value '" << value ().c_str () << "' for option '"
+ << option ().c_str () << "'";
+
+ if (!message ().empty ())
+ os << ": " << message ().c_str ();
+ }
+
+ const char* invalid_value::
+ what () const throw ()
+ {
+ return "invalid option value";
+ }
+
+ // eos_reached
+ //
+ void eos_reached::
+ print (::std::ostream& os) const
+ {
+ os << what ();
+ }
+
+ const char* eos_reached::
+ what () const throw ()
+ {
+ return "end of argument stream reached";
+ }
+
+ // file_io_failure
+ //
+ file_io_failure::
+ ~file_io_failure () throw ()
+ {
+ }
+
+ void file_io_failure::
+ print (::std::ostream& os) const
+ {
+ os << "unable to open file '" << file ().c_str () << "' or read failure";
+ }
+
+ const char* file_io_failure::
+ what () const throw ()
+ {
+ return "unable to open file or read failure";
+ }
+
+ // unmatched_quote
+ //
+ unmatched_quote::
+ ~unmatched_quote () throw ()
+ {
+ }
+
+ void unmatched_quote::
+ print (::std::ostream& os) const
+ {
+ os << "unmatched quote in argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unmatched_quote::
+ what () const throw ()
+ {
+ return "unmatched quote";
+ }
+
+ // scanner
+ //
+ scanner::
+ ~scanner ()
+ {
+ }
+
+ // argv_scanner
+ //
+ bool argv_scanner::
+ more ()
+ {
+ return i_ < argc_;
+ }
+
+ const char* argv_scanner::
+ peek ()
+ {
+ if (i_ < argc_)
+ return argv_[i_];
+ else
+ throw eos_reached ();
+ }
+
+ const char* argv_scanner::
+ next ()
+ {
+ if (i_ < argc_)
+ {
+ const char* r (argv_[i_]);
+
+ if (erase_)
+ {
+ for (int i (i_ + 1); i < argc_; ++i)
+ argv_[i - 1] = argv_[i];
+
+ --argc_;
+ argv_[argc_] = 0;
+ }
+ else
+ ++i_;
+
+ ++start_position_;
+ return r;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ void argv_scanner::
+ skip ()
+ {
+ if (i_ < argc_)
+ {
+ ++i_;
+ ++start_position_;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ std::size_t argv_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ // argv_file_scanner
+ //
+ int argv_file_scanner::zero_argc_ = 0;
+ std::string argv_file_scanner::empty_string_;
+
+ bool argv_file_scanner::
+ more ()
+ {
+ if (!args_.empty ())
+ return true;
+
+ while (base::more ())
+ {
+ // See if the next argument is the file option.
+ //
+ const char* a (base::peek ());
+ const option_info* oi = 0;
+ const char* ov = 0;
+
+ if (!skip_)
+ {
+ if ((oi = find (a)) != 0)
+ {
+ base::next ();
+
+ if (!base::more ())
+ throw missing_value (a);
+
+ ov = base::next ();
+ }
+ else if (std::strncmp (a, "-", 1) == 0)
+ {
+ if ((ov = std::strchr (a, '=')) != 0)
+ {
+ std::string o (a, 0, ov - a);
+ if ((oi = find (o.c_str ())) != 0)
+ {
+ base::next ();
+ ++ov;
+ }
+ }
+ }
+ }
+
+ if (oi != 0)
+ {
+ if (oi->search_func != 0)
+ {
+ std::string f (oi->search_func (ov, oi->arg));
+
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ load (ov);
+
+ if (!args_.empty ())
+ return true;
+ }
+ else
+ {
+ if (!skip_)
+ skip_ = (std::strcmp (a, "--") == 0);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ const char* argv_file_scanner::
+ peek ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? base::peek () : args_.front ().value.c_str ();
+ }
+
+ const std::string& argv_file_scanner::
+ peek_file ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? empty_string_ : *args_.front ().file;
+ }
+
+ std::size_t argv_file_scanner::
+ peek_line ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? 0 : args_.front ().line;
+ }
+
+ const char* argv_file_scanner::
+ next ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::next ();
+ else
+ {
+ hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);
+ args_.pop_front ();
+ ++start_position_;
+ return hold_[i_].c_str ();
+ }
+ }
+
+ void argv_file_scanner::
+ skip ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::skip ();
+ else
+ {
+ args_.pop_front ();
+ ++start_position_;
+ }
+ }
+
+ const argv_file_scanner::option_info* argv_file_scanner::
+ find (const char* a) const
+ {
+ for (std::size_t i (0); i < options_count_; ++i)
+ if (std::strcmp (a, options_[i].option) == 0)
+ return &options_[i];
+
+ return 0;
+ }
+
+ std::size_t argv_file_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ void argv_file_scanner::
+ load (const std::string& file)
+ {
+ using namespace std;
+
+ ifstream is (file.c_str ());
+
+ if (!is.is_open ())
+ throw file_io_failure (file);
+
+ files_.push_back (file);
+
+ arg a;
+ a.file = &*files_.rbegin ();
+
+ for (a.line = 1; !is.eof (); ++a.line)
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ throw file_io_failure (file);
+
+ string::size_type n (line.size ());
+
+ // Trim the line from leading and trailing whitespaces.
+ //
+ if (n != 0)
+ {
+ const char* f (line.c_str ());
+ const char* l (f + n);
+
+ const char* of (f);
+ while (f < l && (*f == ' ' || *f == '\t' || *f == '\r'))
+ ++f;
+
+ --l;
+
+ const char* ol (l);
+ while (l > f && (*l == ' ' || *l == '\t' || *l == '\r'))
+ --l;
+
+ if (f != of || l != ol)
+ line = f <= l ? string (f, l - f + 1) : string ();
+ }
+
+ // Ignore empty lines, those that start with #.
+ //
+ if (line.empty () || line[0] == '#')
+ continue;
+
+ string::size_type p (string::npos);
+ if (line.compare (0, 1, "-") == 0)
+ {
+ p = line.find (' ');
+
+ string::size_type q (line.find ('='));
+ if (q != string::npos && q < p)
+ p = q;
+ }
+
+ string s1;
+ if (p != string::npos)
+ {
+ s1.assign (line, 0, p);
+
+ // Skip leading whitespaces in the argument.
+ //
+ if (line[p] == '=')
+ ++p;
+ else
+ {
+ n = line.size ();
+ for (++p; p < n; ++p)
+ {
+ char c (line[p]);
+ if (c != ' ' && c != '\t' && c != '\r')
+ break;
+ }
+ }
+ }
+ else if (!skip_)
+ skip_ = (line == "--");
+
+ string s2 (line, p != string::npos ? p : 0);
+
+ // If the string (which is an option value or argument) is
+ // wrapped in quotes, remove them.
+ //
+ n = s2.size ();
+ char cf (s2[0]), cl (s2[n - 1]);
+
+ if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
+ {
+ if (n == 1 || cf != cl)
+ throw unmatched_quote (s2);
+
+ s2 = string (s2, 1, n - 2);
+ }
+
+ if (!s1.empty ())
+ {
+ // See if this is another file option.
+ //
+ const option_info* oi;
+ if (!skip_ && (oi = find (s1.c_str ())))
+ {
+ if (s2.empty ())
+ throw missing_value (oi->option);
+
+ if (oi->search_func != 0)
+ {
+ string f (oi->search_func (s2.c_str (), oi->arg));
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ {
+ // If the path of the file being parsed is not simple and the
+ // path of the file that needs to be loaded is relative, then
+ // complete the latter using the former as a base.
+ //
+#ifndef _WIN32
+ string::size_type p (file.find_last_of ('/'));
+ bool c (p != string::npos && s2[0] != '/');
+#else
+ string::size_type p (file.find_last_of ("/\\"));
+ bool c (p != string::npos && s2[1] != ':');
+#endif
+ if (c)
+ s2.insert (0, file, 0, p + 1);
+
+ load (s2);
+ }
+
+ continue;
+ }
+
+ a.value = s1;
+ args_.push_back (a);
+ }
+
+ a.value = s2;
+ args_.push_back (a);
+ }
+ }
+
+ template <typename X>
+ struct parser
+ {
+ static void
+ parse (X& x, bool& xs, scanner& s)
+ {
+ using namespace std;
+
+ const char* o (s.next ());
+ if (s.more ())
+ {
+ string v (s.next ());
+ istringstream is (v);
+ if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<bool>
+ {
+ static void
+ parse (bool& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ const char* v (s.next ());
+
+ if (std::strcmp (v, "1") == 0 ||
+ std::strcmp (v, "true") == 0 ||
+ std::strcmp (v, "TRUE") == 0 ||
+ std::strcmp (v, "True") == 0)
+ x = true;
+ else if (std::strcmp (v, "0") == 0 ||
+ std::strcmp (v, "false") == 0 ||
+ std::strcmp (v, "FALSE") == 0 ||
+ std::strcmp (v, "False") == 0)
+ x = false;
+ else
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<std::string>
+ {
+ static void
+ parse (std::string& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ x = s.next ();
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X>
+ struct parser<std::pair<X, std::size_t> >
+ {
+ static void
+ parse (std::pair<X, std::size_t>& x, bool& xs, scanner& s)
+ {
+ x.second = s.position ();
+ parser<X>::parse (x.first, xs, s);
+ }
+ };
+
+ template <typename X>
+ struct parser<std::vector<X> >
+ {
+ static void
+ parse (std::vector<X>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.push_back (x);
+ xs = true;
+ }
+ };
+
+ template <typename X, typename C>
+ struct parser<std::set<X, C> >
+ {
+ static void
+ parse (std::set<X, C>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.insert (x);
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::map<K, V, C> >
+ {
+ static void
+ parse (std::map<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m[k] = v;
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::multimap<K, V, C> >
+ {
+ static void
+ parse (std::multimap<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m.insert (typename std::multimap<K, V, C>::value_type (k, v));
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X, typename T, T X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, s);
+ }
+
+ template <typename X, bool X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ s.next ();
+ x.*M = true;
+ }
+
+ template <typename X, typename T, T X::*M, bool X::*S>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, x.*S, s);
+ }
+ }
+ }
+ }
+}
+
+#include <map>
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace details
+ {
+ // options
+ //
+
+ options::
+ options ()
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (0),
+ port_specified_ (false),
+ socket_ (),
+ socket_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ bool erase,
+ ::odb::mysql::details::cli::unknown_mode opt,
+ ::odb::mysql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (0),
+ port_specified_ (false),
+ socket_ (),
+ socket_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mysql::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ ::odb::mysql::details::cli::unknown_mode opt,
+ ::odb::mysql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (0),
+ port_specified_ (false),
+ socket_ (),
+ socket_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mysql::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::mysql::details::cli::unknown_mode opt,
+ ::odb::mysql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (0),
+ port_specified_ (false),
+ socket_ (),
+ socket_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mysql::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::mysql::details::cli::unknown_mode opt,
+ ::odb::mysql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (0),
+ port_specified_ (false),
+ socket_ (),
+ socket_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::mysql::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (::odb::mysql::details::cli::scanner& s,
+ ::odb::mysql::details::cli::unknown_mode opt,
+ ::odb::mysql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (0),
+ port_specified_ (false),
+ socket_ (),
+ socket_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ _parse (s, opt, arg);
+ }
+
+ ::odb::mysql::details::cli::usage_para options::
+ print_usage (::std::ostream& os, ::odb::mysql::details::cli::usage_para p)
+ {
+ CLI_POTENTIALLY_UNUSED (os);
+
+ if (p != ::odb::mysql::details::cli::usage_para::none)
+ os << ::std::endl;
+
+ os << "--user <name> MySQL database user." << ::std::endl;
+
+ os << std::endl
+ << "--password <str> MySQL database password" << ::std::endl;
+
+ os << std::endl
+ << "--database <name> MySQL database name." << ::std::endl;
+
+ os << std::endl
+ << "--host <addr> MySQL database host name or address (localhost by" << ::std::endl
+ << " default)." << ::std::endl;
+
+ os << std::endl
+ << "--port <integer> MySQL database port number." << ::std::endl;
+
+ os << std::endl
+ << "--socket <name> MySQL database socket name." << ::std::endl;
+
+ os << std::endl
+ << "--options-file <file> Read additional options from <file>. Each option should" << ::std::endl
+ << " appear on a separate line optionally followed by space or" << ::std::endl
+ << " equal sign (=) and an option value. Empty lines and lines" << ::std::endl
+ << " starting with # are ignored." << ::std::endl;
+
+ p = ::odb::mysql::details::cli::usage_para::option;
+
+ return p;
+ }
+
+ typedef
+ std::map<std::string, void (*) (options&, ::odb::mysql::details::cli::scanner&)>
+ _cli_options_map;
+
+ static _cli_options_map _cli_options_map_;
+
+ struct _cli_options_map_init
+ {
+ _cli_options_map_init ()
+ {
+ _cli_options_map_["--user"] =
+ &::odb::mysql::details::cli::thunk< options, std::string, &options::user_,
+ &options::user_specified_ >;
+ _cli_options_map_["--password"] =
+ &::odb::mysql::details::cli::thunk< options, std::string, &options::password_,
+ &options::password_specified_ >;
+ _cli_options_map_["--database"] =
+ &::odb::mysql::details::cli::thunk< options, std::string, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["--host"] =
+ &::odb::mysql::details::cli::thunk< options, std::string, &options::host_,
+ &options::host_specified_ >;
+ _cli_options_map_["--port"] =
+ &::odb::mysql::details::cli::thunk< options, unsigned int, &options::port_,
+ &options::port_specified_ >;
+ _cli_options_map_["--socket"] =
+ &::odb::mysql::details::cli::thunk< options, std::string, &options::socket_,
+ &options::socket_specified_ >;
+ _cli_options_map_["--options-file"] =
+ &::odb::mysql::details::cli::thunk< options, std::string, &options::options_file_,
+ &options::options_file_specified_ >;
+ }
+ };
+
+ static _cli_options_map_init _cli_options_map_init_;
+
+ bool options::
+ _parse (const char* o, ::odb::mysql::details::cli::scanner& s)
+ {
+ _cli_options_map::const_iterator i (_cli_options_map_.find (o));
+
+ if (i != _cli_options_map_.end ())
+ {
+ (*(i->second)) (*this, s);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool options::
+ _parse (::odb::mysql::details::cli::scanner& s,
+ ::odb::mysql::details::cli::unknown_mode opt_mode,
+ ::odb::mysql::details::cli::unknown_mode arg_mode)
+ {
+ bool r = false;
+ bool opt = true;
+
+ while (s.more ())
+ {
+ const char* o = s.peek ();
+
+ if (std::strcmp (o, "--") == 0)
+ {
+ opt = false;
+ s.skip ();
+ r = true;
+ continue;
+ }
+
+ if (opt)
+ {
+ if (_parse (o, s))
+ {
+ r = true;
+ continue;
+ }
+
+ if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
+ {
+ // Handle combined option values.
+ //
+ std::string co;
+ if (const char* v = std::strchr (o, '='))
+ {
+ co.assign (o, 0, v - o);
+ ++v;
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (co.c_str ()),
+ const_cast<char*> (v)
+ };
+
+ ::odb::mysql::details::cli::argv_scanner ns (0, ac, av);
+
+ if (_parse (co.c_str (), ns))
+ {
+ // Parsed the option but not its value?
+ //
+ if (ns.end () != 2)
+ throw ::odb::mysql::details::cli::invalid_value (co, v);
+
+ s.next ();
+ r = true;
+ continue;
+ }
+ else
+ {
+ // Set the unknown option and fall through.
+ //
+ o = co.c_str ();
+ }
+ }
+
+ switch (opt_mode)
+ {
+ case ::odb::mysql::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::mysql::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::mysql::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::mysql::details::cli::unknown_option (o);
+ }
+ }
+
+ break;
+ }
+ }
+
+ switch (arg_mode)
+ {
+ case ::odb::mysql::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::mysql::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::mysql::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::mysql::details::cli::unknown_argument (o);
+ }
+ }
+
+ break;
+ }
+
+ return r;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
diff --git a/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.hxx b/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.hxx
new file mode 100644
index 0000000..daa7d52
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.hxx
@@ -0,0 +1,570 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+#ifndef LIBODB_MYSQL_DETAILS_OPTIONS_HXX
+#define LIBODB_MYSQL_DETAILS_OPTIONS_HXX
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <list>
+#include <deque>
+#include <iosfwd>
+#include <string>
+#include <cstddef>
+#include <exception>
+
+#ifndef CLI_POTENTIALLY_UNUSED
+# if defined(_MSC_VER) || defined(__xlC__)
+# define CLI_POTENTIALLY_UNUSED(x) (void*)&x
+# else
+# define CLI_POTENTIALLY_UNUSED(x) (void)x
+# endif
+#endif
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ class usage_para
+ {
+ public:
+ enum value
+ {
+ none,
+ text,
+ option
+ };
+
+ usage_para (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ class unknown_mode
+ {
+ public:
+ enum value
+ {
+ skip,
+ stop,
+ fail
+ };
+
+ unknown_mode (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ // Exceptions.
+ //
+
+ class exception: public std::exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const = 0;
+ };
+
+ ::std::ostream&
+ operator<< (::std::ostream&, const exception&);
+
+ class unknown_option: public exception
+ {
+ public:
+ virtual
+ ~unknown_option () throw ();
+
+ unknown_option (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class unknown_argument: public exception
+ {
+ public:
+ virtual
+ ~unknown_argument () throw ();
+
+ unknown_argument (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ class missing_value: public exception
+ {
+ public:
+ virtual
+ ~missing_value () throw ();
+
+ missing_value (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class invalid_value: public exception
+ {
+ public:
+ virtual
+ ~invalid_value () throw ();
+
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message = std::string ());
+
+ const std::string&
+ option () const;
+
+ const std::string&
+ value () const;
+
+ const std::string&
+ message () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ std::string value_;
+ std::string message_;
+ };
+
+ class eos_reached: public exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class file_io_failure: public exception
+ {
+ public:
+ virtual
+ ~file_io_failure () throw ();
+
+ file_io_failure (const std::string& file);
+
+ const std::string&
+ file () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string file_;
+ };
+
+ class unmatched_quote: public exception
+ {
+ public:
+ virtual
+ ~unmatched_quote () throw ();
+
+ unmatched_quote (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ // Command line argument scanner interface.
+ //
+ // The values returned by next() are guaranteed to be valid
+ // for the two previous arguments up until a call to a third
+ // peek() or next().
+ //
+ // The position() function returns a monotonically-increasing
+ // number which, if stored, can later be used to determine the
+ // relative position of the argument returned by the following
+ // call to next(). Note that if multiple scanners are used to
+ // extract arguments from multiple sources, then the end
+ // position of the previous scanner should be used as the
+ // start position of the next.
+ //
+ class scanner
+ {
+ public:
+ virtual
+ ~scanner ();
+
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+
+ virtual std::size_t
+ position () = 0;
+ };
+
+ class argv_scanner: public scanner
+ {
+ public:
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ int
+ end () const;
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ protected:
+ std::size_t start_position_;
+ int i_;
+ int& argc_;
+ char** argv_;
+ bool erase_;
+ };
+
+ class argv_file_scanner: public argv_scanner
+ {
+ public:
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t start_position = 0);
+
+ struct option_info
+ {
+ // If search_func is not NULL, it is called, with the arg
+ // value as the second argument, to locate the options file.
+ // If it returns an empty string, then the file is ignored.
+ //
+ const char* option;
+ std::string (*search_func) (const char*, void* arg);
+ void* arg;
+ };
+
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const option_info* options = 0,
+ std::size_t options_count = 0,
+ std::size_t start_position = 0);
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ // Return the file path if the peeked at argument came from a file and
+ // the empty string otherwise. The reference is guaranteed to be valid
+ // till the end of the scanner lifetime.
+ //
+ const std::string&
+ peek_file ();
+
+ // Return the 1-based line number if the peeked at argument came from
+ // a file and zero otherwise.
+ //
+ std::size_t
+ peek_line ();
+
+ private:
+ const option_info*
+ find (const char*) const;
+
+ void
+ load (const std::string& file);
+
+ typedef argv_scanner base;
+
+ const std::string option_;
+ option_info option_info_;
+ const option_info* options_;
+ std::size_t options_count_;
+
+ struct arg
+ {
+ std::string value;
+ const std::string* file;
+ std::size_t line;
+ };
+
+ std::deque<arg> args_;
+ std::list<std::string> files_;
+
+ // Circular buffer of two arguments.
+ //
+ std::string hold_[2];
+ std::size_t i_;
+
+ bool skip_;
+
+ static int zero_argc_;
+ static std::string empty_string_;
+ };
+
+ template <typename X>
+ struct parser;
+ }
+ }
+ }
+}
+
+#include <string>
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace details
+ {
+ class options
+ {
+ public:
+ options ();
+
+ options (int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::mysql::details::cli::unknown_mode option = ::odb::mysql::details::cli::unknown_mode::fail,
+ ::odb::mysql::details::cli::unknown_mode argument = ::odb::mysql::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::mysql::details::cli::unknown_mode option = ::odb::mysql::details::cli::unknown_mode::fail,
+ ::odb::mysql::details::cli::unknown_mode argument = ::odb::mysql::details::cli::unknown_mode::stop);
+
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::mysql::details::cli::unknown_mode option = ::odb::mysql::details::cli::unknown_mode::fail,
+ ::odb::mysql::details::cli::unknown_mode argument = ::odb::mysql::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::mysql::details::cli::unknown_mode option = ::odb::mysql::details::cli::unknown_mode::fail,
+ ::odb::mysql::details::cli::unknown_mode argument = ::odb::mysql::details::cli::unknown_mode::stop);
+
+ options (::odb::mysql::details::cli::scanner&,
+ ::odb::mysql::details::cli::unknown_mode option = ::odb::mysql::details::cli::unknown_mode::fail,
+ ::odb::mysql::details::cli::unknown_mode argument = ::odb::mysql::details::cli::unknown_mode::stop);
+
+ // Option accessors.
+ //
+ const std::string&
+ user () const;
+
+ bool
+ user_specified () const;
+
+ const std::string&
+ password () const;
+
+ bool
+ password_specified () const;
+
+ const std::string&
+ database () const;
+
+ bool
+ database_specified () const;
+
+ const std::string&
+ host () const;
+
+ bool
+ host_specified () const;
+
+ const unsigned int&
+ port () const;
+
+ bool
+ port_specified () const;
+
+ const std::string&
+ socket () const;
+
+ bool
+ socket_specified () const;
+
+ const std::string&
+ options_file () const;
+
+ bool
+ options_file_specified () const;
+
+ // Print usage information.
+ //
+ static ::odb::mysql::details::cli::usage_para
+ print_usage (::std::ostream&,
+ ::odb::mysql::details::cli::usage_para = ::odb::mysql::details::cli::usage_para::none);
+
+ // Implementation details.
+ //
+ protected:
+ bool
+ _parse (const char*, ::odb::mysql::details::cli::scanner&);
+
+ private:
+ bool
+ _parse (::odb::mysql::details::cli::scanner&,
+ ::odb::mysql::details::cli::unknown_mode option,
+ ::odb::mysql::details::cli::unknown_mode argument);
+
+ public:
+ std::string user_;
+ bool user_specified_;
+ std::string password_;
+ bool password_specified_;
+ std::string database_;
+ bool database_specified_;
+ std::string host_;
+ bool host_specified_;
+ unsigned int port_;
+ bool port_specified_;
+ std::string socket_;
+ bool socket_specified_;
+ std::string options_file_;
+ bool options_file_specified_;
+ };
+ }
+ }
+}
+
+#include <odb/mysql/details/options.ixx>
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
+#endif // LIBODB_MYSQL_DETAILS_OPTIONS_HXX
diff --git a/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.ixx b/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.ixx
new file mode 100644
index 0000000..66bc5df
--- /dev/null
+++ b/libodb-mysql/odb/mysql/details/pregenerated/odb/mysql/details/options.ixx
@@ -0,0 +1,384 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <cassert>
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // usage_para
+ //
+ inline usage_para::
+ usage_para (value v)
+ : v_ (v)
+ {
+ }
+
+ // unknown_mode
+ //
+ inline unknown_mode::
+ unknown_mode (value v)
+ : v_ (v)
+ {
+ }
+
+ // exception
+ //
+ inline ::std::ostream&
+ operator<< (::std::ostream& os, const exception& e)
+ {
+ e.print (os);
+ return os;
+ }
+
+ // unknown_option
+ //
+ inline unknown_option::
+ unknown_option (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& unknown_option::
+ option () const
+ {
+ return option_;
+ }
+
+ // unknown_argument
+ //
+ inline unknown_argument::
+ unknown_argument (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unknown_argument::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // missing_value
+ //
+ inline missing_value::
+ missing_value (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& missing_value::
+ option () const
+ {
+ return option_;
+ }
+
+ // invalid_value
+ //
+ inline invalid_value::
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message)
+ : option_ (option),
+ value_ (value),
+ message_ (message)
+ {
+ }
+
+ inline const std::string& invalid_value::
+ option () const
+ {
+ return option_;
+ }
+
+ inline const std::string& invalid_value::
+ value () const
+ {
+ return value_;
+ }
+
+ inline const std::string& invalid_value::
+ message () const
+ {
+ return message_;
+ }
+
+ // file_io_failure
+ //
+ inline file_io_failure::
+ file_io_failure (const std::string& file)
+ : file_ (file)
+ {
+ }
+
+ inline const std::string& file_io_failure::
+ file () const
+ {
+ return file_;
+ }
+
+ // unmatched_quote
+ //
+ inline unmatched_quote::
+ unmatched_quote (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unmatched_quote::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // argv_scanner
+ //
+ inline argv_scanner::
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + 1),
+ i_ (1),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline argv_scanner::
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + static_cast<std::size_t> (start)),
+ i_ (start),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline int argv_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ // argv_file_scanner
+ //
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+
+ load (file);
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const option_info* options,
+ std::size_t options_count,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ load (file);
+ }
+ }
+ }
+ }
+}
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace details
+ {
+ // options
+ //
+
+ inline const std::string& options::
+ user () const
+ {
+ return this->user_;
+ }
+
+ inline bool options::
+ user_specified () const
+ {
+ return this->user_specified_;
+ }
+
+ inline const std::string& options::
+ password () const
+ {
+ return this->password_;
+ }
+
+ inline bool options::
+ password_specified () const
+ {
+ return this->password_specified_;
+ }
+
+ inline const std::string& options::
+ database () const
+ {
+ return this->database_;
+ }
+
+ inline bool options::
+ database_specified () const
+ {
+ return this->database_specified_;
+ }
+
+ inline const std::string& options::
+ host () const
+ {
+ return this->host_;
+ }
+
+ inline bool options::
+ host_specified () const
+ {
+ return this->host_specified_;
+ }
+
+ inline const unsigned int& options::
+ port () const
+ {
+ return this->port_;
+ }
+
+ inline bool options::
+ port_specified () const
+ {
+ return this->port_specified_;
+ }
+
+ inline const std::string& options::
+ socket () const
+ {
+ return this->socket_;
+ }
+
+ inline bool options::
+ socket_specified () const
+ {
+ return this->socket_specified_;
+ }
+
+ inline const std::string& options::
+ options_file () const
+ {
+ return this->options_file_;
+ }
+
+ inline bool options::
+ options_file_specified () const
+ {
+ return this->options_file_specified_;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
diff --git a/libodb-mysql/odb/mysql/enum.cxx b/libodb-mysql/odb/mysql/enum.cxx
new file mode 100644
index 0000000..1e53b29
--- /dev/null
+++ b/libodb-mysql/odb/mysql/enum.cxx
@@ -0,0 +1,28 @@
+// file : odb/mysql/enums.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memmove
+#include <cassert>
+
+#include <odb/mysql/enum.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ void enum_traits::
+ strip_value (const details::buffer& i, unsigned long& size)
+ {
+ char* d (const_cast<char*> (i.data ()));
+
+ unsigned long p (0);
+ for (; p < size && d[p] != ' '; ++p) ;
+ assert (p != size);
+
+ p++; // Skip space;
+ size -= p;
+
+ std::memmove (d, d + p, size);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/enum.hxx b/libodb-mysql/odb/mysql/enum.hxx
new file mode 100644
index 0000000..da0436d
--- /dev/null
+++ b/libodb-mysql/odb/mysql/enum.hxx
@@ -0,0 +1,129 @@
+// file : odb/mysql/enums.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_ENUMS_HXX
+#define ODB_MYSQL_ENUMS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memmove
+#include <cassert>
+
+#include <odb/details/buffer.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/traits.hxx>
+#include <odb/mysql/version.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ // Common interface for working with the dual enum image (integer or
+ // string). Used by the generated code and query machinery.
+ //
+ // We use overload resolution instead of template specialization to
+ // avoid having to specify the image type explicitly.
+ //
+ struct LIBODB_MYSQL_EXPORT enum_traits
+ {
+ //
+ // Integer image.
+ //
+ static void
+ bind (MYSQL_BIND& b, unsigned short& i, unsigned long&, my_bool* is_null)
+ {
+ b.buffer_type = MYSQL_TYPE_SHORT;
+ b.is_unsigned = 1;
+ b.buffer = &i;
+ b.is_null = is_null;
+ }
+
+ static bool
+ grow (unsigned short&, unsigned long&)
+ {
+ return false;
+ }
+
+ template <typename T>
+ static bool
+ set_image (unsigned short& i, unsigned long&, bool& is_null, const T& v)
+ {
+ value_traits<T, id_enum>::set_image (i, is_null, v);
+ return false;
+ }
+
+ template <typename T>
+ static void
+ set_value (T& v, unsigned short i, unsigned long, bool is_null)
+ {
+ value_traits<T, id_enum>::set_value (v, i, is_null);
+ }
+
+ //
+ // String image.
+ //
+
+ static void
+ bind (MYSQL_BIND& b,
+ details::buffer& i,
+ unsigned long& size,
+ my_bool* is_null)
+ {
+ b.buffer_type = MYSQL_TYPE_STRING;
+ b.buffer = i.data ();
+ b.buffer_length = static_cast<unsigned long> (i.capacity ());
+ b.length = &size;
+ b.is_null = is_null;
+ }
+
+ static bool
+ grow (details::buffer& i, unsigned long& size)
+ {
+ i.capacity (size);
+ return true;
+ }
+
+ template <typename T>
+ static bool
+ set_image (details::buffer& i,
+ unsigned long& size,
+ bool& is_null,
+ const T& v)
+ {
+ std::size_t n (0), c (i.capacity ());
+ value_traits<T, id_enum>::set_image (i, n, is_null, v);
+ size = static_cast<unsigned long> (n);
+ return c != i.capacity ();
+ }
+
+ template <typename T>
+ static void
+ set_value (T& v,
+ const details::buffer& i,
+ unsigned long size,
+ bool is_null)
+ {
+ // The buffer has the following content: "<num> <str>". Get rid of
+ // the numeric value so that we have "<str>". For more information
+ // why it is done this way, see mysql::object_columns class in the
+ // ODB compiler.
+ //
+ strip_value (i, size);
+
+ value_traits<T, id_enum>::set_value (v, i, size, is_null);
+ }
+
+ private:
+ static void
+ strip_value (const details::buffer& i, unsigned long& size);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_ENUMS_HXX
diff --git a/libodb-mysql/odb/mysql/error.cxx b/libodb-mysql/odb/mysql/error.cxx
new file mode 100644
index 0000000..26380ec
--- /dev/null
+++ b/libodb-mysql/odb/mysql/error.cxx
@@ -0,0 +1,76 @@
+// file : odb/mysql/error.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <new> // std::bad_alloc
+#include <string>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/exceptions.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ static void
+ translate_error (connection& c,
+ unsigned int e,
+ const string& sqlstate,
+ string msg)
+ {
+ switch (e)
+ {
+ case CR_OUT_OF_MEMORY:
+ {
+ throw bad_alloc ();
+ }
+ case ER_LOCK_DEADLOCK:
+ {
+ throw deadlock ();
+ }
+ case CR_SERVER_LOST:
+ case CR_SERVER_GONE_ERROR:
+ {
+ c.mark_failed ();
+ throw connection_lost ();
+ }
+ case CR_UNKNOWN_ERROR:
+ {
+ c.mark_failed ();
+ }
+ // Fall through.
+ default:
+ {
+ // Get rid of a trailing newline if there is one.
+ //
+ string::size_type n (msg.size ());
+ if (n != 0 && msg[n - 1] == '\n')
+ msg.resize (n - 1);
+
+ throw database_exception (e, sqlstate, msg);
+ }
+ }
+ }
+
+ void
+ translate_error (connection& c)
+ {
+ MYSQL* h (c.handle ());
+ translate_error (c,
+ mysql_errno (h),
+ mysql_sqlstate (h),
+ mysql_error (h));
+ }
+
+ void
+ translate_error (connection& c, MYSQL_STMT* h)
+ {
+ translate_error (c,
+ mysql_stmt_errno (h),
+ mysql_stmt_sqlstate (h),
+ mysql_stmt_error (h));
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/error.hxx b/libodb-mysql/odb/mysql/error.hxx
new file mode 100644
index 0000000..0893841
--- /dev/null
+++ b/libodb-mysql/odb/mysql/error.hxx
@@ -0,0 +1,34 @@
+// file : odb/mysql/error.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_ERROR_HXX
+#define ODB_MYSQL_ERROR_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/version.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class connection;
+
+ // Translate MySQL error and throw an appropriate exception. Also,
+ // if the error code indicates that the connection is no longer
+ // usable, mark it as failed.
+ //
+ LIBODB_MYSQL_EXPORT void
+ translate_error (connection&);
+
+ LIBODB_MYSQL_EXPORT void
+ translate_error (connection&, MYSQL_STMT*);
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_ERROR_HXX
diff --git a/libodb-mysql/odb/mysql/exceptions.cxx b/libodb-mysql/odb/mysql/exceptions.cxx
new file mode 100644
index 0000000..b15d316
--- /dev/null
+++ b/libodb-mysql/odb/mysql/exceptions.cxx
@@ -0,0 +1,71 @@
+// file : odb/mysql/exceptions.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/mysql/exceptions.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // database_exception
+ //
+
+ database_exception::
+ ~database_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ database_exception::
+ database_exception (unsigned int e, const string& s, const string& m)
+ : error_ (e), sqlstate_ (s), message_ (m)
+ {
+ ostringstream ostr;
+ ostr << error_ << " (" << sqlstate_ << "): " << message_;
+ what_ = ostr.str ();
+ }
+
+ const char* database_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ database_exception* database_exception::
+ clone () const
+ {
+ return new database_exception (*this);
+ }
+
+ //
+ // cli_exception
+ //
+
+ cli_exception::
+ cli_exception (const std::string& what)
+ : what_ (what)
+ {
+ }
+
+ cli_exception::
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* cli_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ cli_exception* cli_exception::
+ clone () const
+ {
+ return new cli_exception (*this);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/exceptions.hxx b/libodb-mysql/odb/mysql/exceptions.hxx
new file mode 100644
index 0000000..5e9f9f1
--- /dev/null
+++ b/libodb-mysql/odb/mysql/exceptions.hxx
@@ -0,0 +1,87 @@
+// file : odb/mysql/exceptions.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_EXCEPTIONS_HXX
+#define ODB_MYSQL_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+
+#include <odb/exceptions.hxx>
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ struct LIBODB_MYSQL_EXPORT database_exception: odb::database_exception
+ {
+ database_exception (unsigned int,
+ const std::string& sqlstate,
+ const std::string& message);
+
+ ~database_exception () ODB_NOTHROW_NOEXCEPT;
+
+ unsigned int
+ error () const
+ {
+ return error_;
+ }
+
+ const std::string&
+ sqlstate () const
+ {
+ return sqlstate_;
+ }
+
+ const std::string&
+ message () const
+ {
+ return message_;
+ }
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual database_exception*
+ clone () const;
+
+ private:
+ unsigned int error_;
+ std::string sqlstate_;
+ std::string message_;
+ std::string what_;
+ };
+
+ struct LIBODB_MYSQL_EXPORT cli_exception: odb::exception
+ {
+ cli_exception (const std::string& what);
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT;
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual cli_exception*
+ clone () const;
+
+ private:
+ std::string what_;
+ };
+
+ namespace core
+ {
+ using mysql::database_exception;
+ using mysql::cli_exception;
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_EXCEPTIONS_HXX
diff --git a/libodb-mysql/odb/mysql/forward.hxx b/libodb-mysql/odb/mysql/forward.hxx
new file mode 100644
index 0000000..243c94e
--- /dev/null
+++ b/libodb-mysql/odb/mysql/forward.hxx
@@ -0,0 +1,91 @@
+// file : odb/mysql/forward.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_FORWARD_HXX
+#define ODB_MYSQL_FORWARD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ namespace core
+ {
+ using namespace odb::common;
+ }
+
+ //
+ //
+ class database;
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+ class connection_factory;
+ class statement;
+ class transaction;
+ class tracer;
+
+ namespace core
+ {
+ using mysql::database;
+ using mysql::connection;
+ using mysql::connection_ptr;
+ using mysql::transaction;
+ using mysql::statement;
+ }
+
+ // Implementation details.
+ //
+ enum statement_kind
+ {
+ statement_select,
+ statement_insert,
+ statement_update,
+ statement_delete
+ };
+
+ class binding;
+ class select_statement;
+
+ template <typename T>
+ class object_statements;
+
+ template <typename T>
+ class polymorphic_root_object_statements;
+
+ template <typename T>
+ class polymorphic_derived_object_statements;
+
+ template <typename T>
+ class no_id_object_statements;
+
+ template <typename T>
+ class view_statements;
+
+ template <typename T>
+ class container_statements;
+
+ template <typename T>
+ class smart_container_statements;
+
+ template <typename T, typename ST>
+ class section_statements;
+
+ class query_base;
+ }
+
+ namespace details
+ {
+ template <>
+ struct counter_type<mysql::connection>
+ {
+ typedef shared_base counter;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_FORWARD_HXX
diff --git a/libodb-mysql/odb/mysql/mysql-types.hxx b/libodb-mysql/odb/mysql/mysql-types.hxx
new file mode 100644
index 0000000..af7135b
--- /dev/null
+++ b/libodb-mysql/odb/mysql/mysql-types.hxx
@@ -0,0 +1,42 @@
+// file : odb/mysql/mysql-types.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_MYSQL_TYPES_HXX
+#define ODB_MYSQL_MYSQL_TYPES_HXX
+
+#include <odb/mysql/details/config.hxx>
+
+#include <odb/mysql/version.hxx>
+
+// Starting with 8.0.1 instead of my_bool MySQL uses bool directly. We keep
+// using the alias for compatibility with previous versions.
+//
+#if !defined(LIBODB_MYSQL_MARIADB) && MYSQL_VERSION_ID >= 80001
+typedef bool my_bool;
+#else
+typedef char my_bool;
+#endif
+
+// Starting with 8.0.11 MySQL renamed st_mysql_bind to MYSQL_BIND.
+//
+#if !defined(LIBODB_MYSQL_MARIADB) && MYSQL_VERSION_ID >= 80011
+struct MYSQL_BIND;
+#else
+typedef struct st_mysql_bind MYSQL_BIND;
+#endif
+
+// MariaDB defines time types directly in mysql.h. Note that MariaDB is only
+// supported by the build2 build so we include the header as <mysql/mysql.h>
+// unconditionally.
+//
+#ifdef LIBODB_MYSQL_MARIADB
+# include <mysql/mysql.h>
+#else
+# ifdef LIBODB_MYSQL_INCLUDE_SHORT
+# include <mysql_time.h>
+# else
+# include <mysql/mysql_time.h>
+# endif
+#endif
+
+#endif // ODB_MYSQL_MYSQL_TYPES_HXX
diff --git a/libodb-mysql/odb/mysql/mysql.hxx b/libodb-mysql/odb/mysql/mysql.hxx
new file mode 100644
index 0000000..38fdd62
--- /dev/null
+++ b/libodb-mysql/odb/mysql/mysql.hxx
@@ -0,0 +1,31 @@
+// file : odb/mysql/mysql.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_MYSQL_HXX
+#define ODB_MYSQL_MYSQL_HXX
+
+#include <odb/mysql/details/config.hxx>
+
+#include <odb/mysql/mysql-types.hxx>
+
+#ifdef _WIN32
+# ifndef NOMINMAX // No min and max macros.
+# define NOMINMAX
+# include <winsock2.h>
+# undef NOMINMAX
+# else
+# include <winsock2.h>
+# endif
+#endif
+
+#ifdef LIBODB_MYSQL_INCLUDE_SHORT
+# include <mysql.h>
+# include <errmsg.h>
+# include <mysqld_error.h>
+#else
+# include <mysql/mysql.h>
+# include <mysql/errmsg.h>
+# include <mysql/mysqld_error.h>
+#endif
+
+#endif // ODB_MYSQL_MYSQL_HXX
diff --git a/libodb-mysql/odb/mysql/no-id-object-result.hxx b/libodb-mysql/odb/mysql/no-id-object-result.hxx
new file mode 100644
index 0000000..7a38120
--- /dev/null
+++ b/libodb-mysql/odb/mysql/no-id-object-result.hxx
@@ -0,0 +1,81 @@
+// file : odb/mysql/no-id-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_NO_ID_OBJECT_RESULT_HXX
+#define ODB_MYSQL_NO_ID_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/no-id-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx> // query_base
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ class no_id_object_result_impl: public odb::no_id_object_result_impl<T>
+ {
+ public:
+ typedef odb::no_id_object_result_impl<T> base_type;
+
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_mysql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~no_id_object_result_impl ();
+
+ no_id_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ fetch ();
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/mysql/no-id-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_NO_ID_OBJECT_RESULT_HXX
diff --git a/libodb-mysql/odb/mysql/no-id-object-result.txx b/libodb-mysql/odb/mysql/no-id-object-result.txx
new file mode 100644
index 0000000..21168b9
--- /dev/null
+++ b/libodb-mysql/odb/mysql/no-id-object-result.txx
@@ -0,0 +1,178 @@
+// file : odb/mysql/no-id-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/mysql/no-id-object-statements.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ no_id_object_result_impl<T>::
+ ~no_id_object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ no_id_object_result_impl<T>::
+ no_id_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ load (object_type& obj)
+ {
+ if (count_ > statement_->fetched ())
+ fetch ();
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+ tc_.init (obj, statements_.image (), &this->db_);
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (this->end_)
+ return;
+
+ // If we are cached, simply increment the position and
+ // postpone the actual row fetching until later. This way
+ // if the same object is loaded in between iteration, the
+ // image won't be messed up.
+ //
+ count_++;
+
+ if (statement_->cached ())
+ this->end_ = count_ > statement_->result_size ();
+ else
+ fetch ();
+
+ if (this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ fetch ()
+ {
+ // If the result is cached, the image can grow between calls
+ // to fetch() as a result of other statements execution.
+ //
+ if (statement_->cached ())
+ {
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+ }
+
+ while (!this->end_ && count_ > statement_->fetched ())
+ {
+ select_statement::result r (statement_->fetch ());
+
+ switch (r)
+ {
+ case select_statement::truncated:
+ {
+ // Don't re-fetch data we are skipping.
+ //
+ if (count_ != statement_->fetched ())
+ continue;
+
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ statement_->refetch ();
+ }
+ // Fall throught.
+ }
+ case select_statement::success:
+ {
+ break;
+ }
+ case select_statement::no_data:
+ {
+ this->end_ = true;
+ break;
+ }
+ }
+ }
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ cache ()
+ {
+ if (!this->end_ && !statement_->cached ())
+ {
+ statement_->cache ();
+
+ if (count_ == statement_->result_size ())
+ {
+ statement_->free_result ();
+ count_++; // One past the result size.
+ this->end_ = true;
+ }
+ }
+ }
+
+ template <typename T>
+ std::size_t no_id_object_result_impl<T>::
+ size ()
+ {
+ if (!this->end_)
+ {
+ if (!statement_->cached ())
+ throw result_not_cached ();
+
+ return statement_->result_size ();
+ }
+ else
+ return count_ - 1; // One past the result size.
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/no-id-object-statements.hxx b/libodb-mysql/odb/mysql/no-id-object-statements.hxx
new file mode 100644
index 0000000..b554b72
--- /dev/null
+++ b/libodb-mysql/odb/mysql/no-id-object-statements.hxx
@@ -0,0 +1,135 @@
+// file : odb/mysql/no-id-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_NO_ID_OBJECT_STATEMENTS_HXX
+#define ODB_MYSQL_NO_ID_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/binding.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/statements-base.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // Implementation for objects without object id.
+ //
+
+ template <typename T>
+ class no_id_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mysql> object_traits;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef mysql::insert_statement insert_statement_type;
+
+ public:
+ no_id_object_statements (connection_type&);
+
+ virtual
+ ~no_id_object_statements ();
+
+ // Object image.
+ //
+ image_type&
+ image () {return image_;}
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Select binding (needed for query support).
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ my_bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ 0,
+ false));
+
+ return *persist_;
+ }
+
+ public:
+ // select = total
+ // insert = total - inverse; inverse == 0 for object without id
+ //
+ static const std::size_t select_column_count =
+ object_traits::column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count;
+
+ private:
+ no_id_object_statements (const no_id_object_statements&);
+ no_id_object_statements& operator= (const no_id_object_statements&);
+
+ private:
+ image_type image_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ MYSQL_BIND select_image_bind_[select_column_count];
+ my_bool select_image_truncated_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ MYSQL_BIND insert_image_bind_[insert_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ };
+ }
+}
+
+#include <odb/mysql/no-id-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_NO_ID_OBJECT_STATEMENTS_HXX
diff --git a/libodb-mysql/odb/mysql/no-id-object-statements.txx b/libodb-mysql/odb/mysql/no-id-object-statements.txx
new file mode 100644
index 0000000..5917461
--- /dev/null
+++ b/libodb-mysql/odb/mysql/no-id-object-statements.txx
@@ -0,0 +1,36 @@
+// file : odb/mysql/no-id-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ no_id_object_statements<T>::
+ ~no_id_object_statements ()
+ {
+ }
+
+ template <typename T>
+ no_id_object_statements<T>::
+ no_id_object_statements (connection_type& conn)
+ : statements_base (conn),
+ select_image_binding_ (select_image_bind_, select_column_count),
+ insert_image_binding_ (insert_image_bind_, insert_column_count)
+ {
+ image_.version = 0;
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].error = select_image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/polymorphic-object-result.hxx b/libodb-mysql/odb/mysql/polymorphic-object-result.hxx
new file mode 100644
index 0000000..1ccc313
--- /dev/null
+++ b/libodb-mysql/odb/mysql/polymorphic-object-result.hxx
@@ -0,0 +1,94 @@
+// file : odb/mysql/polymorphic-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_POLYMORPHIC_OBJECT_RESULT_HXX
+#define ODB_MYSQL_POLYMORPHIC_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/polymorphic-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx> // query_base
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ class polymorphic_object_result_impl:
+ public odb::polymorphic_object_result_impl<T>
+ {
+ public:
+ typedef odb::polymorphic_object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_mysql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename base_type::root_type root_type;
+ typedef typename base_type::discriminator_type discriminator_type;
+
+ typedef object_traits_impl<root_type, id_mysql> root_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~polymorphic_object_result_impl ();
+
+ polymorphic_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type*, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual discriminator_type
+ load_discriminator ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ fetch (bool next = true);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/mysql/polymorphic-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_POLYMORPHIC_OBJECT_RESULT_HXX
diff --git a/libodb-mysql/odb/mysql/polymorphic-object-result.txx b/libodb-mysql/odb/mysql/polymorphic-object-result.txx
new file mode 100644
index 0000000..e587f6f
--- /dev/null
+++ b/libodb-mysql/odb/mysql/polymorphic-object-result.txx
@@ -0,0 +1,370 @@
+// file : odb/mysql/polymorphic-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/mysql/polymorphic-object-statements.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ ~polymorphic_object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ polymorphic_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> st,
+ statements_type& sts,
+ const schema_version_migration* svm)
+ : base_type (sts.connection ()),
+ statement_ (st),
+ statements_ (sts),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ load (object_type* pobj, bool f)
+ {
+ if (count_ > statement_->fetched ())
+ fetch ();
+ else if (f && statement_->cached ())
+ {
+ // We have to re-load the image in case it has been overwritten
+ // between the last time we fetched and this call to load().
+ //
+ fetch (false);
+ }
+
+ typename statements_type::root_statements_type& rsts (
+ statements_.root_statements ());
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!rsts.locked ());
+ typename statements_type::auto_lock l (rsts);
+
+ typename object_traits::image_type& i (statements_.image ());
+ typename root_traits::image_type& ri (rsts.image ());
+
+ id_type id (root_traits::id (ri));
+
+ // Determine this object's dynamic type.
+ //
+ typedef typename root_traits::info_type info_type;
+ discriminator_type d (root_traits::discriminator (ri));
+ discriminator_type disc (d);
+
+ // Use the polymorphic_info() helper to get concrete_info if
+ // object_type is concrete and NULL if it is abstract.
+ //
+ const info_type* spi (polymorphic_info (object_traits::info));
+ const info_type& pi (
+ spi != 0 && spi->discriminator == d
+ ? *spi
+ : root_traits::map->find (d));
+
+ typedef typename root_traits::pointer_type root_pointer_type;
+ typedef typename root_traits::pointer_traits root_pointer_traits;
+
+ typename object_traits::pointer_cache_traits::insert_guard ig;
+
+ if (pobj == 0)
+ {
+ // Need to create a new instance of the dynamic type.
+ //
+ root_pointer_type rp (pi.create ());
+ pointer_type p (
+ root_pointer_traits::template static_pointer_cast<object_type> (rp));
+
+ // Insert it as a root pointer (for non-unique pointers, rp should
+ // still be valid and for unique pointers this is a no-op).
+ //
+ ig.reset (
+ object_traits::pointer_cache_traits::insert (this->db_, id, rp));
+
+ pobj = &pointer_traits::get_ref (p);
+ current (p);
+ }
+ else
+ {
+ // We are loading into an existing instance. If the static and
+ // dynamic types differ, then make sure the instance is at least
+ // of the dynamic type.
+ //
+ if (&pi != &object_traits::info)
+ {
+ const info_type& dpi (root_traits::map->find (typeid (*pobj)));
+
+ if (&dpi != &pi && dpi.derived (pi))
+ throw object_not_persistent (); // @@ type_mismatch ?
+ }
+ }
+
+ callback_event ce (callback_event::pre_load);
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+
+ tc_.init (*pobj, i, &this->db_);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, dynamic part, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ root_traits::init (idi, id);
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, *pobj, false);
+
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ if (&pi != &object_traits::info)
+ {
+ std::size_t d (object_traits::depth);
+ pi.dispatch (info_type::call_load, this->db_, pobj, &d);
+ };
+
+ rsts.load_delayed (tc_.version ());
+ l.unlock ();
+
+ ce = callback_event::post_load;
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+ object_traits::pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::id_type
+ polymorphic_object_result_impl<T>::
+ load_id ()
+ {
+ if (count_ > statement_->fetched ())
+ fetch ();
+ else if (statement_->cached ())
+ {
+ // We have to re-load the image in case it has been overwritten
+ // between the last time we fetched and this call to load_id().
+ //
+ fetch (false);
+ }
+
+ return root_traits::id (statements_.root_statements ().image ());
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::discriminator_type
+ polymorphic_object_result_impl<T>::
+ load_discriminator ()
+ {
+ if (count_ > statement_->fetched ())
+ fetch ();
+ else if (statement_->cached ())
+ {
+ // We have to re-load the image in case it has been overwritten
+ // between the last time we fetched and this call to
+ // load_discriminator().
+ //
+ fetch (false);
+ }
+
+ return root_traits::discriminator (
+ statements_.root_statements ().image ());
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (this->end_)
+ return;
+
+ // If we are cached, simply increment the position and
+ // postpone the actual row fetching until later. This way
+ // if the same object is loaded in between iteration, the
+ // image won't be messed up.
+ //
+ count_++;
+
+ if (statement_->cached ())
+ this->end_ = count_ > statement_->result_size ();
+ else
+ fetch ();
+
+ if (this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T, typename R>
+ struct polymorphic_image_rebind
+ {
+ // Derived type version.
+ //
+ typedef object_traits_impl<T, id_mysql> traits;
+
+ static bool
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (traits::check_version (sts.select_image_versions (), im))
+ {
+ binding& b (sts.select_image_binding (traits::depth));
+ object_traits_calls<T> tc (svm);
+ tc.bind (b.bind, 0, 0, im, statement_select);
+ traits::update_version (
+ sts.select_image_versions (), im, sts.select_image_bindings ());
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ template <typename R>
+ struct polymorphic_image_rebind<R, R>
+ {
+ // Root type version.
+ //
+ typedef object_traits_impl<R, id_mysql> traits;
+
+ static bool
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (im.version != sts.select_image_version ())
+ {
+ binding& b (sts.select_image_binding ());
+ object_traits_calls<R> tc (svm);
+ tc.bind (b.bind, im, statement_select);
+ sts.select_image_version (im.version);
+ b.version++;
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ fetch (bool next)
+ {
+ typedef polymorphic_image_rebind<object_type, root_type> image_rebind;
+
+ // If the result is cached, the image can grow between calls
+ // to fetch() as a result of other statements execution.
+ //
+ if (statement_->cached ())
+ image_rebind::rebind (statements_, tc_.version ());
+
+ while (!this->end_ && (!next || count_ > statement_->fetched ()))
+ {
+ select_statement::result r (statement_->fetch (next));
+
+ switch (r)
+ {
+ case select_statement::truncated:
+ {
+ // Don't re-fetch data we are skipping.
+ //
+ if (next && count_ != statement_->fetched ())
+ continue;
+
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (image_rebind::rebind (statements_, tc_.version ()))
+ statement_->refetch ();
+
+ // Fall throught.
+ }
+ case select_statement::success:
+ {
+ break;
+ }
+ case select_statement::no_data:
+ {
+ this->end_ = true;
+ break;
+ }
+ }
+
+ // If we are refetching the current row, then we are done.
+ //
+ if (!next)
+ break;
+ }
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ cache ()
+ {
+ if (!this->end_ && !statement_->cached ())
+ {
+ statement_->cache ();
+
+ if (count_ == statement_->result_size ())
+ {
+ statement_->free_result ();
+ count_++; // One past the result size.
+ this->end_ = true;
+ }
+ }
+ }
+
+ template <typename T>
+ std::size_t polymorphic_object_result_impl<T>::
+ size ()
+ {
+ if (!this->end_)
+ {
+ if (!statement_->cached ())
+ throw result_not_cached ();
+
+ return statement_->result_size ();
+ }
+ else
+ return count_ - 1; // One past the result size.
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/polymorphic-object-statements.hxx b/libodb-mysql/odb/mysql/polymorphic-object-statements.hxx
new file mode 100644
index 0000000..0eacac4
--- /dev/null
+++ b/libodb-mysql/odb/mysql/polymorphic-object-statements.hxx
@@ -0,0 +1,474 @@
+// file : odb/mysql/polymorphic-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+#define ODB_MYSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/binding.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/statements-base.hxx>
+#include <odb/mysql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // Implementation for polymorphic objects.
+ //
+
+ template <typename T>
+ class polymorphic_root_object_statements: public object_statements<T>
+ {
+ public:
+ typedef typename object_statements<T>::connection_type connection_type;
+ typedef typename object_statements<T>::object_traits object_traits;
+ typedef typename object_statements<T>::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::discriminator_image_type
+ discriminator_image_type;
+
+ typedef
+ typename object_statements<T>::select_statement_type
+ select_statement_type;
+
+ public:
+ // Interface compatibility with derived_object_statements.
+ //
+ typedef polymorphic_root_object_statements root_statements_type;
+
+ root_statements_type&
+ root_statements ()
+ {
+ return *this;
+ }
+
+ public:
+ // Discriminator binding.
+ //
+ discriminator_image_type&
+ discriminator_image () {return discriminator_image_;}
+
+ std::size_t
+ discriminator_image_version () const
+ {return discriminator_image_version_;}
+
+ void
+ discriminator_image_version (std::size_t v)
+ {discriminator_image_version_ = v;}
+
+ binding&
+ discriminator_image_binding () {return discriminator_image_binding_;}
+
+ my_bool*
+ discriminator_image_truncated () {return discriminator_image_truncated_;}
+
+ // Id binding for discriminator retrieval.
+ //
+ id_image_type&
+ discriminator_id_image () {return discriminator_id_image_;}
+
+ std::size_t
+ discriminator_id_image_version () const
+ {return discriminator_id_image_version_;}
+
+ void
+ discriminator_id_image_version (std::size_t v)
+ {discriminator_id_image_version_ = v;}
+
+ binding&
+ discriminator_id_image_binding ()
+ {return discriminator_id_image_binding_;}
+
+ //
+ //
+ select_statement_type&
+ find_discriminator_statement ()
+ {
+ if (find_discriminator_ == 0)
+ find_discriminator_.reset (
+ new (details::shared) select_statement_type (
+ this->conn_,
+ object_traits::find_discriminator_statement,
+ false, // Doesn't need to be processed.
+ false, // Don't optimize.
+ discriminator_id_image_binding_,
+ discriminator_image_binding_,
+ false));
+
+ return *find_discriminator_;
+ }
+
+ public:
+ polymorphic_root_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_root_object_statements ();
+
+ // Static "override" (statements type).
+ //
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (this->locked ());
+
+ if (!this->delayed_.empty ())
+ this->template load_delayed_<polymorphic_root_object_statements> (
+ svm);
+ }
+
+ public:
+ static const std::size_t id_column_count =
+ object_statements<T>::id_column_count;
+
+ static const std::size_t discriminator_column_count =
+ object_traits::discriminator_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ private:
+ // Discriminator image.
+ //
+ discriminator_image_type discriminator_image_;
+ std::size_t discriminator_image_version_;
+ binding discriminator_image_binding_;
+ MYSQL_BIND discriminator_image_bind_[discriminator_column_count +
+ managed_optimistic_column_count];
+ my_bool discriminator_image_truncated_[discriminator_column_count +
+ managed_optimistic_column_count];
+
+ // Id image for discriminator retrieval (only used as a parameter).
+ //
+ id_image_type discriminator_id_image_;
+ std::size_t discriminator_id_image_version_;
+ binding discriminator_id_image_binding_;
+ MYSQL_BIND discriminator_id_image_bind_[id_column_count];
+
+ details::shared_ptr<select_statement_type> find_discriminator_;
+ };
+
+ template <typename T>
+ class polymorphic_derived_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mysql> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::id_image_type id_image_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef typename object_traits::root_type root_type;
+ typedef
+ polymorphic_root_object_statements<root_type>
+ root_statements_type;
+
+ typedef typename object_traits::base_type base_type;
+ typedef
+ typename object_traits::base_traits::statements_type
+ base_statements_type;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef mysql::insert_statement insert_statement_type;
+ typedef mysql::select_statement select_statement_type;
+ typedef mysql::update_statement update_statement_type;
+ typedef mysql::delete_statement delete_statement_type;
+
+ typedef typename root_statements_type::auto_lock auto_lock;
+
+ public:
+ polymorphic_derived_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_derived_object_statements ();
+
+ public:
+ // Delayed loading.
+ //
+ static void
+ delayed_loader (odb::database&,
+ const id_type&,
+ root_type&,
+ const schema_version_migration*);
+
+ public:
+ // Root and immediate base statements.
+ //
+ root_statements_type&
+ root_statements ()
+ {
+ return root_statements_;
+ }
+
+ base_statements_type&
+ base_statements ()
+ {
+ return base_statements_;
+ }
+
+ public:
+ // Object image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ std::size_t
+ insert_id_binding_version () const { return insert_id_binding_version_;}
+
+ void
+ insert_id_binding_version (std::size_t v) {insert_id_binding_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t*
+ select_image_versions () { return select_image_versions_;}
+
+ binding*
+ select_image_bindings () {return select_image_bindings_;}
+
+ binding&
+ select_image_binding (std::size_t d)
+ {
+ return select_image_bindings_[object_traits::depth - d];
+ }
+
+ my_bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Object id binding (comes from the root statements).
+ //
+ id_image_type&
+ id_image () {return root_statements_.id_image ();}
+
+ std::size_t
+ id_image_version () const {return root_statements_.id_image_version ();}
+
+ void
+ id_image_version (std::size_t v) {root_statements_.id_image_version (v);}
+
+ binding&
+ id_image_binding () {return root_statements_.id_image_binding ();}
+
+ binding&
+ optimistic_id_image_binding () {
+ return root_statements_.optimistic_id_image_binding ();}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ 0,
+ false));
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement (std::size_t d)
+ {
+ std::size_t i (object_traits::depth - d);
+ details::shared_ptr<select_statement_type>& p (find_[i]);
+
+ if (p == 0)
+ p.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statements[i],
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ root_statements_.id_image_binding (),
+ select_image_bindings_[i],
+ false));
+
+ return *p;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
+ update_image_binding_,
+ false));
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement,
+ root_statements_.id_image_binding (),
+ false));
+
+ return *erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ image_,
+ id_image (),
+ id_image_binding (),
+ &id_image_binding ()); // Note, not id+version.
+ }
+
+ public:
+ // select = total - id - separate_load + base::select
+ // insert = total - inverse
+ // update = total - inverse - id - readonly - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ id_column_count -
+ object_traits::separate_load_column_count +
+ base_statements_type::select_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count;
+
+ static const std::size_t update_column_count = insert_column_count -
+ object_traits::id_column_count -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ polymorphic_derived_object_statements (
+ const polymorphic_derived_object_statements&);
+
+ polymorphic_derived_object_statements&
+ operator= (const polymorphic_derived_object_statements&);
+
+ private:
+ root_statements_type& root_statements_;
+ base_statements_type& base_statements_;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ image_type image_;
+
+ // Select binding. Here we are have an array of statements/bindings
+ // one for each depth. In other words, if we have classes root, base,
+ // and derived, then we have the following array of statements:
+ //
+ // [0] d + b + r
+ // [1] d + b
+ // [2] d
+ //
+ // Also, because we have a chain of images bound to these statements,
+ // we have an array of versions, one entry for each base plus one for
+ // our own image.
+ //
+ // A poly-abstract class only needs the first statement and in this
+ // case we have only one entry in the the bindings and statements
+ // arrays (but not versions; we still have a chain of images).
+ //
+ std::size_t select_image_versions_[object_traits::depth];
+ binding select_image_bindings_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ MYSQL_BIND select_image_bind_[select_column_count];
+ my_bool select_image_truncated_[select_column_count];
+
+ // Insert binding. The id binding is copied from the hierarchy root.
+ //
+ std::size_t insert_image_version_;
+ std::size_t insert_id_binding_version_;
+ binding insert_image_binding_;
+ MYSQL_BIND insert_image_bind_[insert_column_count];
+
+ // Update binding. The id suffix binding is copied from the hierarchy
+ // root.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+ binding update_image_binding_;
+ MYSQL_BIND update_image_bind_[update_column_count + id_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+ };
+ }
+}
+
+#include <odb/mysql/polymorphic-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
diff --git a/libodb-mysql/odb/mysql/polymorphic-object-statements.txx b/libodb-mysql/odb/mysql/polymorphic-object-statements.txx
new file mode 100644
index 0000000..5899bc0
--- /dev/null
+++ b/libodb-mysql/odb/mysql/polymorphic-object-statements.txx
@@ -0,0 +1,145 @@
+// file : odb/mysql/polymorphic-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/transaction.hxx>
+#include <odb/mysql/statement-cache.hxx>
+#include <odb/mysql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // polymorphic_root_object_statements
+ //
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ ~polymorphic_root_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ polymorphic_root_object_statements (connection_type& conn)
+ : object_statements<T> (conn),
+ discriminator_image_binding_ (discriminator_image_bind_,
+ discriminator_column_count +
+ managed_optimistic_column_count),
+ discriminator_id_image_binding_ (discriminator_id_image_bind_,
+ id_column_count)
+ {
+ discriminator_image_.version = 0;
+ discriminator_id_image_.version = 0;
+
+ discriminator_image_version_ = 0;
+ discriminator_id_image_version_ = 0;
+
+ std::memset (discriminator_image_bind_,
+ 0,
+ sizeof (discriminator_image_bind_));
+ std::memset (discriminator_id_image_bind_,
+ 0,
+ sizeof (discriminator_id_image_bind_));
+ std::memset (discriminator_image_truncated_,
+ 0,
+ sizeof (discriminator_image_truncated_));
+
+ for (std::size_t i (0);
+ i < discriminator_column_count + managed_optimistic_column_count;
+ ++i)
+ {
+ discriminator_image_bind_[i].error =
+ discriminator_image_truncated_ + i;
+ }
+ }
+
+ //
+ // polymorphic_derived_object_statements
+ //
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ ~polymorphic_derived_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ polymorphic_derived_object_statements (connection_type& conn)
+ : statements_base (conn),
+ root_statements_ (conn.statement_cache ().find_object<root_type> ()),
+ base_statements_ (conn.statement_cache ().find_object<base_type> ()),
+ insert_image_binding_ (insert_image_bind_, insert_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count)
+ {
+ image_.base = &base_statements_.image ();
+ image_.version = 0;
+
+ for (std::size_t i (0); i < object_traits::depth; ++i)
+ select_image_versions_[i] = 0;
+
+ for (std::size_t i (0);
+ i < (object_traits::abstract ? 1 : object_traits::depth);
+ ++i)
+ {
+ select_image_bindings_[i].bind = select_image_bind_;
+ select_image_bindings_[i].count = object_traits::find_column_counts[i];
+ }
+
+ insert_image_version_ = 0;
+ insert_id_binding_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].error = select_image_truncated_ + i;
+ }
+
+ template <typename T>
+ void polymorphic_derived_object_statements<T>::
+ delayed_loader (odb::database& db,
+ const id_type& id,
+ root_type& robj,
+ const schema_version_migration* svm)
+ {
+ connection_type& conn (transaction::current ().connection (db));
+ polymorphic_derived_object_statements& sts (
+ conn.statement_cache ().find_object<object_type> ());
+ root_statements_type& rsts (sts.root_statements ());
+
+ object_type& obj (static_cast<object_type&> (robj));
+
+ // The same code as in object_statements::load_delayed_().
+ //
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (sts, &id))
+ throw object_not_persistent ();
+
+ object_traits::callback (db, obj, callback_event::pre_load);
+ tc.init (obj, sts.image (), &db);
+ tc.load_ (sts, obj, false); // Load containers, etc.
+
+ rsts.load_delayed (svm);
+
+ {
+ typename root_statements_type::auto_unlock u (rsts);
+ object_traits::callback (db, obj, callback_event::post_load);
+ }
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/prepared-query.cxx b/libodb-mysql/odb/mysql/prepared-query.cxx
new file mode 100644
index 0000000..498d147
--- /dev/null
+++ b/libodb-mysql/odb/mysql/prepared-query.cxx
@@ -0,0 +1,15 @@
+// file : odb/mysql/prepared-query.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/prepared-query.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ prepared_query_impl::
+ ~prepared_query_impl ()
+ {
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/prepared-query.hxx b/libodb-mysql/odb/mysql/prepared-query.hxx
new file mode 100644
index 0000000..d892fec
--- /dev/null
+++ b/libodb-mysql/odb/mysql/prepared-query.hxx
@@ -0,0 +1,34 @@
+// file : odb/mysql/prepared-query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_PREPARED_QUERY_HXX
+#define ODB_MYSQL_PREPARED_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/prepared-query.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/query.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ struct LIBODB_MYSQL_EXPORT prepared_query_impl: odb::prepared_query_impl
+ {
+ virtual
+ ~prepared_query_impl ();
+
+ prepared_query_impl (odb::connection& c): odb::prepared_query_impl (c) {}
+
+ mysql::query_base query;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_PREPARED_QUERY_HXX
diff --git a/libodb-mysql/odb/mysql/query-const-expr.cxx b/libodb-mysql/odb/mysql/query-const-expr.cxx
new file mode 100644
index 0000000..4c1dd37
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query-const-expr.cxx
@@ -0,0 +1,14 @@
+// file : odb/mysql/query-const-expr.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/query.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ // Sun CC cannot handle this in query.cxx.
+ //
+ const query_base query_base::true_expr (true);
+ }
+}
diff --git a/libodb-mysql/odb/mysql/query-dynamic.cxx b/libodb-mysql/odb/mysql/query-dynamic.cxx
new file mode 100644
index 0000000..89041ca
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query-dynamic.cxx
@@ -0,0 +1,157 @@
+// file : odb/mysql/query-dynamic.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+
+#include <odb/mysql/query-dynamic.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ static const char* logic_operators[] = {") AND (", ") OR ("};
+ static const char* comp_operators[] = {"=", "!=", "<", ">", "<=", ">="};
+
+ static void
+ translate (query_base& q, const odb::query_base& s, size_t p)
+ {
+ typedef odb::query_base::clause_part part;
+
+ const part& x (s.clause ()[p]);
+
+ switch (x.kind)
+ {
+ case part::kind_column:
+ {
+ const query_column_base* c (
+ static_cast<const query_column_base*> (
+ x.native_info[id_mysql].column));
+
+ q.append (c->table (), c->column ());
+ break;
+ }
+ case part::kind_param_val:
+ case part::kind_param_ref:
+ {
+ const query_column_base* c (
+ static_cast<const query_column_base*> (
+ x.native_info[id_mysql].column));
+
+ query_param_factory f (
+ reinterpret_cast<query_param_factory> (
+ x.native_info[id_mysql].param_factory));
+
+ const odb::query_param* p (
+ reinterpret_cast<const odb::query_param*> (x.data));
+
+ q.append (f (p->value, x.kind == part::kind_param_ref),
+ c->conversion ());
+ break;
+ }
+ case part::kind_native:
+ {
+ q.append (s.strings ()[x.data]);
+ break;
+ }
+ case part::kind_true:
+ case part::kind_false:
+ {
+ q.append (x.kind == part::kind_true);
+ break;
+ }
+ case part::op_add:
+ {
+ translate (q, s, x.data);
+ translate (q, s, p - 1);
+ break;
+ }
+ case part::op_and:
+ case part::op_or:
+ {
+ q += "(";
+ translate (q, s, x.data);
+ q += logic_operators[x.kind - part::op_and];
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_not:
+ {
+ q += "NOT (";
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_null:
+ case part::op_not_null:
+ {
+ translate (q, s, p - 1);
+ q += (x.kind == part::op_null ? "IS NULL" : "IS NOT NULL");
+ break;
+ }
+ case part::op_in:
+ {
+ if (x.data != 0)
+ {
+ size_t b (p - x.data);
+
+ translate (q, s, b - 1); // column
+ q += "IN (";
+
+ for (size_t i (b); i != p; ++i)
+ {
+ if (i != b)
+ q += ",";
+
+ translate (q, s, i);
+ }
+
+ q += ")";
+ }
+ else
+ q.append (false);
+
+ break;
+ }
+ case part::op_like:
+ {
+ translate (q, s, p - 2); // column
+ q += "LIKE";
+ translate (q, s, p - 1); // pattern
+ break;
+ }
+ case part::op_like_escape:
+ {
+ translate (q, s, p - 3); // column
+ q += "LIKE";
+ translate (q, s, p - 2); // pattern
+ q += "ESCAPE";
+ translate (q, s, p - 1); // escape
+ break;
+ }
+ case part::op_eq:
+ case part::op_ne:
+ case part::op_lt:
+ case part::op_gt:
+ case part::op_le:
+ case part::op_ge:
+ {
+ translate (q, s, x.data);
+ q += comp_operators[x.kind - part::op_eq];
+ translate (q, s, p - 1);
+ break;
+ }
+ }
+ }
+
+ query_base::
+ query_base (const odb::query_base& q)
+ : binding_ (0, 0)
+ {
+ if (!q.empty ())
+ translate (*this, q, q.clause ().size () - 1);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/query-dynamic.hxx b/libodb-mysql/odb/mysql/query-dynamic.hxx
new file mode 100644
index 0000000..f6a571f
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query-dynamic.hxx
@@ -0,0 +1,32 @@
+// file : odb/mysql/query-dynamic.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_QUERY_DYNAMIC_HXX
+#define ODB_MYSQL_QUERY_DYNAMIC_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/query.hxx>
+#include <odb/query-dynamic.hxx>
+
+#include <odb/mysql/query.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ typedef details::shared_ptr<query_param> (*query_param_factory) (
+ const void* val, bool by_ref);
+
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void*, bool);
+ }
+}
+
+#include <odb/mysql/query-dynamic.ixx>
+#include <odb/mysql/query-dynamic.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_QUERY_DYNAMIC_HXX
diff --git a/libodb-mysql/odb/mysql/query-dynamic.ixx b/libodb-mysql/odb/mysql/query-dynamic.ixx
new file mode 100644
index 0000000..f9f3984
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query-dynamic.ixx
@@ -0,0 +1,26 @@
+// file : odb/mysql/query-dynamic.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ //
+ template <typename T, database_type_id ID>
+ inline query_column<T, ID>::
+ query_column (odb::query_column<T>& qc,
+ const char* table, const char* column, const char* conv)
+ : query_column_base (table, column, conv)
+ {
+ native_column_info& ci (qc.native_info[id_mysql]);
+ ci.column = static_cast<query_column_base*> (this);
+
+ // For some reason GCC needs this statically-typed pointer in
+ // order to instantiate the functions.
+ //
+ query_param_factory f (&query_param_factory_impl<T, ID>);
+ ci.param_factory = reinterpret_cast<void*> (f);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/query-dynamic.txx b/libodb-mysql/odb/mysql/query-dynamic.txx
new file mode 100644
index 0000000..52e653b
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query-dynamic.txx
@@ -0,0 +1,20 @@
+// file : odb/mysql/query-dynamic.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void* val, bool by_ref)
+ {
+ const T& v (*static_cast<const T*> (val));
+
+ return details::shared_ptr<query_param> (
+ by_ref
+ ? new (details::shared) query_param_impl<T, ID> (ref_bind<T> (v))
+ : new (details::shared) query_param_impl<T, ID> (val_bind<T> (v)));
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/query.cxx b/libodb-mysql/odb/mysql/query.cxx
new file mode 100644
index 0000000..a420495
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query.cxx
@@ -0,0 +1,346 @@
+// file : odb/mysql/query.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+#include <odb/mysql/query.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ // query_param
+ //
+ query_param::
+ ~query_param ()
+ {
+ }
+
+ // query_base
+ //
+ query_base::
+ query_base (const query_base& q)
+ : clause_ (q.clause_),
+ parameters_ (q.parameters_),
+ bind_ (q.bind_),
+ binding_ (0, 0)
+ {
+ // Here and below we want to maintain up to date binding info so
+ // that the call to parameters_binding() below is an immutable
+ // operation, provided the query does not have any by-reference
+ // parameters. This way a by-value-only query can be shared
+ // between multiple threads without the need for synchronization.
+ //
+ if (size_t n = bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = n;
+ binding_.version++;
+ }
+ }
+
+ query_base& query_base::
+ operator= (const query_base& q)
+ {
+ if (this != &q)
+ {
+ clause_ = q.clause_;
+ parameters_ = q.parameters_;
+ bind_ = q.bind_;
+
+ size_t n (bind_.size ());
+ binding_.bind = n != 0 ? &bind_[0] : 0;
+ binding_.count = n;
+ binding_.version++;
+ }
+
+ return *this;
+ }
+
+ query_base& query_base::
+ operator+= (const query_base& q)
+ {
+ clause_.insert (clause_.end (), q.clause_.begin (), q.clause_.end ());
+
+ size_t n (bind_.size ());
+
+ parameters_.insert (
+ parameters_.end (), q.parameters_.begin (), q.parameters_.end ());
+
+ bind_.insert (
+ bind_.end (), q.bind_.begin (), q.bind_.end ());
+
+ if (n != bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+ }
+
+ return *this;
+ }
+
+ void query_base::
+ append (const string& q)
+ {
+ if (!clause_.empty () &&
+ clause_.back ().kind == clause_part::kind_native)
+ {
+ string& s (clause_.back ().part);
+
+ char first (!q.empty () ? q[0] : ' ');
+ char last (!s.empty () ? s[s.size () - 1] : ' ');
+
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ s += ' ';
+
+ s += q;
+ }
+ else
+ clause_.push_back (clause_part (clause_part::kind_native, q));
+ }
+
+ void query_base::
+ append (const char* table, const char* column)
+ {
+ string s (table);
+ s += '.';
+ s += column;
+
+ clause_.push_back (clause_part (clause_part::kind_column, s));
+ }
+
+ void query_base::
+ append (details::shared_ptr<query_param> p, const char* conv)
+ {
+ clause_.push_back (clause_part (clause_part::kind_param));
+
+ if (conv != 0)
+ clause_.back ().part = conv;
+
+ parameters_.push_back (p);
+ bind_.push_back (MYSQL_BIND ());
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+
+ MYSQL_BIND* b (&bind_.back ());
+ memset (b, 0, sizeof (MYSQL_BIND));
+ p->bind (b);
+ }
+
+ void query_base::
+ init_parameters () const
+ {
+ bool inc_ver (false);
+
+ for (size_t i (0); i < parameters_.size (); ++i)
+ {
+ query_param& p (*parameters_[i]);
+
+ if (p.reference ())
+ {
+ if (p.init ())
+ {
+ p.bind (&bind_[i]);
+ inc_ver = true;
+ }
+ }
+ }
+
+ if (inc_ver)
+ binding_.version++;
+ }
+
+ static bool
+ check_prefix (const string& s)
+ {
+ string::size_type n;
+
+ // It is easier to compare to upper and lower-case versions
+ // rather than getting involved with the portable case-
+ // insensitive string comparison mess.
+ //
+ if (s.compare (0, (n = 5), "WHERE") == 0 ||
+ s.compare (0, (n = 5), "where") == 0 ||
+ s.compare (0, (n = 6), "SELECT") == 0 ||
+ s.compare (0, (n = 6), "select") == 0 ||
+ s.compare (0, (n = 8), "ORDER BY") == 0 ||
+ s.compare (0, (n = 8), "order by") == 0 ||
+ s.compare (0, (n = 8), "GROUP BY") == 0 ||
+ s.compare (0, (n = 8), "group by") == 0 ||
+ s.compare (0, (n = 6), "HAVING") == 0 ||
+ s.compare (0, (n = 6), "having") == 0 ||
+ s.compare (0, (n = 4), "CALL") == 0 ||
+ s.compare (0, (n = 4), "call") == 0)
+ {
+ // It either has to be an exact match, or there should be
+ // a whitespace following the keyword.
+ //
+ if (s.size () == n || s[n] == ' ' || s[n] == '\n' || s[n] =='\t')
+ return true;
+ }
+
+ return false;
+ }
+
+ void query_base::
+ optimize ()
+ {
+ // Remove a single TRUE literal or one that is followe by one of
+ // the other clauses. This avoids useless WHERE clauses like
+ //
+ // WHERE TRUE GROUP BY foo
+ //
+ clause_type::iterator i (clause_.begin ()), e (clause_.end ());
+
+ if (i != e && i->kind == clause_part::kind_bool && i->bool_part)
+ {
+ clause_type::iterator j (i + 1);
+
+ if (j == e ||
+ (j->kind == clause_part::kind_native && check_prefix (j->part)))
+ clause_.erase (i);
+ }
+ }
+
+ const char* query_base::
+ clause_prefix () const
+ {
+ if (!clause_.empty ())
+ {
+ const clause_part& p (clause_.front ());
+
+ if (p.kind == clause_part::kind_native && check_prefix (p.part))
+ return "";
+
+ return "WHERE ";
+ }
+
+ return "";
+ }
+
+ string query_base::
+ clause () const
+ {
+ string r;
+
+ for (clause_type::const_iterator i (clause_.begin ()),
+ end (clause_.end ());
+ i != end;
+ ++i)
+ {
+ char last (!r.empty () ? r[r.size () - 1] : ' ');
+
+ switch (i->kind)
+ {
+ case clause_part::kind_column:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ r += i->part;
+ break;
+ }
+ case clause_part::kind_param:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ // Add the conversion expression, if any.
+ //
+ string::size_type p (0);
+ if (!i->part.empty ())
+ {
+ p = i->part.find ("(?)");
+ r.append (i->part, 0, p);
+ }
+
+ r += '?';
+
+ if (!i->part.empty ())
+ r.append (i->part, p + 3, string::npos);
+
+ break;
+ }
+ case clause_part::kind_native:
+ {
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ const string& p (i->part);
+ char first (!p.empty () ? p[0] : ' ');
+
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ r += ' ';
+
+ r += p;
+ break;
+ }
+ case clause_part::kind_bool:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ r += i->bool_part ? "TRUE" : "FALSE";
+ break;
+ }
+ }
+ }
+
+ return clause_prefix () + r;
+ }
+
+ query_base
+ operator&& (const query_base& x, const query_base& y)
+ {
+ // Optimize cases where one or both sides are constant truth.
+ //
+ bool xt (x.const_true ()), yt (y.const_true ());
+
+ if (xt && yt)
+ return x;
+
+ if (xt)
+ return y;
+
+ if (yt)
+ return x;
+
+ query_base r ("(");
+ r += x;
+ r += ") AND (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator|| (const query_base& x, const query_base& y)
+ {
+ query_base r ("(");
+ r += x;
+ r += ") OR (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator! (const query_base& x)
+ {
+ query_base r ("NOT (");
+ r += x;
+ r += ")";
+ return r;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/query.hxx b/libodb-mysql/odb/mysql/query.hxx
new file mode 100644
index 0000000..d0972c9
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query.hxx
@@ -0,0 +1,2277 @@
+// file : odb/mysql/query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_QUERY_HXX
+#define ODB_MYSQL_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx> // odb::query_column
+#include <odb/query.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/traits.hxx>
+#include <odb/mysql/enum.hxx>
+#include <odb/mysql/binding.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/details/export.hxx>
+#include <odb/mysql/details/conversion.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ struct val_bind
+ {
+ typedef const T& type;
+
+ explicit
+ val_bind (type v): val (v) {}
+
+ type val;
+ };
+
+ template <typename T, std::size_t N>
+ struct val_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ val_bind (type v): val (v) {}
+
+ type val;
+ };
+
+ template <typename T>
+ struct ref_bind
+ {
+ typedef const T& type;
+
+ explicit
+ ref_bind (type r): ref (r) {}
+
+ const void*
+ ptr () const {return &ref;}
+
+ type ref;
+ };
+
+ template <typename T, std::size_t N>
+ struct ref_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ ref_bind (type r): ref (r) {}
+
+ // Allow implicit conversion from decayed ref_bind's.
+ //
+ ref_bind (ref_bind<T*> r): ref (r.ref) {}
+ ref_bind (ref_bind<const T*> r): ref (r.ref) {}
+
+ const void*
+ ptr () const {return ref;}
+
+ type ref;
+ };
+
+ template <typename T, database_type_id ID>
+ struct val_bind_typed: val_bind<T>
+ {
+ explicit
+ val_bind_typed (typename val_bind<T>::type v): val_bind<T> (v) {}
+ };
+
+ template <typename T, database_type_id ID>
+ struct ref_bind_typed: ref_bind<T>
+ {
+ explicit
+ ref_bind_typed (typename ref_bind<T>::type r): ref_bind<T> (r) {}
+ };
+
+ struct LIBODB_MYSQL_EXPORT query_param: details::shared_base
+ {
+ virtual
+ ~query_param ();
+
+ bool
+ reference () const
+ {
+ return value_ != 0;
+ }
+
+ virtual bool
+ init () = 0;
+
+ virtual void
+ bind (MYSQL_BIND*) = 0;
+
+ protected:
+ query_param (const void* value) : value_ (value) {}
+
+ protected:
+ const void* value_;
+ };
+
+ //
+ //
+ template <typename T, database_type_id ID>
+ struct query_column;
+
+ class LIBODB_MYSQL_EXPORT query_base
+ {
+ public:
+ struct clause_part
+ {
+ enum kind_type
+ {
+ kind_column,
+ kind_param,
+ kind_native,
+ kind_bool
+ };
+
+ clause_part (kind_type k): kind (k), bool_part (false) {}
+ clause_part (kind_type k, const std::string& p)
+ : kind (k), part (p), bool_part (false) {}
+ clause_part (bool p): kind (kind_bool), bool_part (p) {}
+
+ kind_type kind;
+ std::string part; // If kind is param, then part is conversion expr.
+ bool bool_part;
+ };
+
+ query_base ()
+ : binding_ (0, 0)
+ {
+ }
+
+ // True or false literal.
+ //
+ explicit
+ query_base (bool v)
+ : binding_ (0, 0)
+ {
+ append (v);
+ }
+
+ explicit
+ query_base (const char* native)
+ : binding_ (0, 0)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ explicit
+ query_base (const std::string& native)
+ : binding_ (0, 0)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ query_base (const char* table, const char* column)
+ : binding_ (0, 0)
+ {
+ append (table, column);
+ }
+
+ template <typename T>
+ explicit
+ query_base (val_bind<T> v)
+ : binding_ (0, 0)
+ {
+ *this += v;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (val_bind_typed<T, ID> v)
+ : binding_ (0, 0)
+ {
+ *this += v;
+ }
+
+ template <typename T>
+ explicit
+ query_base (ref_bind<T> r)
+ : binding_ (0, 0)
+ {
+ *this += r;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (ref_bind_typed<T, ID> r)
+ : binding_ (0, 0)
+ {
+ *this += r;
+ }
+
+ template <database_type_id ID>
+ query_base (const query_column<bool, ID>&);
+
+ // Translate common query representation to MySQL native. Defined
+ // in query-dynamic.cxx
+ //
+ query_base (const odb::query_base&);
+
+ // Copy c-tor and assignment.
+ //
+ query_base (const query_base&);
+
+ query_base&
+ operator= (const query_base&);
+
+ public:
+ std::string
+ clause () const;
+
+ const char*
+ clause_prefix () const;
+
+ // Initialize the by-reference parameters from bound variables.
+ //
+ void
+ init_parameters () const;
+
+ binding&
+ parameters_binding () const;
+
+ public:
+ bool
+ empty () const
+ {
+ return clause_.empty ();
+ }
+
+ static const query_base true_expr;
+
+ bool
+ const_true () const
+ {
+ return clause_.size () == 1 &&
+ clause_.front ().kind == clause_part::kind_bool &&
+ clause_.front ().bool_part;
+ }
+
+ void
+ optimize ();
+
+ public:
+ template <typename T>
+ static val_bind<T>
+ _val (const T& x)
+ {
+ return val_bind<T> (x);
+ }
+
+ template <database_type_id ID, typename T>
+ static val_bind_typed<T, ID>
+ _val (const T& x)
+ {
+ return val_bind_typed<T, ID> (x);
+ }
+
+ template <typename T>
+ static ref_bind<T>
+ _ref (const T& x)
+ {
+ return ref_bind<T> (x);
+ }
+
+ template <database_type_id ID, typename T>
+ static ref_bind_typed<T, ID>
+ _ref (const T& x)
+ {
+ return ref_bind_typed<T, ID> (x);
+ }
+
+ // Some compilers (notably VC++), when deducing const T& from const
+ // array do not strip const from the array type. As a result, in the
+ // above signatures we get, for example, T = const char[4] instead
+ // of T = char[4], which is what we want. So to "fix" such compilers,
+ // we will have to provide the following specializations of the above
+ // functions.
+ //
+ template <typename T, std::size_t N>
+ static val_bind<T[N]>
+ _val (const T (&x) [N])
+ {
+ return val_bind<T[N]> (x);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static val_bind_typed<T[N], ID>
+ _val (const T (&x) [N])
+ {
+ return val_bind_typed<T[N], ID> (x);
+ }
+
+ template <typename T, std::size_t N>
+ static ref_bind<T[N]>
+ _ref (const T (&x) [N])
+ {
+ return ref_bind<T[N]> (x);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static ref_bind_typed<T[N], ID>
+ _ref (const T (&x) [N])
+ {
+ return ref_bind_typed<T[N], ID> (x);
+ }
+
+ public:
+ query_base&
+ operator+= (const query_base&);
+
+ query_base&
+ operator+= (const std::string& q)
+ {
+ append (q);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (val_bind<T> v)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ v, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (val_bind_typed<T, ID> v)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (v, 0);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (ref_bind<T> r)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ r, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (ref_bind_typed<T, ID> r)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (r, 0);
+ return *this;
+ }
+
+ // Implementation details.
+ //
+ public:
+ template <typename T, database_type_id ID>
+ void
+ append (val_bind<T>, const char* conv);
+
+ template <typename T, database_type_id ID>
+ void
+ append (ref_bind<T>, const char* conv);
+
+ void
+ append (details::shared_ptr<query_param>, const char* conv);
+
+ void
+ append (bool v)
+ {
+ clause_.push_back (clause_part (v));
+ }
+
+ void
+ append (const std::string& native);
+
+ void
+ append (const char* native) // Clashes with append(bool).
+ {
+ append (std::string (native));
+ }
+
+ void
+ append (const char* table, const char* column);
+
+ private:
+ typedef std::vector<clause_part> clause_type;
+ typedef std::vector<details::shared_ptr<query_param> > parameters_type;
+
+ clause_type clause_;
+ parameters_type parameters_;
+ mutable std::vector<MYSQL_BIND> bind_;
+ mutable binding binding_;
+ };
+
+ inline query_base
+ operator+ (const query_base& x, const query_base& y)
+ {
+ query_base r (x);
+ r += y;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, val_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, val_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, ref_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, ref_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const query_base& q, const std::string& s)
+ {
+ query_base r (q);
+ r += s;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const std::string& s, const query_base& q)
+ {
+ query_base r (s);
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, val_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, val_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, ref_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, ref_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ LIBODB_MYSQL_EXPORT query_base
+ operator&& (const query_base& x, const query_base& y);
+
+ LIBODB_MYSQL_EXPORT query_base
+ operator|| (const query_base& x, const query_base& y);
+
+ LIBODB_MYSQL_EXPORT query_base
+ operator! (const query_base& x);
+
+ // query_column
+ //
+ struct LIBODB_MYSQL_EXPORT query_column_base
+ {
+ // Note that we keep shallow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column_base (const char* table,
+ const char* column,
+ const char* conv)
+ : table_ (table), column_ (column), conversion_ (conv)
+ {
+ }
+
+ const char*
+ table () const
+ {
+ return table_;
+ }
+
+ const char*
+ column () const
+ {
+ return column_;
+ }
+
+ // Can be NULL.
+ //
+ const char*
+ conversion () const
+ {
+ return conversion_;
+ }
+
+ protected:
+ const char* table_;
+ const char* column_;
+ const char* conversion_;
+ };
+
+ template <typename T, database_type_id ID>
+ struct query_column: query_column_base
+ {
+ typedef typename decay_traits<T>::type decayed_type;
+
+ // Note that we keep shallow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column (const char* table, const char* column, const char* conv)
+ : query_column_base (table, column, conv) {}
+
+ // Implementation is in query-dynamic.ixx.
+ //
+ query_column (odb::query_column<T>&,
+ const char* table, const char* column, const char* conv);
+
+ // is_null, is_not_null
+ //
+ public:
+ query_base
+ is_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NULL";
+ return q;
+ }
+
+ query_base
+ is_not_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NOT NULL";
+ return q;
+ }
+
+ // in
+ //
+ public:
+ query_base
+ in (decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type,
+ decayed_type) const;
+
+ template <typename I>
+ query_base
+ in_range (I begin, I end) const;
+
+ // like
+ //
+ public:
+ query_base
+ like (decayed_type pattern) const
+ {
+ return like (val_bind<T> (pattern));
+ }
+
+ query_base
+ like (val_bind<T> pattern) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)));
+ }
+
+ query_base
+ like (ref_bind<T> pattern) const;
+
+ query_base
+ like (decayed_type pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (pattern), escape);
+ }
+
+ query_base
+ like (val_bind<T> pattern, decayed_type escape) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)), escape);
+ }
+
+ query_base
+ like (ref_bind<T> pattern, decayed_type escape) const;
+
+ // =
+ //
+ public:
+ query_base
+ equal (decayed_type v) const
+ {
+ return equal (val_bind<T> (v));
+ }
+
+ query_base
+ equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ equal (val_bind<T2> v) const
+ {
+ return equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator== (const query_column& c, decayed_type v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (decayed_type v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, val_bind<T> v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (val_bind<T> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (const query_column& c, val_bind<T2> v)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (val_bind<T2> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, ref_bind<T> r)
+ {
+ return c.equal (r);
+ }
+
+ friend query_base
+ operator== (ref_bind<T> r, const query_column& c)
+ {
+ return c.equal (r);
+ }
+
+ // !=
+ //
+ public:
+ query_base
+ unequal (decayed_type v) const
+ {
+ return unequal (val_bind<T> (v));
+ }
+
+ query_base
+ unequal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ unequal (val_bind<T2> v) const
+ {
+ return unequal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ unequal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator!= (const query_column& c, decayed_type v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (decayed_type v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, val_bind<T> v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (val_bind<T> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (const query_column& c, val_bind<T2> v)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (val_bind<T2> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, ref_bind<T> r)
+ {
+ return c.unequal (r);
+ }
+
+ friend query_base
+ operator!= (ref_bind<T> r, const query_column& c)
+ {
+ return c.unequal (r);
+ }
+
+ // <
+ //
+ public:
+ query_base
+ less (decayed_type v) const
+ {
+ return less (val_bind<T> (v));
+ }
+
+ query_base
+ less (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less (val_bind<T2> v) const
+ {
+ return less (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator< (const query_column& c, decayed_type v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (decayed_type v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, val_bind<T> v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (val_bind<T> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (const query_column& c, val_bind<T2> v)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, ref_bind<T> r)
+ {
+ return c.less (r);
+ }
+
+ friend query_base
+ operator< (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater (r);
+ }
+
+ // >
+ //
+ public:
+ query_base
+ greater (decayed_type v) const
+ {
+ return greater (val_bind<T> (v));
+ }
+
+ query_base
+ greater (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater (val_bind<T2> v) const
+ {
+ return greater (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator> (const query_column& c, decayed_type v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (decayed_type v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, val_bind<T> v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (val_bind<T> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (val_bind<T2> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater (r);
+ }
+
+ friend query_base
+ operator> (ref_bind<T> r, const query_column& c)
+ {
+ return c.less (r);
+ }
+
+ // <=
+ //
+ public:
+ query_base
+ less_equal (decayed_type v) const
+ {
+ return less_equal (val_bind<T> (v));
+ }
+
+ query_base
+ less_equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less_equal (val_bind<T2> v) const
+ {
+ return less_equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less_equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator<= (const query_column& c, decayed_type v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (decayed_type v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, val_bind<T> v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (val_bind<T> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (const query_column& c, val_bind<T2> v)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, ref_bind<T> r)
+ {
+ return c.less_equal (r);
+ }
+
+ friend query_base
+ operator<= (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater_equal (r);
+ }
+
+ // >=
+ //
+ public:
+ query_base
+ greater_equal (decayed_type v) const
+ {
+ return greater_equal (val_bind<T> (v));
+ }
+
+ query_base
+ greater_equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater_equal (val_bind<T2> v) const
+ {
+ return greater_equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater_equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator>= (const query_column& c, decayed_type v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (decayed_type v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, val_bind<T> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (val_bind<T> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (val_bind<T2> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater_equal (r);
+ }
+
+ friend query_base
+ operator>= (ref_bind<T> r, const query_column& c)
+ {
+ return c.less_equal (r);
+ }
+
+ // Column comparison.
+ //
+ public:
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator== (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () ==
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator!= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () !=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "!=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator< (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator> (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator<= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator>= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+ };
+
+ // Provide operator+() for using columns to construct native
+ // query fragments (e.g., ORDER BY).
+ //
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const std::string& s)
+ {
+ query_base q (c.table (), c.column ());
+ q += s;
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, const query_column<T, ID>& c)
+ {
+ query_base q (s);
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const query_base& q)
+ {
+ query_base r (c.table (), c.column ());
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, const query_column<T, ID>& c)
+ {
+ query_base r (q);
+ r.append (c.table (), c.column ());
+ return r;
+ }
+
+ //
+ //
+ template <typename T, database_type_id>
+ struct query_param_impl;
+
+ // TINY
+ //
+ template <typename T>
+ struct query_param_impl<T, id_tiny>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_TINY;
+ b->is_unsigned = false;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_tiny>::set_image (image_, is_null, v);
+ }
+
+ private:
+ signed char image_;
+ };
+
+ template <typename T>
+ struct query_param_impl<T, id_utiny>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_TINY;
+ b->is_unsigned = true;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_utiny>::set_image (image_, is_null, v);
+ }
+
+ private:
+ unsigned char image_;
+ };
+
+ // SHORT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_short>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_SHORT;
+ b->is_unsigned = false;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_short>::set_image (image_, is_null, v);
+ }
+
+ private:
+ short image_;
+ };
+
+ template <typename T>
+ struct query_param_impl<T, id_ushort>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_SHORT;
+ b->is_unsigned = true;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_ushort>::set_image (image_, is_null, v);
+ }
+
+ private:
+ unsigned short image_;
+ };
+
+ // LONG
+ //
+ template <typename T>
+ struct query_param_impl<T, id_long>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_LONG;
+ b->is_unsigned = false;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_long>::set_image (image_, is_null, v);
+ }
+
+ private:
+ int image_;
+ };
+
+ template <typename T>
+ struct query_param_impl<T, id_ulong>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_LONG;
+ b->is_unsigned = true;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_ulong>::set_image (image_, is_null, v);
+ }
+
+ private:
+ unsigned int image_;
+ };
+
+ // LONGLONG
+ //
+ template <typename T>
+ struct query_param_impl<T, id_longlong>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_LONGLONG;
+ b->is_unsigned = false;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_longlong>::set_image (image_, is_null, v);
+ }
+
+ private:
+ long long image_;
+ };
+
+ template <typename T>
+ struct query_param_impl<T, id_ulonglong>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_LONGLONG;
+ b->is_unsigned = true;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_ulonglong>::set_image (image_, is_null, v);
+ }
+
+ private:
+ unsigned long long image_;
+ };
+
+ // FLOAT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_float>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_FLOAT;
+ b->is_unsigned = false;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_float>::set_image (image_, is_null, v);
+ }
+
+ private:
+ float image_;
+ };
+
+ // DOUBLE
+ //
+ template <typename T>
+ struct query_param_impl<T, id_double>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_DOUBLE;
+ b->is_unsigned = false;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_double>::set_image (image_, is_null, v);
+ }
+
+ private:
+ double image_;
+ };
+
+ // DECIMAL
+ //
+ template <typename T>
+ struct query_param_impl<T, id_decimal>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_NEWDECIMAL;
+ b->buffer = buffer_.data ();
+ b->buffer_length = static_cast<unsigned long> (buffer_.capacity ());
+ b->length = &size_;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_decimal>::set_image (buffer_, size, is_null, v);
+ size_ = static_cast<unsigned long> (size);
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ unsigned long size_;
+ };
+
+ // DATE
+ //
+ template <typename T>
+ struct query_param_impl<T, id_date>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_DATE;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_date>::set_image (image_, is_null, v);
+ }
+
+ private:
+ MYSQL_TIME image_;
+ };
+
+ // TIME
+ //
+ template <typename T>
+ struct query_param_impl<T, id_time>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_TIME;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_time>::set_image (image_, is_null, v);
+ }
+
+ private:
+ MYSQL_TIME image_;
+ };
+
+ // DATETIME
+ //
+ template <typename T>
+ struct query_param_impl<T, id_datetime>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_DATETIME;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_datetime>::set_image (image_, is_null, v);
+ }
+
+ private:
+ MYSQL_TIME image_;
+ };
+
+ // TIMESTAMP
+ //
+ template <typename T>
+ struct query_param_impl<T, id_timestamp>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_TIMESTAMP;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_timestamp>::set_image (image_, is_null, v);
+ }
+
+ private:
+ MYSQL_TIME image_;
+ };
+
+ // YEAR
+ //
+ template <typename T>
+ struct query_param_impl<T, id_year>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_SHORT;
+ b->is_unsigned = false;
+ b->buffer = &image_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_year>::set_image (image_, is_null, v);
+ }
+
+ private:
+ short image_;
+ };
+
+ // STRING
+ //
+ template <typename T>
+ struct query_param_impl<T, id_string>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_STRING;
+ b->buffer = buffer_.data ();
+ b->buffer_length = static_cast<unsigned long> (buffer_.capacity ());
+ b->length = &size_;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_string>::set_image (buffer_, size, is_null, v);
+ size_ = static_cast<unsigned long> (size);
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ unsigned long size_;
+ };
+
+ // BLOB
+ //
+ template <typename T>
+ struct query_param_impl<T, id_blob>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_BLOB;
+ b->buffer = buffer_.data ();
+ b->buffer_length = static_cast<unsigned long> (buffer_.capacity ());
+ b->length = &size_;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_blob>::set_image (buffer_, size, is_null, v);
+ size_ = static_cast<unsigned long> (size);
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ unsigned long size_;
+ };
+
+ // BIT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_bit>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_BLOB;
+ b->buffer = buffer_;
+ b->buffer_length = static_cast<unsigned long> (sizeof (buffer_));
+ b->length = &size_;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0);
+ value_traits<T, id_bit>::set_image (
+ buffer_, sizeof (buffer_), size, is_null, v);
+ size_ = static_cast<unsigned long> (size);
+ }
+
+ private:
+ // Max 64 bit.
+ //
+ unsigned char buffer_[8];
+ unsigned long size_;
+ };
+
+ // ENUM
+ //
+ // The image type can be either integer or string.
+ //
+ template <typename T>
+ struct query_param_impl<T, id_enum>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ enum_traits::bind (*b, image_, size_, 0);
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ return enum_traits::set_image (image_, size_, is_null, v);
+ }
+
+ private:
+ typename value_traits<T, id_enum>::image_type image_;
+ unsigned long size_; // Keep size in case it is a string.
+ };
+
+ // SET
+ //
+ template <typename T>
+ struct query_param_impl<T, id_set>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (MYSQL_BIND* b)
+ {
+ b->buffer_type = MYSQL_TYPE_STRING;
+ b->buffer = buffer_.data ();
+ b->buffer_length = static_cast<unsigned long> (buffer_.capacity ());
+ b->length = &size_;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_set>::set_image (buffer_, size, is_null, v);
+ size_ = static_cast<unsigned long> (size);
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ unsigned long size_;
+ };
+ }
+}
+
+// odb::mysql::query and odb::query specialization for MySQL.
+//
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ class query: public query_base,
+ public query_selector<T, id_mysql>::columns_type
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : query_base (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : query_base (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : query_base (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (val_bind<T2> v)
+ : query_base (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (ref_bind<T2> r)
+ : query_base (r)
+ {
+ }
+
+ query (const query_base& q)
+ : query_base (q)
+ {
+ }
+
+ template <database_type_id ID>
+ query (const query_column<bool, ID>& qc)
+ : query_base (qc)
+ {
+ }
+
+ query (const odb::query_base& q)
+ : query_base (q)
+ {
+ }
+ };
+
+ namespace core
+ {
+ using mysql::query;
+ }
+ }
+
+ // Derive odb::query from odb::mysql::query so that it can be
+ // implicitly converted in mysql::database::query() calls.
+ //
+ template <typename T>
+ class query<T, mysql::query_base>: public mysql::query<T>
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : mysql::query<T> (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : mysql::query<T> (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : mysql::query<T> (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (mysql::val_bind<T2> v)
+ : mysql::query<T> (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (mysql::ref_bind<T2> r)
+ : mysql::query<T> (r)
+ {
+ }
+
+ query (const mysql::query_base& q)
+ : mysql::query<T> (q)
+ {
+ }
+
+ template <mysql::database_type_id ID>
+ query (const mysql::query_column<bool, ID>& qc)
+ : mysql::query<T> (qc)
+ {
+ }
+ };
+}
+
+#include <odb/mysql/query.ixx>
+#include <odb/mysql/query.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_QUERY_HXX
diff --git a/libodb-mysql/odb/mysql/query.ixx b/libodb-mysql/odb/mysql/query.ixx
new file mode 100644
index 0000000..ea1ec03
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query.ixx
@@ -0,0 +1,34 @@
+// file : odb/mysql/query.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mysql
+ {
+ inline binding& query_base::
+ parameters_binding () const
+ {
+ return binding_;
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (val_bind<T> v, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (v)),
+ conv);
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (ref_bind<T> r, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (r)),
+ conv);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/query.txx b/libodb-mysql/odb/mysql/query.txx
new file mode 100644
index 0000000..e09ed85
--- /dev/null
+++ b/libodb-mysql/odb/mysql/query.txx
@@ -0,0 +1,168 @@
+// file : odb/mysql/query.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // query_base
+ //
+
+ template <database_type_id ID>
+ query_base::
+ query_base (const query_column<bool, ID>& c)
+ : binding_ (0, 0)
+ {
+ // Cannot use IS TRUE here since database type can be a non-
+ // integral type.
+ //
+ append (c.table (), c.column ());
+ append ("=");
+ append<bool, ID> (val_bind<bool> (true), c.conversion ());
+ }
+
+ //
+ // query_column
+ //
+
+ // in
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3,
+ decayed_type v4) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3, decayed_type v4,
+ decayed_type v5) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v5), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ template <typename I>
+ query_base query_column<T, ID>::
+ in_range (I begin, I end) const
+ {
+ if (begin != end)
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+
+ for (I i (begin); i != end; ++i)
+ {
+ if (i != begin)
+ q += ",";
+
+ q.append<T, ID> (val_bind<T> (*i), conversion_);
+ }
+
+ q += ")";
+ return q;
+ }
+ else
+ return query_base (false);
+ }
+
+ // like
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/section-statements.hxx b/libodb-mysql/odb/mysql/section-statements.hxx
new file mode 100644
index 0000000..a88683f
--- /dev/null
+++ b/libodb-mysql/odb/mysql/section-statements.hxx
@@ -0,0 +1,200 @@
+// file : odb/mysql/section-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_SECTION_STATEMENTS_HXX
+#define ODB_MYSQL_SECTION_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/binding.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class connection;
+
+ // Template argument is the section traits type.
+ //
+ template <typename T, typename ST>
+ class section_statements
+ {
+ public:
+ typedef ST traits;
+
+ typedef typename traits::image_type image_type;
+ typedef typename traits::id_image_type id_image_type;
+
+ typedef mysql::select_statement select_statement_type;
+ typedef mysql::update_statement update_statement_type;
+
+ typedef mysql::connection connection_type;
+
+ section_statements (connection_type&,
+ image_type&, id_image_type&,
+ binding& id, binding& idv);
+
+ connection_type&
+ connection () {return conn_;}
+
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ image_type&
+ image () {return image_;}
+
+ const binding&
+ id_binding () {return id_binding_;}
+
+ // Id and optimistic concurrency version (if any).
+ //
+ const binding&
+ idv_binding () {return idv_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ my_bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ //
+ // Statements.
+ //
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ traits::select_statement,
+ traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_binding_,
+ select_image_binding_,
+ false));
+
+ return *select_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ traits::update_statement,
+ traits::versioned, // Process if versioned.
+ update_image_binding_,
+ false));
+
+ return *update_;
+ }
+
+ public:
+ static const std::size_t id_column_count = traits::id_column_count;
+ static const std::size_t managed_optimistic_load_column_count =
+ traits::managed_optimistic_load_column_count;
+ static const std::size_t managed_optimistic_update_column_count =
+ traits::managed_optimistic_update_column_count;
+ static const std::size_t select_column_count = traits::load_column_count;
+ static const std::size_t update_column_count =
+ traits::update_column_count;
+
+ private:
+ section_statements (const section_statements&);
+ section_statements& operator= (const section_statements&);
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+
+ // These come from object_statements.
+ //
+ image_type& image_;
+ binding& id_binding_;
+ binding& idv_binding_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+
+ static const std::size_t select_bind_count =
+ select_column_count != 0 || managed_optimistic_load_column_count != 0
+ ? select_column_count + managed_optimistic_load_column_count
+ : 1;
+
+ binding select_image_binding_;
+ MYSQL_BIND select_image_bind_[select_bind_count];
+ my_bool select_image_truncated_[select_bind_count];
+
+ // Update binding.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+
+ static const std::size_t update_bind_count =
+ update_column_count != 0 || managed_optimistic_update_column_count != 0
+ ? update_column_count + id_column_count +
+ managed_optimistic_update_column_count
+ : 1;
+
+ binding update_image_binding_;
+ MYSQL_BIND update_image_bind_[update_bind_count];
+
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<update_statement_type> update_;
+ };
+ }
+}
+
+#include <odb/mysql/section-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_SECTION_STATEMENTS_HXX
diff --git a/libodb-mysql/odb/mysql/section-statements.txx b/libodb-mysql/odb/mysql/section-statements.txx
new file mode 100644
index 0000000..5c0672e
--- /dev/null
+++ b/libodb-mysql/odb/mysql/section-statements.txx
@@ -0,0 +1,40 @@
+// file : odb/mysql/section-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T, typename ST>
+ section_statements<T, ST>::
+ section_statements (connection_type& conn,
+ image_type& im, id_image_type&,
+ binding& id, binding& idv)
+ : conn_ (conn),
+ svm_ (0),
+ image_ (im),
+ id_binding_ (id),
+ idv_binding_ (idv),
+ select_image_binding_ (select_image_bind_,
+ select_column_count +
+ managed_optimistic_load_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_update_column_count)
+ {
+ select_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+ for (std::size_t i (0); i < select_bind_count; ++i)
+ select_image_bind_[i].error = select_image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/simple-object-result.hxx b/libodb-mysql/odb/mysql/simple-object-result.hxx
new file mode 100644
index 0000000..ae0848c
--- /dev/null
+++ b/libodb-mysql/odb/mysql/simple-object-result.hxx
@@ -0,0 +1,85 @@
+// file : odb/mysql/simple-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_SIMPLE_OBJECT_RESULT_HXX
+#define ODB_MYSQL_SIMPLE_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/simple-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx> // query_base
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ class object_result_impl: public odb::object_result_impl<T>
+ {
+ public:
+ typedef odb::object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_mysql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~object_result_impl ();
+
+ object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ fetch (bool next = true);
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/mysql/simple-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_SIMPLE_OBJECT_RESULT_HXX
diff --git a/libodb-mysql/odb/mysql/simple-object-result.txx b/libodb-mysql/odb/mysql/simple-object-result.txx
new file mode 100644
index 0000000..5c6e198
--- /dev/null
+++ b/libodb-mysql/odb/mysql/simple-object-result.txx
@@ -0,0 +1,235 @@
+// file : odb/mysql/simple-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // result_not_cached
+
+#include <odb/mysql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ object_result_impl<T>::
+ ~object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ object_result_impl<T>::
+ object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ load (object_type& obj, bool f)
+ {
+ if (count_ > statement_->fetched ())
+ fetch ();
+ else if (f && statement_->cached ())
+ {
+ // We have to re-load the image in case it has been overwritten
+ // between the last time we fetched and this call to load().
+ //
+ fetch (false);
+ }
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!statements_.locked ());
+ typename statements_type::auto_lock l (statements_);
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+
+ typename object_traits::image_type& i (statements_.image ());
+ tc_.init (obj, i, &this->db_);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ object_traits::init (idi, object_traits::id (i));
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, obj, false);
+ statements_.load_delayed (tc_.version ());
+ l.unlock ();
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ typename object_result_impl<T>::id_type
+ object_result_impl<T>::
+ load_id ()
+ {
+ if (count_ > statement_->fetched ())
+ fetch ();
+ else if (statement_->cached ())
+ {
+ // We have to re-load the image in case it has been overwritten
+ // between the last time we fetched and this call to load_id().
+ //
+ fetch (false);
+ }
+
+ return object_traits::id (statements_.image ());
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (this->end_)
+ return;
+
+ // If we are cached, simply increment the position and
+ // postpone the actual row fetching until later. This way
+ // if the same object is loaded in between iteration, the
+ // image won't be messed up.
+ //
+ count_++;
+
+ if (statement_->cached ())
+ this->end_ = count_ > statement_->result_size ();
+ else
+ fetch ();
+
+ if (this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ fetch (bool next)
+ {
+ // If the result is cached, the image can grow between calls
+ // to fetch() as a result of other statements execution.
+ //
+ if (statement_->cached ())
+ {
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+ }
+
+ while (!this->end_ && (!next || count_ > statement_->fetched ()))
+ {
+ select_statement::result r (statement_->fetch (next));
+
+ switch (r)
+ {
+ case select_statement::truncated:
+ {
+ // Don't re-fetch data we are skipping.
+ //
+ if (next && count_ != statement_->fetched ())
+ continue;
+
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ statement_->refetch ();
+ }
+ // Fall throught.
+ }
+ case select_statement::success:
+ {
+ break;
+ }
+ case select_statement::no_data:
+ {
+ this->end_ = true;
+ break;
+ }
+ }
+
+ // If we are refetching the current row, then we are done.
+ //
+ if (!next)
+ break;
+ }
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ cache ()
+ {
+ if (!this->end_ && !statement_->cached ())
+ {
+ statement_->cache ();
+
+ if (count_ == statement_->result_size ())
+ {
+ statement_->free_result ();
+ count_++; // One past the result size.
+ this->end_ = true;
+ }
+ }
+ }
+
+ template <typename T>
+ std::size_t object_result_impl<T>::
+ size ()
+ {
+ if (!this->end_)
+ {
+ if (!statement_->cached ())
+ throw result_not_cached ();
+
+ return statement_->result_size ();
+ }
+ else
+ return count_ - 1; // One past the result size.
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/simple-object-statements.cxx b/libodb-mysql/odb/mysql/simple-object-statements.cxx
new file mode 100644
index 0000000..af0df6b
--- /dev/null
+++ b/libodb-mysql/odb/mysql/simple-object-statements.cxx
@@ -0,0 +1,15 @@
+// file : odb/mysql/simple-object-statements.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ object_statements_base::
+ ~object_statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/simple-object-statements.hxx b/libodb-mysql/odb/mysql/simple-object-statements.hxx
new file mode 100644
index 0000000..5292b66
--- /dev/null
+++ b/libodb-mysql/odb/mysql/simple-object-statements.hxx
@@ -0,0 +1,587 @@
+// file : odb/mysql/simple-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_SIMPLE_OBJECT_STATEMENTS_HXX
+#define ODB_MYSQL_SIMPLE_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cassert>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/binding.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/statements-base.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ // The extra_statement_cache class is only defined (and used) in
+ // the generated source file. However, object_statements may be
+ // referenced from another source file in the case of a polymorphic
+ // hierarchy (though in this case the extra statement cache is
+ // not used). As a result, we cannot have a by-value member and
+ // instead will store a pointer and lazily allocate the cache if
+ // and when needed. We will also need to store a pointer to the
+ // deleter function which will be initialized during allocation
+ // (at that point we know that the cache class is defined).
+ //
+ template <typename T, typename I, typename ID>
+ struct extra_statement_cache_ptr
+ {
+ typedef I image_type;
+ typedef ID id_image_type;
+ typedef mysql::connection connection_type;
+
+ extra_statement_cache_ptr (): p_ (0) {}
+ ~extra_statement_cache_ptr ()
+ {
+ if (p_ != 0)
+ (this->*deleter_) (0, 0, 0, 0, 0);
+ }
+
+ T&
+ get (connection_type& c,
+ image_type& im, id_image_type& idim,
+ binding& id, binding* idv)
+ {
+ if (p_ == 0)
+ allocate (&c, &im, &idim, &id, (idv != 0 ? idv : &id));
+
+ return *p_;
+ }
+
+ private:
+ void
+ allocate (connection_type*,
+ image_type*, id_image_type*,
+ binding*, binding*);
+
+ private:
+ T* p_;
+ void (extra_statement_cache_ptr::*deleter_) (
+ connection_type*, image_type*, id_image_type*, binding*, binding*);
+ };
+
+ template <typename T, typename I, typename ID>
+ void extra_statement_cache_ptr<T, I, ID>::
+ allocate (connection_type* c,
+ image_type* im, id_image_type* idim,
+ binding* id, binding* idv)
+ {
+ // To reduce object code size, this function acts as both allocator
+ // and deleter.
+ //
+ if (p_ == 0)
+ {
+ p_ = new T (*c, *im, *idim, *id, *idv);
+ deleter_ = &extra_statement_cache_ptr<T, I, ID>::allocate;
+ }
+ else
+ delete p_;
+ }
+
+ //
+ // Implementation for objects with object id.
+ //
+
+ class LIBODB_MYSQL_EXPORT object_statements_base: public statements_base
+ {
+ // Locking.
+ //
+ public:
+ void
+ lock ()
+ {
+ assert (!locked_);
+ locked_ = true;
+ }
+
+ void
+ unlock ()
+ {
+ assert (locked_);
+ locked_ = false;
+ }
+
+ bool
+ locked () const
+ {
+ return locked_;
+ }
+
+ struct auto_unlock
+ {
+ // Unlocks the statement on construction and re-locks it on
+ // destruction.
+ //
+ auto_unlock (object_statements_base&);
+ ~auto_unlock ();
+
+ private:
+ auto_unlock (const auto_unlock&);
+ auto_unlock& operator= (const auto_unlock&);
+
+ private:
+ object_statements_base& s_;
+ };
+
+ public:
+ virtual
+ ~object_statements_base ();
+
+ protected:
+ object_statements_base (connection_type& conn)
+ : statements_base (conn), locked_ (false)
+ {
+ }
+
+ protected:
+ bool locked_;
+ };
+
+ template <typename T, bool optimistic>
+ struct optimistic_data;
+
+ template <typename T>
+ struct optimistic_data<T, true>
+ {
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mysql> object_traits;
+
+ optimistic_data (MYSQL_BIND*);
+
+ binding*
+ id_image_binding () {return &id_image_binding_;}
+
+ // The id + optimistic column binding.
+ //
+ binding id_image_binding_;
+
+ details::shared_ptr<delete_statement> erase_;
+ };
+
+ template <typename T>
+ struct optimistic_data<T, false>
+ {
+ optimistic_data (MYSQL_BIND*) {}
+
+ binding*
+ id_image_binding () {return 0;}
+ };
+
+ template <typename T>
+ class object_statements: public object_statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_mysql> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+ typedef typename object_traits::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::pointer_cache_traits
+ pointer_cache_traits;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef mysql::insert_statement insert_statement_type;
+ typedef mysql::select_statement select_statement_type;
+ typedef mysql::update_statement update_statement_type;
+ typedef mysql::delete_statement delete_statement_type;
+
+ // Automatic lock.
+ //
+ struct auto_lock
+ {
+ // Lock the statements unless they are already locked in which
+ // case subsequent calls to locked() will return false.
+ //
+ auto_lock (object_statements&);
+
+ // Unlock the statemens if we are holding the lock and clear
+ // the delayed loads. This should only happen in case an
+ // exception is thrown. In normal circumstances, the user
+ // should call unlock() explicitly.
+ //
+ ~auto_lock ();
+
+ // Return true if this auto_lock instance holds the lock.
+ //
+ bool
+ locked () const;
+
+ // Unlock the statemens.
+ //
+ void
+ unlock ();
+
+ private:
+ auto_lock (const auto_lock&);
+ auto_lock& operator= (const auto_lock&);
+
+ private:
+ object_statements& s_;
+ bool locked_;
+ };
+
+ public:
+ object_statements (connection_type&);
+
+ virtual
+ ~object_statements ();
+
+ // Delayed loading.
+ //
+ typedef void (*loader_function) (odb::database&,
+ const id_type&,
+ object_type&,
+ const schema_version_migration*);
+
+ void
+ delay_load (const id_type& id,
+ object_type& obj,
+ const typename pointer_cache_traits::position_type& p,
+ loader_function l = 0)
+ {
+ delayed_.push_back (delayed_load (id, obj, p, l));
+ }
+
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (locked ());
+
+ if (!delayed_.empty ())
+ load_delayed_<object_statements> (svm);
+ }
+
+ void
+ clear_delayed ()
+ {
+ if (!delayed_.empty ())
+ clear_delayed_ ();
+ }
+
+ // Object image.
+ //
+ image_type&
+ image () {return image_;}
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_image_version () const { return update_id_image_version_;}
+
+ void
+ update_id_image_version (std::size_t v) {update_id_image_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ my_bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Object id image and binding.
+ //
+ id_image_type&
+ id_image () {return id_image_;}
+
+ std::size_t
+ id_image_version () const {return id_image_version_;}
+
+ void
+ id_image_version (std::size_t v) {id_image_version_ = v;}
+
+ binding&
+ id_image_binding () {return id_image_binding_;}
+
+ // Optimistic id + managed column image binding. It points to
+ // the same suffix as id binding and they are always updated
+ // at the same time.
+ //
+ binding&
+ optimistic_id_image_binding () {return od_.id_image_binding_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ insert_image_binding_,
+ (object_traits::auto_id ? &id_image_binding_ : 0),
+ false));
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement ()
+ {
+ if (find_ == 0)
+ find_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statement,
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_image_binding_,
+ select_image_binding_,
+ false));
+
+ return *find_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
+ update_image_binding_,
+ false));
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement,
+ id_image_binding_,
+ false));
+
+ return *erase_;
+ }
+
+ delete_statement_type&
+ optimistic_erase_statement ()
+ {
+ if (od_.erase_ == 0)
+ {
+ od_.erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::optimistic_erase_statement,
+ od_.id_image_binding_,
+ false));
+ }
+
+ return *od_.erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ image_, id_image_,
+ id_image_binding_, od_.id_image_binding ());
+ }
+
+ public:
+ // select = total - separate_load
+ // insert = total - inverse - managed_optimistic
+ // update = total - inverse - managed_optimistic - id - readonly
+ // - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ object_traits::separate_load_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count -
+ object_traits::managed_optimistic_column_count;
+
+ static const std::size_t update_column_count =
+ insert_column_count -
+ id_column_count -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ object_statements (const object_statements&);
+ object_statements& operator= (const object_statements&);
+
+ protected:
+ template <typename STS>
+ void
+ load_delayed_ (const schema_version_migration*);
+
+ void
+ clear_delayed_ ();
+
+ protected:
+ template <typename T1>
+ friend class polymorphic_derived_object_statements;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ image_type image_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ MYSQL_BIND select_image_bind_[select_column_count];
+ my_bool select_image_truncated_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ MYSQL_BIND insert_image_bind_[insert_column_count];
+
+ // Update binding. Note that the id suffix is bound to id_image_
+ // below instead of image_ which makes this binding effectively
+ // bound to two images. As a result, we have to track versions
+ // for both of them. If this object uses optimistic concurrency,
+ // then the binding for the managed column (version, timestamp,
+ // etc) comes after the id and the image for such a column is
+ // stored as part of the id image.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_image_version_;
+ binding update_image_binding_;
+ MYSQL_BIND update_image_bind_[update_column_count + id_column_count +
+ managed_optimistic_column_count];
+
+ // Id image binding (only used as a parameter). Uses the suffix in
+ // the update bind.
+ //
+ id_image_type id_image_;
+ std::size_t id_image_version_;
+ binding id_image_binding_;
+
+ // Extra data for objects with optimistic concurrency support.
+ //
+ optimistic_data<T, managed_optimistic_column_count != 0> od_;
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_;
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+
+ // Delayed loading.
+ //
+ struct delayed_load
+ {
+ typedef typename pointer_cache_traits::position_type position_type;
+
+ delayed_load () {}
+ delayed_load (const id_type& i,
+ object_type& o,
+ const position_type& p,
+ loader_function l)
+ : id (i), obj (&o), pos (p), loader (l)
+ {
+ }
+
+ id_type id;
+ object_type* obj;
+ position_type pos;
+ loader_function loader;
+ };
+
+ typedef std::vector<delayed_load> delayed_loads;
+ delayed_loads delayed_;
+
+ // Delayed vectors swap guard. See the load_delayed_() function for
+ // details.
+ //
+ struct swap_guard
+ {
+ swap_guard (object_statements& os, delayed_loads& dl)
+ : os_ (os), dl_ (dl)
+ {
+ dl_.swap (os_.delayed_);
+ }
+
+ ~swap_guard ()
+ {
+ os_.clear_delayed ();
+ dl_.swap (os_.delayed_);
+ }
+
+ private:
+ object_statements& os_;
+ delayed_loads& dl_;
+ };
+ };
+ }
+}
+
+#include <odb/mysql/simple-object-statements.ixx>
+#include <odb/mysql/simple-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_SIMPLE_OBJECT_STATEMENTS_HXX
diff --git a/libodb-mysql/odb/mysql/simple-object-statements.ixx b/libodb-mysql/odb/mysql/simple-object-statements.ixx
new file mode 100644
index 0000000..a75fc0d
--- /dev/null
+++ b/libodb-mysql/odb/mysql/simple-object-statements.ixx
@@ -0,0 +1,68 @@
+// file : odb/mysql/simple-object-statements.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // auto_unlock
+ //
+ inline object_statements_base::auto_unlock::
+ auto_unlock (object_statements_base& s)
+ : s_ (s)
+ {
+ s_.unlock ();
+ }
+
+ inline object_statements_base::auto_unlock::
+ ~auto_unlock ()
+ {
+ s_.lock ();
+ }
+
+ //
+ // auto_lock
+ //
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ auto_lock (object_statements& s)
+ : s_ (s)
+ {
+ if (!s_.locked ())
+ {
+ s_.lock ();
+ locked_ = true;
+ }
+ else
+ locked_ = false;
+ }
+
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ ~auto_lock ()
+ {
+ if (locked_)
+ {
+ s_.unlock ();
+ s_.clear_delayed ();
+ }
+ }
+
+ template <typename T>
+ inline bool object_statements<T>::auto_lock::
+ locked () const
+ {
+ return locked_;
+ }
+
+ template <typename T>
+ inline void object_statements<T>::auto_lock::
+ unlock ()
+ {
+ assert (locked_);
+ s_.unlock ();
+ locked_ = false;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/simple-object-statements.txx b/libodb-mysql/odb/mysql/simple-object-statements.txx
new file mode 100644
index 0000000..2c04c71
--- /dev/null
+++ b/libodb-mysql/odb/mysql/simple-object-statements.txx
@@ -0,0 +1,146 @@
+// file : odb/mysql/simple-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // optimistic_data
+ //
+
+ template <typename T>
+ optimistic_data<T, true>::
+ optimistic_data (MYSQL_BIND* b)
+ : id_image_binding_ (
+ b,
+ object_traits::id_column_count +
+ object_traits::managed_optimistic_column_count)
+ {
+ }
+
+ //
+ // object_statements
+ //
+
+ template <typename T>
+ object_statements<T>::
+ ~object_statements ()
+ {
+ }
+
+ template <typename T>
+ object_statements<T>::
+ object_statements (connection_type& conn)
+ : object_statements_base (conn),
+ select_image_binding_ (select_image_bind_, select_column_count),
+ insert_image_binding_ (insert_image_bind_, insert_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_column_count),
+ id_image_binding_ (update_image_bind_ + update_column_count,
+ id_column_count),
+ od_ (update_image_bind_ + update_column_count)
+ {
+ image_.version = 0;
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_image_version_ = 0;
+
+ id_image_.version = 0;
+ id_image_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].error = select_image_truncated_ + i;
+ }
+
+ template <typename T>
+ template <typename STS>
+ void object_statements<T>::
+ load_delayed_ (const schema_version_migration* svm)
+ {
+ database& db (connection ().database ());
+
+ delayed_loads dls;
+ swap_guard sg (*this, dls);
+
+ while (!dls.empty ())
+ {
+ delayed_load l (dls.back ());
+ typename pointer_cache_traits::insert_guard ig (l.pos);
+ dls.pop_back ();
+
+ if (l.loader == 0)
+ {
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (static_cast<STS&> (*this), &l.id))
+ throw object_not_persistent ();
+
+ object_traits::callback (db, *l.obj, callback_event::pre_load);
+
+ // Our calls to init/load below can result in additional delayed
+ // loads being added to the delayed_ vector. We need to process
+ // those before we call the post callback.
+ //
+ tc.init (*l.obj, image (), &db);
+
+ // Load containers, etc.
+ //
+ tc.load_ (static_cast<STS&> (*this), *l.obj, false);
+
+ if (!delayed_.empty ())
+ load_delayed_<STS> (svm);
+
+ // Temporarily unlock the statement for the post_load call so that
+ // it can load objects of this type recursively. This is safe to do
+ // because we have completely loaded the current object. Also the
+ // delayed_ list is clear before the unlock and should be clear on
+ // re-lock (since a callback can only call public API functions
+ // which will make sure all the delayed loads are processed before
+ // returning).
+ //
+ {
+ auto_unlock u (*this);
+ object_traits::callback (db, *l.obj, callback_event::post_load);
+ }
+ }
+ else
+ l.loader (db, l.id, *l.obj, svm);
+
+ pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+ }
+
+ template <typename T>
+ void object_statements<T>::
+ clear_delayed_ ()
+ {
+ // Remove the objects from the session cache.
+ //
+ for (typename delayed_loads::iterator i (delayed_.begin ()),
+ e (delayed_.end ()); i != e; ++i)
+ {
+ pointer_cache_traits::erase (i->pos);
+ }
+
+ delayed_.clear ();
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/statement-cache.hxx b/libodb-mysql/odb/mysql/statement-cache.hxx
new file mode 100644
index 0000000..1b62864
--- /dev/null
+++ b/libodb-mysql/odb/mysql/statement-cache.hxx
@@ -0,0 +1,60 @@
+// file : odb/mysql/statement-cache.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_STATEMENT_CACHE_HXX
+#define ODB_MYSQL_STATEMENT_CACHE_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <typeinfo>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/statements-base.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/type-info.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class LIBODB_MYSQL_EXPORT statement_cache
+ {
+ public:
+ statement_cache (connection& conn)
+ : conn_ (conn),
+ version_seq_ (conn_.database ().schema_version_sequence ()) {}
+
+ template <typename T>
+ typename object_traits_impl<T, id_mysql>::statements_type&
+ find_object ();
+
+
+ template <typename T>
+ view_statements<T>&
+ find_view ();
+
+ private:
+ typedef std::map<const std::type_info*,
+ details::shared_ptr<statements_base>,
+ details::type_info_comparator> map;
+
+ connection& conn_;
+ unsigned int version_seq_;
+ map map_;
+ };
+ }
+}
+
+#include <odb/mysql/statement-cache.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_STATEMENT_CACHE_HXX
diff --git a/libodb-mysql/odb/mysql/statement-cache.txx b/libodb-mysql/odb/mysql/statement-cache.txx
new file mode 100644
index 0000000..c134f8d
--- /dev/null
+++ b/libodb-mysql/odb/mysql/statement-cache.txx
@@ -0,0 +1,60 @@
+// file : odb/mysql/statement-cache.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/database.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ typename object_traits_impl<T, id_mysql>::statements_type&
+ statement_cache::
+ find_object ()
+ {
+ typedef
+ typename object_traits_impl<T, id_mysql>::statements_type
+ statements_type;
+
+ // Clear the cache if the database version has changed. This
+ // makes sure we don't re-use statements that correspond to
+ // the old schema.
+ //
+ if (version_seq_ != conn_.database ().schema_version_sequence ())
+ {
+ map_.clear ();
+ version_seq_ = conn_.database ().schema_version_sequence ();
+ }
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<statements_type&> (*i->second);
+
+ details::shared_ptr<statements_type> p (
+ new (details::shared) statements_type (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+
+ template <typename T>
+ view_statements<T>& statement_cache::
+ find_view ()
+ {
+ // We don't cache any statements for views so no need to clear
+ // the cache.
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<view_statements<T>&> (*i->second);
+
+ details::shared_ptr<view_statements<T> > p (
+ new (details::shared) view_statements<T> (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/statement.cxx b/libodb-mysql/odb/mysql/statement.cxx
new file mode 100644
index 0000000..83f294a
--- /dev/null
+++ b/libodb-mysql/odb/mysql/statement.cxx
@@ -0,0 +1,814 @@
+// file : odb/mysql/statement.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::strlen, std::memmove, std::memset
+#include <cassert>
+
+#include <odb/tracer.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/error.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ // statement
+ //
+
+ statement::
+ statement (connection_type& conn,
+ const string& text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize)
+ : conn_ (conn)
+ {
+ if (process == 0)
+ {
+ text_copy_ = text;
+ text_ = text_copy_.c_str ();
+ }
+ else
+ text_ = text.c_str (); // Temporary, see init().
+
+ init (text.size (), sk, process, optimize);
+ }
+
+ statement::
+ statement (connection_type& conn,
+ const char* text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize,
+ bool copy)
+ : conn_ (conn)
+ {
+ size_t n;
+
+ if (process == 0 && copy)
+ {
+ text_copy_ = text;
+ text_ = text_copy_.c_str ();
+ n = text_copy_.size ();
+ }
+ else
+ {
+ text_ = text; // Potentially temporary, see init().
+ n = strlen (text_);
+ }
+
+ init (n, sk, process, optimize);
+ }
+
+ void statement::
+ init (size_t text_size,
+ statement_kind sk,
+ const binding* proc,
+ bool optimize)
+ {
+ if (proc != 0)
+ {
+ switch (sk)
+ {
+ case statement_select:
+ process_select (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (MYSQL_BIND),
+ '`', '`',
+ optimize);
+ break;
+ case statement_insert:
+ process_insert (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (MYSQL_BIND),
+ '?');
+ break;
+ case statement_update:
+ process_update (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (MYSQL_BIND),
+ '?');
+ break;
+ case statement_delete:
+ assert (false);
+ }
+
+ text_ = text_copy_.c_str ();
+ text_size = text_copy_.size ();
+ }
+
+ // Empty statement.
+ //
+ if (*text_ == '\0')
+ return;
+
+ stmt_.reset (conn_.alloc_stmt_handle ());
+
+ conn_.clear ();
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->prepare (conn_, *this);
+ }
+
+ if (mysql_stmt_prepare (stmt_,
+ text_,
+ static_cast<unsigned long> (text_size)) != 0)
+ translate_error (conn_, stmt_);
+ }
+
+ size_t statement::
+ process_bind (MYSQL_BIND* b, size_t n)
+ {
+ size_t shifts (0);
+ for (MYSQL_BIND* e (b + n); b != e;)
+ {
+ if (b->buffer == 0)
+ {
+ // It is possible that this array has already been processed
+ // (shared among multiple statements).
+ //
+ if (b->length != 0)
+ {
+ n -= e - b;
+ break;
+ }
+
+ e--;
+
+ // Shift the rest of the entries to the left.
+ //
+ memmove (b, b + 1, (e - b) * sizeof (MYSQL_BIND));
+
+ // Store the original position of the NULL entry at the end.
+ //
+ e->buffer = 0;
+ e->length = reinterpret_cast<unsigned long*> (b + shifts);
+
+ shifts++;
+ continue;
+ }
+
+ b++;
+ }
+
+ return n - shifts;
+ }
+
+ void statement::
+ restore_bind (MYSQL_BIND* b, size_t n)
+ {
+ for (MYSQL_BIND* e (b + n - 1); e->buffer == 0 && e->length != 0;)
+ {
+ MYSQL_BIND* p (reinterpret_cast<MYSQL_BIND*> (e->length));
+
+ // Shift the entries from the specified position to the right.
+ //
+ memmove (p + 1, p, (e - p) * sizeof (MYSQL_BIND));
+
+ // Restore the original NULL entry.
+ //
+ memset (p, 0, sizeof (MYSQL_BIND));
+ }
+ }
+
+ statement::
+ ~statement ()
+ {
+ if (stmt_ != 0)
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->deallocate (conn_, *this);
+ }
+
+ // Let the connection handle the release of the statement (it
+ // may delay the actual freeing if it will mess up the currently
+ // active statement).
+ //
+ conn_.free_stmt_handle (stmt_);
+ }
+ }
+
+ const char* statement::
+ text () const
+ {
+ return text_;
+ }
+
+ void statement::
+ cancel ()
+ {
+ }
+
+ // select_statement
+ //
+
+ select_statement::
+ ~select_statement ()
+ {
+ assert (freed_);
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ bool optimize,
+ binding& param,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ end_ (false),
+ cached_ (false),
+ freed_ (true),
+ rows_ (0),
+ param_ (&param),
+ param_version_ (0),
+ result_ (result),
+ result_version_ (0)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& param,
+ binding& result,
+ bool copy_text)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize,
+ copy_text),
+ end_ (false),
+ cached_ (false),
+ freed_ (true),
+ rows_ (0),
+ param_ (&param),
+ param_version_ (0),
+ result_ (result),
+ result_version_ (0)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ bool optimize,
+ binding& result)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize),
+ end_ (false),
+ cached_ (false),
+ freed_ (true),
+ rows_ (0),
+ param_ (0),
+ result_ (result),
+ result_version_ (0)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& result,
+ bool copy_text)
+ : statement (conn,
+ text, statement_select,
+ (process ? &result : 0), optimize,
+ copy_text),
+ end_ (false),
+ cached_ (false),
+ freed_ (true),
+ rows_ (0),
+ param_ (0),
+ result_ (result),
+ result_version_ (0)
+ {
+ }
+
+ void select_statement::
+ execute ()
+ {
+ assert (freed_);
+
+ conn_.clear ();
+
+ end_ = false;
+ rows_ = 0;
+
+ if (mysql_stmt_reset (stmt_))
+ translate_error (conn_, stmt_);
+
+ if (param_ != 0 && param_version_ != param_->version)
+ {
+ // For now cannot have NULL entries.
+ //
+ if (mysql_stmt_bind_param (stmt_, param_->bind))
+ translate_error (conn_, stmt_);
+
+ param_version_ = param_->version;
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ if (mysql_stmt_execute (stmt_))
+ translate_error (conn_, stmt_);
+
+ // This flag appears to be cleared once we start processing the
+ // result, so we have to cache it for free_result() below.
+ //
+#if MYSQL_VERSION_ID >= 50503
+ out_params_ = (conn_.handle ()->server_status & SERVER_PS_OUT_PARAMS);
+#endif
+
+ freed_ = false;
+ conn_.active (this);
+ }
+
+ void select_statement::
+ cache ()
+ {
+ if (!cached_)
+ {
+ if (!end_)
+ {
+ if (mysql_stmt_store_result (stmt_))
+ translate_error (conn_, stmt_);
+
+ // mysql_stmt_num_rows() returns the number of rows that have been
+ // fetched by store_result.
+ //
+ size_ = rows_ + static_cast<size_t> (mysql_stmt_num_rows (stmt_));
+ }
+ else
+ size_ = rows_;
+
+ cached_ = true;
+ }
+ }
+
+ select_statement::result select_statement::
+ fetch (bool next)
+ {
+ if (result_version_ != result_.version)
+ {
+ size_t count (process_bind (result_.bind, result_.count));
+
+ // Make sure that the number of columns in the result returned by
+ // the database matches the number that we expect. A common cause
+ // of this assertion is a native view with a number of data members
+ // not matching the number of columns in the SELECT-list.
+ //
+ assert (mysql_stmt_field_count (stmt_) == count);
+
+ if (mysql_stmt_bind_result (stmt_, result_.bind))
+ translate_error (conn_, stmt_);
+
+ if (count != result_.count)
+ restore_bind (result_.bind, result_.count);
+
+ result_version_ = result_.version;
+ }
+
+ if (!next && rows_ != 0)
+ {
+ assert (cached_);
+ mysql_stmt_data_seek (stmt_, static_cast<my_ulonglong> (rows_ - 1));
+ }
+
+ int r (mysql_stmt_fetch (stmt_));
+
+ switch (r)
+ {
+ case 0:
+ {
+ if (next)
+ rows_++;
+ return success;
+ }
+ case MYSQL_NO_DATA:
+ {
+ end_ = true;
+ return no_data;
+ }
+ case MYSQL_DATA_TRUNCATED:
+ {
+ if (next)
+ rows_++;
+ return truncated;
+ }
+ default:
+ {
+ translate_error (conn_, stmt_);
+ return no_data; // Never reached.
+ }
+ }
+ }
+
+ void select_statement::
+ refetch ()
+ {
+ // Re-fetch columns that were truncated.
+ //
+ unsigned int col (0);
+ for (size_t i (0); i < result_.count; ++i)
+ {
+ MYSQL_BIND& b (result_.bind[i]);
+
+ if (b.buffer == 0) // Skip NULL entries.
+ continue;
+
+ if (*b.error)
+ {
+ *b.error = 0;
+
+ if (mysql_stmt_fetch_column (stmt_, &b, col, 0))
+ translate_error (conn_, stmt_);
+ }
+
+ col++;
+ }
+ }
+
+ void select_statement::
+ free_result ()
+ {
+ if (!freed_)
+ {
+ // If this is a stored procedure call, then we have multiple
+ // results. The first is the rowset that is the result of the
+ // procedure (actually, it can be several rowsets if, for
+ // example, the procedure executes multiple SELECT statements,
+ // but we don't support this). This result we have just handled.
+ // Next, there could be another rowset with just one row which
+ // contains the output variable values (OUT and INOUT; actually,
+ // if the procedure does not have any SELECT statements, then
+ // this will be the first result and we have just handled it).
+ // Finally, the last result is always the stored procedure status
+ // (not clear how to get this value; all MySQL sample code simply
+ // ignores it).
+ //
+ // So what we need to do here is read and ignore these other
+ // results since otherwise MySQL won't let us execute any
+ // subsequent statements. Calling mysql_stmt_next_result()
+ // until it tells us there is no more results seems to do
+ // the trick.
+ //
+ // mysql_stmt_next_result() is only available since 5.5.3.
+ // Checking the source code reveals that it does call
+ // mysql_next_result() (which is available prior to 5.5.3)
+ // but also does some other house keeping. So it is not
+ // clear if it is possible to emulate the below logic for
+ // prior MySQL versions.
+ //
+
+ // Handling OUT parameters requires a special Voodoo dance:
+ // we have to fetch the row itself and we have to call fetch
+ // again and get MYSQL_NO_DATA. Without doing these exact
+ // steps the server simply drops the connection. Go figure.
+ //
+#if MYSQL_VERSION_ID >= 50503
+ if (out_params_)
+ {
+ if (mysql_stmt_fetch (stmt_) != MYSQL_NO_DATA)
+ translate_error (conn_, stmt_);
+ }
+#endif
+
+ if (mysql_stmt_free_result (stmt_))
+ translate_error (conn_, stmt_);
+
+#if MYSQL_VERSION_ID >= 50503
+ {
+ int s;
+ while ((s = mysql_stmt_next_result (stmt_)) == 0)
+ {
+ if (mysql_stmt_field_count (stmt_) != 0)
+ {
+ // The same Voodoo dance as above.
+ //
+ if (conn_.handle ()->server_status & SERVER_PS_OUT_PARAMS)
+ {
+ if (mysql_stmt_fetch (stmt_))
+ translate_error (conn_, stmt_);
+
+ if (mysql_stmt_fetch (stmt_) != MYSQL_NO_DATA)
+ translate_error (conn_, stmt_);
+ }
+
+ if (mysql_stmt_free_result (stmt_))
+ translate_error (conn_, stmt_);
+ }
+ }
+
+ if (s != -1)
+ translate_error (conn_, stmt_);
+ }
+#endif
+
+ if (conn_.active () == this)
+ conn_.active (0);
+
+ end_ = true;
+ cached_ = false;
+ freed_ = true;
+ rows_ = 0;
+ }
+ }
+
+ void select_statement::
+ cancel ()
+ {
+ // If we cached the result, don't free it just yet.
+ //
+ if (!cached_ || end_)
+ free_result ();
+ else
+ conn_.active (0);
+ }
+
+ // insert_statement
+ //
+
+ insert_statement::
+ ~insert_statement ()
+ {
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ binding& param,
+ binding* returning)
+ : statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false),
+ param_ (param),
+ param_version_ (0),
+ returning_ (returning)
+ {
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param,
+ binding* returning,
+ bool copy_text)
+ : statement (conn,
+ text, statement_insert,
+ (process ? &param : 0), false,
+ copy_text),
+ param_ (param),
+ param_version_ (0),
+ returning_ (returning)
+ {
+ }
+
+ bool insert_statement::
+ execute ()
+ {
+ conn_.clear ();
+
+ if (mysql_stmt_reset (stmt_))
+ translate_error (conn_, stmt_);
+
+ if (param_version_ != param_.version)
+ {
+ size_t count (process_bind (param_.bind, param_.count));
+
+ if (mysql_stmt_bind_param (stmt_, param_.bind))
+ translate_error (conn_, stmt_);
+
+ if (count != param_.count)
+ restore_bind (param_.bind, param_.count);
+
+ param_version_ = param_.version;
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ if (mysql_stmt_execute (stmt_))
+ {
+ // An auto-assigned object id should never cause a duplicate
+ // primary key.
+ //
+ if (returning_ == 0 && mysql_stmt_errno (stmt_) == ER_DUP_ENTRY)
+ return false;
+ else
+ translate_error (conn_, stmt_);
+ }
+
+ if (returning_ != 0)
+ {
+ unsigned long long i (mysql_stmt_insert_id (stmt_));
+
+ MYSQL_BIND& b (returning_->bind[0]);
+ void* v (b.buffer);
+
+ switch (b.buffer_type)
+ {
+ case MYSQL_TYPE_TINY:
+ *static_cast<unsigned char*> (v) = static_cast<unsigned char> (i);
+ break;
+ case MYSQL_TYPE_SHORT:
+ *static_cast<unsigned short*> (v) = static_cast<unsigned short> (i);
+ break;
+ case MYSQL_TYPE_LONG:
+ *static_cast<unsigned int*> (v) = static_cast<unsigned int> (i);
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ *static_cast<unsigned long long*> (v) = i;
+ break;
+ default:
+ assert (false); // Auto id column type is not an integer.
+ }
+
+ *b.is_null = false;
+ }
+
+ return true;
+ }
+
+ // update_statement
+ //
+
+ update_statement::
+ ~update_statement ()
+ {
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const string& text,
+ bool process,
+ binding& param)
+ : statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false),
+ param_ (param),
+ param_version_ (0)
+ {
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const char* text,
+ bool process,
+ binding& param,
+ bool copy_text)
+ : statement (conn,
+ text, statement_update,
+ (process ? &param : 0), false,
+ copy_text),
+ param_ (param),
+ param_version_ (0)
+ {
+ }
+
+ unsigned long long update_statement::
+ execute ()
+ {
+ conn_.clear ();
+
+ if (mysql_stmt_reset (stmt_))
+ translate_error (conn_, stmt_);
+
+ if (param_version_ != param_.version)
+ {
+ size_t count (process_bind (param_.bind, param_.count));
+
+ if (mysql_stmt_bind_param (stmt_, param_.bind))
+ translate_error (conn_, stmt_);
+
+ if (count != param_.count)
+ restore_bind (param_.bind, param_.count);
+
+ param_version_ = param_.version;
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ if (mysql_stmt_execute (stmt_))
+ translate_error (conn_, stmt_);
+
+ my_ulonglong r (mysql_stmt_affected_rows (stmt_));
+
+ if (r == static_cast<my_ulonglong> (-1))
+ translate_error (conn_, stmt_);
+
+ return static_cast<unsigned long long> (r);
+ }
+
+ // delete_statement
+ //
+
+ delete_statement::
+ ~delete_statement ()
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const string& text,
+ binding& param)
+ : statement (conn,
+ text, statement_delete,
+ 0, false),
+ param_ (param),
+ param_version_ (0)
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const char* text,
+ binding& param,
+ bool copy_text)
+ : statement (conn,
+ text, statement_delete,
+ 0, false,
+ copy_text),
+ param_ (param),
+ param_version_ (0)
+ {
+ }
+
+ unsigned long long delete_statement::
+ execute ()
+ {
+ conn_.clear ();
+
+ if (mysql_stmt_reset (stmt_))
+ translate_error (conn_, stmt_);
+
+ if (param_version_ != param_.version)
+ {
+ // Cannot have NULL entries for now.
+ //
+ if (mysql_stmt_bind_param (stmt_, param_.bind))
+ translate_error (conn_, stmt_);
+
+ param_version_ = param_.version;
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ if (mysql_stmt_execute (stmt_))
+ translate_error (conn_, stmt_);
+
+ my_ulonglong r (mysql_stmt_affected_rows (stmt_));
+
+ if (r == static_cast<my_ulonglong> (-1))
+ translate_error (conn_, stmt_);
+
+ return static_cast<unsigned long long> (r);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/statement.hxx b/libodb-mysql/odb/mysql/statement.hxx
new file mode 100644
index 0000000..b1465ea
--- /dev/null
+++ b/libodb-mysql/odb/mysql/statement.hxx
@@ -0,0 +1,327 @@
+// file : odb/mysql/statement.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_STATEMENT_HXX
+#define ODB_MYSQL_STATEMENT_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+#include <odb/statement.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/binding.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/auto-handle.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class connection;
+
+ class LIBODB_MYSQL_EXPORT statement: public odb::statement
+ {
+ public:
+ typedef mysql::connection connection_type;
+
+ virtual
+ ~statement () = 0;
+
+ MYSQL_STMT*
+ handle () const
+ {
+ return stmt_;
+ }
+
+ virtual const char*
+ text () const;
+
+ virtual connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // A statement can be empty. This is used to handle situations
+ // where a SELECT or UPDATE statement ends up not having any
+ // columns after processing. An empty statement cannot be
+ // executed.
+ //
+ bool
+ empty () const
+ {
+ return stmt_ == 0;
+ }
+
+ // Cancel the statement execution (e.g., result fetching) so
+ // that another statement can be executed on the connection.
+ //
+ virtual void
+ cancel ();
+
+ protected:
+ // We keep two versions to take advantage of std::string COW.
+ //
+ statement (connection_type&,
+ const std::string& text,
+ statement_kind,
+ const binding* process,
+ bool optimize);
+
+ statement (connection_type&,
+ const char* text,
+ statement_kind,
+ const binding* process,
+ bool optimize,
+ bool copy_text);
+
+ // Process the bind array so that all non-NULL entries are at
+ // the beginning. Return the actual number of bound columns.
+ //
+ static std::size_t
+ process_bind (MYSQL_BIND*, std::size_t n);
+
+ // Restore the original locations of the NULL entries in the bind
+ // array.
+ //
+ static void
+ restore_bind (MYSQL_BIND*, std::size_t n);
+
+ private:
+ void
+ init (std::size_t text_size,
+ statement_kind,
+ const binding* process,
+ bool optimize);
+
+ protected:
+ connection_type& conn_;
+ std::string text_copy_;
+ const char* text_;
+ auto_handle<MYSQL_STMT> stmt_;
+ };
+
+ class LIBODB_MYSQL_EXPORT select_statement: public statement
+ {
+ public:
+ virtual
+ ~select_statement ();
+
+ select_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& param,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& param,
+ binding& result,
+ bool copy_text = true);
+
+ select_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& result,
+ bool copy_text = true);
+
+ enum result
+ {
+ success,
+ no_data,
+ truncated
+ };
+
+ void
+ execute ();
+
+ void
+ cache ();
+
+ bool
+ cached () const
+ {
+ return cached_;
+ }
+
+ // Can only be called on a cached result.
+ //
+ std::size_t
+ result_size () const
+ {
+ return size_;
+ }
+
+ // Number of rows already fetched.
+ //
+ std::size_t
+ fetched () const
+ {
+ return rows_;
+ }
+
+ // Fetch next or current row depending on the next argument.
+ // Note that fetching of the current row is only supported
+ // if the result is cached.
+ //
+ result
+ fetch (bool next = true);
+
+ void
+ refetch ();
+
+ void
+ free_result ();
+
+ virtual void
+ cancel ();
+
+ private:
+ select_statement (const select_statement&);
+ select_statement& operator= (const select_statement&);
+
+ private:
+ bool end_;
+ bool cached_;
+ bool freed_;
+ std::size_t rows_;
+ std::size_t size_;
+
+#if MYSQL_VERSION_ID >= 50503
+ bool out_params_;
+#endif
+
+ binding* param_;
+ std::size_t param_version_;
+
+ binding& result_;
+ std::size_t result_version_;
+ };
+
+ struct LIBODB_MYSQL_EXPORT auto_result
+ {
+ explicit auto_result (select_statement& s): s_ (s) {}
+ ~auto_result () {s_.free_result ();}
+
+ private:
+ auto_result (const auto_result&);
+ auto_result& operator= (const auto_result&);
+
+ private:
+ select_statement& s_;
+ };
+
+ class LIBODB_MYSQL_EXPORT insert_statement: public statement
+ {
+ public:
+ virtual
+ ~insert_statement ();
+
+ insert_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ binding& param,
+ binding* returning);
+
+ insert_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ binding& param,
+ binding* returning,
+ bool copy_text = true);
+
+ // Return true if successful and false if the row is a duplicate.
+ // All other errors are reported by throwing exceptions.
+ //
+ bool
+ execute ();
+
+ private:
+ insert_statement (const insert_statement&);
+ insert_statement& operator= (const insert_statement&);
+
+ private:
+ binding& param_;
+ std::size_t param_version_;
+
+ binding* returning_;
+ };
+
+ class LIBODB_MYSQL_EXPORT update_statement: public statement
+ {
+ public:
+ virtual
+ ~update_statement ();
+
+ update_statement (connection_type& conn,
+ const std::string& text,
+ bool process_text,
+ binding& param);
+
+ update_statement (connection_type& conn,
+ const char* text,
+ bool process_text,
+ binding& param,
+ bool copy_text = true);
+
+ unsigned long long
+ execute ();
+
+ private:
+ update_statement (const update_statement&);
+ update_statement& operator= (const update_statement&);
+
+ private:
+ binding& param_;
+ std::size_t param_version_;
+ };
+
+ class LIBODB_MYSQL_EXPORT delete_statement: public statement
+ {
+ public:
+ virtual
+ ~delete_statement ();
+
+ delete_statement (connection_type& conn,
+ const std::string& text,
+ binding& param);
+
+ delete_statement (connection_type& conn,
+ const char* text,
+ binding& param,
+ bool copy_text = true);
+
+ unsigned long long
+ execute ();
+
+ private:
+ delete_statement (const delete_statement&);
+ delete_statement& operator= (const delete_statement&);
+
+ private:
+ binding& param_;
+ std::size_t param_version_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_STATEMENT_HXX
diff --git a/libodb-mysql/odb/mysql/statements-base.cxx b/libodb-mysql/odb/mysql/statements-base.cxx
new file mode 100644
index 0000000..3e1bcc6
--- /dev/null
+++ b/libodb-mysql/odb/mysql/statements-base.cxx
@@ -0,0 +1,15 @@
+// file : odb/mysql/statements-base.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/statements-base.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ statements_base::
+ ~statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/statements-base.hxx b/libodb-mysql/odb/mysql/statements-base.hxx
new file mode 100644
index 0000000..93e72d2
--- /dev/null
+++ b/libodb-mysql/odb/mysql/statements-base.hxx
@@ -0,0 +1,63 @@
+// file : odb/mysql/statements-base.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_STATEMENTS_BASE_HXX
+#define ODB_MYSQL_STATEMENTS_BASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/schema-version.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/database.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class LIBODB_MYSQL_EXPORT statements_base: public details::shared_base
+ {
+ public:
+ typedef mysql::connection connection_type;
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Schema version. database::schema_version_migration() is thread-
+ // safe which means it is also slow. Cache the result in statements
+ // so we can avoid the mutex lock. This is thread-safe since if the
+ // version is updated, then the statements cache will be expired.
+ //
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ public:
+ virtual
+ ~statements_base ();
+
+ protected:
+ statements_base (connection_type& conn): conn_ (conn), svm_ (0) {}
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_STATEMENTS_BASE_HXX
diff --git a/libodb-mysql/odb/mysql/tracer.cxx b/libodb-mysql/odb/mysql/tracer.cxx
new file mode 100644
index 0000000..da39633
--- /dev/null
+++ b/libodb-mysql/odb/mysql/tracer.cxx
@@ -0,0 +1,60 @@
+// file : odb/mysql/tracer.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/tracer.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/statement.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ tracer::
+ ~tracer ()
+ {
+ }
+
+ void tracer::
+ prepare (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ execute (connection& c, const statement& s)
+ {
+ execute (c, s.text ());
+ }
+
+ void tracer::
+ deallocate (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ prepare (odb::connection& c, const odb::statement& s)
+ {
+ prepare (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const odb::statement& s)
+ {
+ execute (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const char* s)
+ {
+ execute (static_cast<connection&> (c), s);
+ }
+
+ void tracer::
+ deallocate (odb::connection& c, const odb::statement& s)
+ {
+ deallocate (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/tracer.hxx b/libodb-mysql/odb/mysql/tracer.hxx
new file mode 100644
index 0000000..03ca6df
--- /dev/null
+++ b/libodb-mysql/odb/mysql/tracer.hxx
@@ -0,0 +1,61 @@
+// file : odb/mysql/tracer.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_TRACER_HXX
+#define ODB_MYSQL_TRACER_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/tracer.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class LIBODB_MYSQL_EXPORT tracer: private odb::tracer
+ {
+ public:
+ virtual
+ ~tracer ();
+
+ virtual void
+ prepare (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const char* statement) = 0;
+
+ virtual void
+ deallocate (connection&, const statement&);
+
+ private:
+ // Allow these classes to convert mysql::tracer to odb::tracer.
+ //
+ friend class database;
+ friend class connection;
+ friend class transaction;
+
+ virtual void
+ prepare (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const char* statement);
+
+ virtual void
+ deallocate (odb::connection&, const odb::statement&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_TRACER_HXX
diff --git a/libodb-mysql/odb/mysql/traits-calls.hxx b/libodb-mysql/odb/mysql/traits-calls.hxx
new file mode 100644
index 0000000..f6b2ab6
--- /dev/null
+++ b/libodb-mysql/odb/mysql/traits-calls.hxx
@@ -0,0 +1,210 @@
+// file : odb/mysql/traits-calls.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_TRAITS_CALLS_HXX
+#define ODB_MYSQL_TRAITS_CALLS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/mysql-types.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ //
+ // object_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = object_traits_impl<T, id_mysql>::versioned>
+ struct object_traits_calls;
+
+ template <typename T>
+ struct object_traits_calls<T, false>
+ {
+ typedef object_traits_impl<T, id_mysql> traits;
+ typedef typename traits::image_type image_type;
+
+ object_traits_calls (const schema_version_migration*) {}
+
+ const schema_version_migration*
+ version () const {return 0;}
+
+ static bool
+ grow (image_type& i, my_bool* t)
+ {
+ return traits::grow (i, t);
+ }
+
+ static void
+ bind (MYSQL_BIND* b, image_type& i, statement_kind sk)
+ {
+ traits::bind (b, i, sk);
+ }
+
+ // Poly-derived version.
+ //
+ static void
+ bind (MYSQL_BIND* b,
+ const MYSQL_BIND* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk)
+ {
+ traits::bind (b, id, id_size, i, sk);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+
+ static bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id)
+ {
+ return traits::find_ (sts, id);
+ }
+
+ static void
+ load_ (typename traits::statements_type& sts, T& o, bool reload)
+ {
+ return traits::load_ (sts, o, reload);
+ }
+ };
+
+ template <typename T>
+ struct object_traits_calls<T, true>
+ {
+ typedef object_traits_impl<T, id_mysql> traits;
+ typedef typename traits::image_type image_type;
+
+ object_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ const schema_version_migration*
+ version () const {return &svm_;}
+
+ bool
+ grow (image_type& i, my_bool* t) const
+ {
+ return traits::grow (i, t, svm_);
+ }
+
+ void
+ bind (MYSQL_BIND* b, image_type& i, statement_kind sk) const
+ {
+ traits::bind (b, i, sk, svm_);
+ }
+
+ // Poly-derived version.
+ //
+ void
+ bind (MYSQL_BIND* b,
+ const MYSQL_BIND* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk) const
+ {
+ traits::bind (b, id, id_size, i, sk, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id) const
+ {
+ return traits::find_ (sts, id, svm_);
+ }
+
+ void
+ load_ (typename traits::statements_type& sts, T& o, bool reload) const
+ {
+ return traits::load_ (sts, o, reload, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+
+ //
+ // view_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = view_traits_impl<T, id_mysql>::versioned>
+ struct view_traits_calls;
+
+ template <typename T>
+ struct view_traits_calls<T, false>
+ {
+ typedef view_traits_impl<T, id_mysql> traits;
+ typedef typename traits::image_type image_type;
+
+ view_traits_calls (const schema_version_migration*) {}
+
+ static bool
+ grow (image_type& i, my_bool* t)
+ {
+ return traits::grow (i, t);
+ }
+
+ static void
+ bind (MYSQL_BIND* b, image_type& i)
+ {
+ traits::bind (b, i);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+ };
+
+ template <typename T>
+ struct view_traits_calls<T, true>
+ {
+ typedef view_traits_impl<T, id_mysql> traits;
+ typedef typename traits::image_type image_type;
+
+ view_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ bool
+ grow (image_type& i, my_bool* t) const
+ {
+ return traits::grow (i, t, svm_);
+ }
+
+ void
+ bind (MYSQL_BIND* b, image_type& i) const
+ {
+ traits::bind (b, i, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_TRAITS_CALLS_HXX
diff --git a/libodb-mysql/odb/mysql/traits.cxx b/libodb-mysql/odb/mysql/traits.cxx
new file mode 100644
index 0000000..9fbdbb4
--- /dev/null
+++ b/libodb-mysql/odb/mysql/traits.cxx
@@ -0,0 +1,145 @@
+// file : odb/mysql/traits.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/traits.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace mysql
+ {
+ using details::buffer;
+
+ //
+ // string_value_traits
+ //
+
+ void string_value_traits::
+ set_image (buffer& b,
+ size_t& n,
+ bool& is_null,
+ const string& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v.c_str (), n);
+ }
+
+ //
+ // c_string_value_traits
+ //
+
+ void c_string_value_traits::
+ set_image (buffer& b,
+ size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ n = strlen (v);
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+
+ //
+ // c_array_value_traits_base
+ //
+ void c_array_value_traits_base::
+ set_value (char* const& v,
+ const details::buffer& b,
+ size_t n,
+ bool is_null,
+ size_t N)
+ {
+ if (!is_null)
+ {
+ n = n < N ? n : N;
+
+ if (n != 0)
+ memcpy (v, b.data (), n);
+ }
+ else
+ n = 0;
+
+ if (n != N) // Append '\0' if there is space.
+ v[n] = '\0';
+ }
+
+ void c_array_value_traits_base::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const char* v,
+ size_t N)
+ {
+ is_null = false;
+
+ // Figure out the length. We cannot use strlen since it may
+ // not be 0-terminated (strnlen is not standard).
+ //
+ for (n = 0; n != N && v[n] != '\0'; ++n) ;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+
+ //
+ // default_value_traits<vector<char>, id_blob>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<char>, id_blob>::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ memcpy (b.data (), &v.front (), n);
+ }
+
+ //
+ // default_value_traits<vector<unsigned char>, id_blob>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<unsigned char>, id_blob>::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ memcpy (b.data (), &v.front (), n);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/traits.hxx b/libodb-mysql/odb/mysql/traits.hxx
new file mode 100644
index 0000000..e76f7e7
--- /dev/null
+++ b/libodb-mysql/odb/mysql/traits.hxx
@@ -0,0 +1,988 @@
+// file : odb/mysql/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_TRAITS_HXX
+#define ODB_MYSQL_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cstring> // std::memcpy, std::memset, std::strlen
+
+#ifdef ODB_CXX11
+# include <array>
+#endif
+
+#include <odb/traits.hxx>
+#include <odb/wrapper-traits.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/mysql-types.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/wrapper-p.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ enum database_type_id
+ {
+ id_tiny,
+ id_utiny,
+ id_short,
+ id_ushort,
+ id_long,
+ id_ulong,
+ id_longlong,
+ id_ulonglong,
+
+ id_float,
+ id_double,
+ id_decimal,
+
+ id_date,
+ id_time,
+ id_datetime,
+ id_timestamp,
+ id_year,
+
+ id_string,
+ id_blob,
+
+ id_bit,
+ id_enum,
+ id_set
+ };
+
+ //
+ // image_traits
+ //
+
+ template <database_type_id>
+ struct image_traits;
+
+ template <>
+ struct image_traits<id_tiny> {typedef signed char image_type;};
+
+ template <>
+ struct image_traits<id_utiny> {typedef unsigned char image_type;};
+
+ template <>
+ struct image_traits<id_short> {typedef short image_type;};
+
+ template <>
+ struct image_traits<id_ushort> {typedef unsigned short image_type;};
+
+ template <>
+ struct image_traits<id_long> {typedef int image_type;};
+
+ template <>
+ struct image_traits<id_ulong> {typedef unsigned int image_type;};
+
+ template <>
+ struct image_traits<id_longlong> {typedef long long image_type;};
+
+ template <>
+ struct image_traits<id_ulonglong> {typedef unsigned long long image_type;};
+
+ template <>
+ struct image_traits<id_float> {typedef float image_type;};
+
+ template <>
+ struct image_traits<id_double> {typedef double image_type;};
+
+ template <>
+ struct image_traits<id_decimal> {typedef details::buffer image_type;};
+
+ template <>
+ struct image_traits<id_date> {typedef MYSQL_TIME image_type;};
+
+ template <>
+ struct image_traits<id_time> {typedef MYSQL_TIME image_type;};
+
+ template <>
+ struct image_traits<id_datetime> {typedef MYSQL_TIME image_type;};
+
+ template <>
+ struct image_traits<id_timestamp> {typedef MYSQL_TIME image_type;};
+
+ template <>
+ struct image_traits<id_year> {typedef short image_type;};
+
+ template <>
+ struct image_traits<id_string> {typedef details::buffer image_type;};
+
+ template <>
+ struct image_traits<id_blob> {typedef details::buffer image_type;};
+
+ template <>
+ struct image_traits<id_bit> {typedef unsigned char* image_type;};
+
+ // Note: default mapping is to integer. Alternative mapping is to
+ // string.
+ //
+ template <>
+ struct image_traits<id_enum> {typedef unsigned short image_type;};
+
+ template <>
+ struct image_traits<id_set> {typedef details::buffer image_type;};
+
+ //
+ // value_traits
+ //
+
+ template <typename W, database_type_id, bool null_handler>
+ struct wrapped_value_traits;
+
+ template <typename T, database_type_id>
+ struct default_value_traits;
+
+ template <typename T, database_type_id, bool w = details::wrapper_p<T>::r>
+ struct select_traits;
+
+ template <typename T, database_type_id ID>
+ struct select_traits<T, ID, false>
+ {
+ typedef default_value_traits<T, ID> type;
+ };
+
+ template <typename W, database_type_id ID>
+ struct select_traits<W, ID, true>
+ {
+ typedef
+ wrapped_value_traits<W, ID, wrapper_traits<W>::null_handler>
+ type;
+ };
+
+ template <typename T, database_type_id ID>
+ class value_traits: public select_traits<T, ID>::type
+ {
+ };
+
+ // The wrapped_value_traits specializations should be able to handle
+ // any value type which means we have to have every possible signature
+ // of the set_value() and set_image() functions.
+ //
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, false>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // String, BLOB, ENUM, and SET.
+ //
+ static void
+ set_value (W& v, const details::buffer& b, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::buffer& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // BIT.
+ //
+ static void
+ set_value (W& v, const unsigned char* i, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (unsigned char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, true>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // String, BLOB, ENUM, and SET.
+ //
+ static void
+ set_value (W& v, const details::buffer& b, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::buffer& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // BIT.
+ //
+ static void
+ set_value (W& v, const unsigned char* i, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (unsigned char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename T, database_type_id ID>
+ struct default_value_traits
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ static void
+ set_value (T& v, const image_type& i, bool is_null)
+ {
+ if (!is_null)
+ v = T (i);
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, T v)
+ {
+ is_null = false;
+ i = image_type (v);
+ }
+ };
+
+ // Specialization for numeric enum representations (C++ enum, integer
+ // types, etc). In particular, this specialization works only for C++
+ // enum type as long as its numeric value space starts with 0, is
+ // ascending and contiguous (i.e., the default enumerator assignment).
+ //
+ template <typename T>
+ struct default_value_traits<T, id_enum>
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef unsigned short image_type;
+
+ static void
+ set_value (T& v, unsigned short i, bool is_null)
+ {
+ // In MySQL first enumerator has index 1.
+ //
+ if (!is_null)
+ v = static_cast<T> (i - 1);
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (unsigned short& i, bool& is_null, const T& v)
+ {
+ is_null = false;
+ i = static_cast<unsigned short> (v) + 1;
+ }
+ };
+
+ // std::string specialization.
+ //
+ class LIBODB_MYSQL_EXPORT string_value_traits
+ {
+ public:
+ typedef std::string value_type;
+ typedef std::string query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (std::string& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ v.assign (b.data (), n);
+ else
+ v.erase ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const std::string&);
+ };
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<std::string, id_string>:
+ string_value_traits
+ {
+ };
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<std::string, id_decimal>:
+ string_value_traits
+ {
+ };
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<std::string, id_enum>:
+ string_value_traits
+ {
+ };
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<std::string, id_set>:
+ string_value_traits
+ {
+ };
+
+ // char*/const char* specialization
+ //
+ // Specialization for const char* which only supports initialization
+ // of an image from the value but not the other way around. This way
+ // we can pass such values to the queries.
+ //
+ class LIBODB_MYSQL_EXPORT c_string_value_traits
+ {
+ public:
+ typedef const char* value_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const char*);
+ };
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<char*, id_string>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<char*, id_decimal>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<char*, id_enum>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<char*, id_set>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<const char*, id_string>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<const char*, id_decimal>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<const char*, id_enum>:
+ c_string_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<const char*, id_set>:
+ c_string_value_traits {};
+
+ // char[N] specializations.
+ //
+ struct LIBODB_MYSQL_EXPORT c_array_value_traits_base
+ {
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null,
+ std::size_t N);
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v,
+ std::size_t N);
+ };
+
+ template <std::size_t N>
+ struct c_array_value_traits
+ {
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (v, b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, v, N);
+ }
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_string>: c_array_value_traits<N>
+ {};
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_decimal>: c_array_value_traits<N>
+ {};
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_enum>: c_array_value_traits<N>
+ {};
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_set>: c_array_value_traits<N>
+ {};
+
+ // std::array<char, N> (string) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct std_array_value_traits
+ {
+ typedef std::array<char, N> value_type;
+ typedef std::array<char, N> query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (v.data (), b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, v.data (), N);
+ }
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_string>:
+ std_array_value_traits<N> {};
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_decimal>:
+ std_array_value_traits<N> {};
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_enum>:
+ std_array_value_traits<N> {};
+
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_set>:
+ std_array_value_traits<N> {};
+#endif
+
+ // char specialization.
+ //
+ struct LIBODB_MYSQL_EXPORT char_value_traits
+ {
+ typedef char value_type;
+ typedef char query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (&v, b, n, is_null, 1);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ char v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, &v, 1);
+ }
+ };
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<char, id_string>:
+ char_value_traits {};
+
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<char, id_enum>:
+ char_value_traits {};
+
+ // std::vector<char> (buffer) specialization.
+ //
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<std::vector<char>, id_blob>
+ {
+ public:
+ typedef std::vector<char> value_type;
+ typedef std::vector<char> query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ v.assign (b.data (), b.data () + n);
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const value_type&);
+ };
+
+ // std::vector<unsigned char> (buffer) specialization.
+ //
+ template <>
+ struct LIBODB_MYSQL_EXPORT default_value_traits<
+ std::vector<unsigned char>, id_blob>
+ {
+ public:
+ typedef std::vector<unsigned char> value_type;
+ typedef std::vector<unsigned char> query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ {
+ const unsigned char* d (
+ reinterpret_cast<const unsigned char*> (b.data ()));
+ v.assign (d, d + n);
+ }
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const value_type&);
+ };
+
+ // char[N] (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_blob>
+ {
+ public:
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b.data (), (n < N ? n : N));
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v, n);
+ }
+ };
+
+ // unsigned char[N] (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<unsigned char[N], id_blob>
+ {
+ public:
+ typedef unsigned char* value_type;
+ typedef unsigned char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (unsigned char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b.data (), (n < N ? n : N));
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const unsigned char* v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v, n);
+ }
+ };
+
+#ifdef ODB_CXX11
+ // std::array<char, N> (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_blob>
+ {
+ public:
+ typedef std::array<char, N> value_type;
+ typedef value_type query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b.data (), (n < N ? n : N));
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data (), n);
+ }
+ };
+
+ // std::array<unsigned char, N> (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<unsigned char, N>, id_blob>
+ {
+ public:
+ typedef std::array<unsigned char, N> value_type;
+ typedef value_type query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b.data (), (n < N ? n : N));
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data (), n);
+ }
+ };
+#endif
+
+ //
+ // type_traits
+ //
+
+ template <typename T>
+ struct default_type_traits;
+
+ template <typename T>
+ class type_traits: public default_type_traits<T>
+ {
+ };
+
+ // Integral types.
+ //
+ template <>
+ struct default_type_traits<bool>
+ {
+ static const database_type_id db_type_id = id_tiny;
+ };
+
+ template <>
+ struct default_type_traits<signed char>
+ {
+ static const database_type_id db_type_id = id_tiny;
+ };
+
+ template <>
+ struct default_type_traits<unsigned char>
+ {
+ static const database_type_id db_type_id = id_utiny;
+ };
+
+ template <>
+ struct default_type_traits<short>
+ {
+ static const database_type_id db_type_id = id_short;
+ };
+
+ template <>
+ struct default_type_traits<unsigned short>
+ {
+ static const database_type_id db_type_id = id_ushort;
+ };
+
+ template <>
+ struct default_type_traits<int>
+ {
+ static const database_type_id db_type_id = id_long;
+ };
+
+ template <>
+ struct default_type_traits<unsigned int>
+ {
+ static const database_type_id db_type_id = id_ulong;
+ };
+
+ template <>
+ struct default_type_traits<long>
+ {
+ static const database_type_id db_type_id = id_longlong;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long>
+ {
+ static const database_type_id db_type_id = id_ulonglong;
+ };
+
+ template <>
+ struct default_type_traits<long long>
+ {
+ static const database_type_id db_type_id = id_longlong;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long long>
+ {
+ static const database_type_id db_type_id = id_ulonglong;
+ };
+
+ // Float types.
+ //
+ template <>
+ struct default_type_traits<float>
+ {
+ static const database_type_id db_type_id = id_float;
+ };
+
+ template <>
+ struct default_type_traits<double>
+ {
+ static const database_type_id db_type_id = id_double;
+ };
+
+ // String types.
+ //
+ template <>
+ struct default_type_traits<std::string>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ template <>
+ struct default_type_traits<char*>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ template <>
+ struct default_type_traits<const char*>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ template <std::size_t N>
+ struct default_type_traits<char[N]>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<char, N> >
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+#endif
+
+ template <>
+ struct default_type_traits<char>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ // Binary types.
+ //
+ template <std::size_t N>
+ struct default_type_traits<unsigned char[N]>
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<char> >
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<unsigned char> >
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<unsigned char, N> >
+ {
+ static const database_type_id db_type_id = id_blob;
+ };
+#endif
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_TRAITS_HXX
diff --git a/libodb-mysql/odb/mysql/transaction-impl.cxx b/libodb-mysql/odb/mysql/transaction-impl.cxx
new file mode 100644
index 0000000..0ca1546
--- /dev/null
+++ b/libodb-mysql/odb/mysql/transaction-impl.cxx
@@ -0,0 +1,108 @@
+// file : odb/mysql/transaction-impl.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/tracer.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/connection.hxx>
+#include <odb/mysql/error.hxx>
+#include <odb/mysql/transaction-impl.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ transaction_impl::
+ transaction_impl (database_type& db)
+ : odb::transaction_impl (db)
+ {
+ }
+
+ transaction_impl::
+ transaction_impl (connection_ptr c)
+ : odb::transaction_impl (c->database (), *c), connection_ (c)
+ {
+ }
+
+ transaction_impl::
+ ~transaction_impl ()
+ {
+ }
+
+ void transaction_impl::
+ start ()
+ {
+ // Grab a connection if we don't already have one.
+ //
+ if (connection_ == 0)
+ {
+ connection_ = static_cast<database_type&> (database_).connection ();
+ odb::transaction_impl::connection_ = connection_.get ();
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "BEGIN");
+ }
+
+ if (mysql_real_query (connection_->handle (), "begin", 5) != 0)
+ translate_error (*connection_);
+ }
+
+ void transaction_impl::
+ commit ()
+ {
+ // Invalidate query results.
+ //
+ connection_->invalidate_results ();
+
+ // Cancel and clear the active statement if any. This normally
+ // should happen automatically, however, if an exception is
+ // thrown, this may not be the case.
+ //
+ connection_->clear ();
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "COMMIT");
+ }
+
+ if (mysql_real_query (connection_->handle (), "commit", 6) != 0)
+ translate_error (*connection_);
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+
+ void transaction_impl::
+ rollback ()
+ {
+ // Invalidate query results.
+ //
+ connection_->invalidate_results ();
+
+ // Cancel and clear the active statement if any. This normally
+ // should happen automatically, however, if an exception is
+ // thrown, this may not be the case.
+ //
+ connection_->clear ();
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "ROLLBACK");
+ }
+
+ if (mysql_real_query (connection_->handle (), "rollback", 8) != 0)
+ translate_error (*connection_);
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/transaction-impl.hxx b/libodb-mysql/odb/mysql/transaction-impl.hxx
new file mode 100644
index 0000000..d87ad35
--- /dev/null
+++ b/libodb-mysql/odb/mysql/transaction-impl.hxx
@@ -0,0 +1,49 @@
+// file : odb/mysql/transaction-impl.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_TRANSACTION_IMPL_HXX
+#define ODB_MYSQL_TRANSACTION_IMPL_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class LIBODB_MYSQL_EXPORT transaction_impl: public odb::transaction_impl
+ {
+ public:
+ typedef mysql::database database_type;
+ typedef mysql::connection connection_type;
+
+ transaction_impl (database_type&);
+ transaction_impl (connection_ptr);
+
+ virtual
+ ~transaction_impl ();
+
+ virtual void
+ start ();
+
+ virtual void
+ commit ();
+
+ virtual void
+ rollback ();
+
+ private:
+ connection_ptr connection_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_TRANSACTION_IMPL_HXX
diff --git a/libodb-mysql/odb/mysql/transaction.cxx b/libodb-mysql/odb/mysql/transaction.cxx
new file mode 100644
index 0000000..5d59fad
--- /dev/null
+++ b/libodb-mysql/odb/mysql/transaction.cxx
@@ -0,0 +1,26 @@
+// file : odb/mysql/transaction.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/mysql/transaction.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ transaction& transaction::
+ current ()
+ {
+ // While the impl type can be of the concrete type, the transaction
+ // object can be created as either odb:: or odb::mysql:: type. To
+ // work around that we are going to hard-cast one two the other
+ // relying on the fact that they have the same representation and
+ // no virtual functions. The former is checked in the tests.
+ //
+ odb::transaction& b (odb::transaction::current ());
+ assert (dynamic_cast<transaction_impl*> (&b.implementation ()) != 0);
+ return reinterpret_cast<transaction&> (b);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/transaction.hxx b/libodb-mysql/odb/mysql/transaction.hxx
new file mode 100644
index 0000000..0f5ae6f
--- /dev/null
+++ b/libodb-mysql/odb/mysql/transaction.hxx
@@ -0,0 +1,88 @@
+// file : odb/mysql/transaction.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_TRANSACTION_HXX
+#define ODB_MYSQL_TRANSACTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx>
+#include <odb/mysql/tracer.hxx>
+
+#include <odb/mysql/details/export.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ class transaction_impl;
+
+ class LIBODB_MYSQL_EXPORT transaction: public odb::transaction
+ {
+ public:
+ typedef mysql::database database_type;
+ typedef mysql::connection connection_type;
+
+ explicit
+ transaction (transaction_impl*, bool make_current = true);
+
+ transaction ();
+
+ // Return the database this transaction is on.
+ //
+ database_type&
+ database ();
+
+ // Return the underlying database connection for this transaction.
+ //
+ connection_type&
+ connection ();
+
+ connection_type&
+ connection (odb::database&);
+
+ // Return current transaction or throw if there is no transaction
+ // in effect.
+ //
+ static transaction&
+ current ();
+
+ // Set the current thread's transaction.
+ //
+ static void
+ current (transaction&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef mysql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ using odb::transaction::tracer;
+
+ public:
+ transaction_impl&
+ implementation ();
+ };
+ }
+}
+
+#include <odb/mysql/transaction.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_TRANSACTION_HXX
diff --git a/libodb-mysql/odb/mysql/transaction.ixx b/libodb-mysql/odb/mysql/transaction.ixx
new file mode 100644
index 0000000..15af6a5
--- /dev/null
+++ b/libodb-mysql/odb/mysql/transaction.ixx
@@ -0,0 +1,57 @@
+// file : odb/mysql/transaction.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/transaction-impl.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ inline transaction::
+ transaction (transaction_impl* impl, bool make_current)
+ : odb::transaction (impl, make_current)
+ {
+ }
+
+ inline transaction::
+ transaction ()
+ : odb::transaction ()
+ {
+ }
+
+ inline transaction_impl& transaction::
+ implementation ()
+ {
+ // We can use static_cast here since we have an instance of
+ // mysql::transaction.
+ //
+ return static_cast<transaction_impl&> (
+ odb::transaction::implementation ());
+ }
+
+ inline transaction::database_type& transaction::
+ database ()
+ {
+ return static_cast<database_type&> (odb::transaction::database ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection ()
+ {
+ return static_cast<connection_type&> (odb::transaction::connection ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection (odb::database& db)
+ {
+ return static_cast<connection_type&> (odb::transaction::connection (db));
+ }
+
+ inline void transaction::
+ current (transaction& t)
+ {
+ odb::transaction::current (t);
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/version-build2-stub.hxx b/libodb-mysql/odb/mysql/version-build2-stub.hxx
new file mode 100644
index 0000000..72bb807
--- /dev/null
+++ b/libodb-mysql/odb/mysql/version-build2-stub.hxx
@@ -0,0 +1,4 @@
+// file : odb/mysql/version-build2-stub.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/mysql/version.hxx>
diff --git a/libodb-mysql/odb/mysql/version-build2.hxx b/libodb-mysql/odb/mysql/version-build2.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libodb-mysql/odb/mysql/version-build2.hxx
diff --git a/libodb-mysql/odb/mysql/version-build2.hxx.in b/libodb-mysql/odb/mysql/version-build2.hxx.in
new file mode 100644
index 0000000..3db7601
--- /dev/null
+++ b/libodb-mysql/odb/mysql/version-build2.hxx.in
@@ -0,0 +1,62 @@
+// file : odb/mysql/version-build2.hxx.in
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef LIBODB_MYSQL_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_MYSQL_VERSION $libodb_mysql.version.project_number$ULL
+#define LIBODB_MYSQL_VERSION_STR "$libodb_mysql.version.project$"
+#define LIBODB_MYSQL_VERSION_ID "$libodb_mysql.version.project_id$"
+
+#define LIBODB_MYSQL_VERSION_MAJOR $libodb_mysql.version.major$
+#define LIBODB_MYSQL_VERSION_MINOR $libodb_mysql.version.minor$
+#define LIBODB_MYSQL_VERSION_PATCH $libodb_mysql.version.patch$
+
+#define LIBODB_MYSQL_PRE_RELEASE $libodb_mysql.version.pre_release$
+
+#define LIBODB_MYSQL_SNAPSHOT $libodb_mysql.version.snapshot_sn$ULL
+#define LIBODB_MYSQL_SNAPSHOT_ID "$libodb_mysql.version.snapshot_id$"
+
+#ifdef LIBODB_MYSQL_MARIADB
+# include <mysql/mariadb_version.h>
+
+// We support MariaDB starting from 10.2.2 when the library started to be
+// named as as mariadb, rather than mysqlclient. Before that we just don't
+// distinguish it from the MySQL client library.
+//
+# if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID < 100202
+# error unexpected MariaDB version detected
+# endif
+#else
+# include <mysql/mysql_version.h>
+
+// Check that we have a compatible MySQL version (5.0.3 or later).
+//
+# if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID < 50003
+# error incompatible MySQL version detected
+# endif
+#endif
+
+#include <odb/version.hxx>
+
+$libodb.check(LIBODB_VERSION, LIBODB_SNAPSHOT)$
+
+#endif // LIBODB_MYSQL_VERSION
diff --git a/libodb-mysql/odb/mysql/version.hxx b/libodb-mysql/odb/mysql/version.hxx
new file mode 100644
index 0000000..d778a10
--- /dev/null
+++ b/libodb-mysql/odb/mysql/version.hxx
@@ -0,0 +1,61 @@
+// file : odb/mysql/version.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifdef LIBODB_MYSQL_BUILD2
+# include <odb/mysql/version-build2.hxx>
+#else
+
+#ifndef ODB_MYSQL_VERSION_HXX
+#define ODB_MYSQL_VERSION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/mysql/details/config.hxx>
+
+#ifdef LIBODB_MYSQL_INCLUDE_SHORT
+# include <mysql_version.h>
+#else
+# include <mysql/mysql_version.h>
+#endif
+
+#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
+
+// Check that we have a compatible MySQL version (5.0.3 or later).
+//
+#if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID < 50003
+# error incompatible MySQL version detected
+#endif
+
+// libodb-mysql version: odb interface version plus the bugfix
+// version.
+//
+#define LIBODB_MYSQL_VERSION 2049976
+#define LIBODB_MYSQL_VERSION_STR "2.5.0-b.26"
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_VERSION_HXX
+#endif // LIBODB_MYSQL_BUILD2
diff --git a/libodb-mysql/odb/mysql/view-result.hxx b/libodb-mysql/odb/mysql/view-result.hxx
new file mode 100644
index 0000000..2a7682b
--- /dev/null
+++ b/libodb-mysql/odb/mysql/view-result.hxx
@@ -0,0 +1,81 @@
+// file : odb/mysql/view-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_VIEW_RESULT_HXX
+#define ODB_MYSQL_VIEW_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/view-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/forward.hxx> // query_base, view_statements
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ class view_result_impl: public odb::view_result_impl<T>
+ {
+ public:
+ typedef odb::view_result_impl<T> base_type;
+
+ typedef typename base_type::view_type view_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef view_traits_impl<view_type, id_mysql> view_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef view_statements<view_type> statements_type;
+
+ virtual
+ ~view_result_impl ();
+
+ view_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (view_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ fetch ();
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ view_traits_calls<view_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/mysql/view-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_VIEW_RESULT_HXX
diff --git a/libodb-mysql/odb/mysql/view-result.txx b/libodb-mysql/odb/mysql/view-result.txx
new file mode 100644
index 0000000..c480920
--- /dev/null
+++ b/libodb-mysql/odb/mysql/view-result.txx
@@ -0,0 +1,178 @@
+// file : odb/mysql/view-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/mysql/view-statements.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ view_result_impl<T>::
+ ~view_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ view_result_impl<T>::
+ view_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ load (view_type& view)
+ {
+ if (count_ > statement_->fetched ())
+ fetch ();
+
+ view_traits::callback (this->db_, view, callback_event::pre_load);
+ tc_.init (view, statements_.image (), &this->db_);
+ view_traits::callback (this->db_, view, callback_event::post_load);
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (this->end_)
+ return;
+
+ // If we are cached, simply increment the position and
+ // postpone the actual row fetching until later. This way
+ // if the same view is loaded in between iteration, the
+ // image won't be messed up.
+ //
+ count_++;
+
+ if (statement_->cached ())
+ this->end_ = count_ > statement_->result_size ();
+ else
+ fetch ();
+
+ if (this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ fetch ()
+ {
+ // If the result is cached, the image can grow between calls
+ // to fetch() as a result of other statements execution.
+ //
+ if (statement_->cached ())
+ {
+ typename view_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.image_version ())
+ {
+ binding& b (statements_.image_binding ());
+ tc_.bind (b.bind, im);
+ statements_.image_version (im.version);
+ b.version++;
+ }
+ }
+
+ while (!this->end_ && count_ > statement_->fetched ())
+ {
+ select_statement::result r (statement_->fetch ());
+
+ switch (r)
+ {
+ case select_statement::truncated:
+ {
+ // Don't re-fetch data we are skipping.
+ //
+ if (count_ != statement_->fetched ())
+ continue;
+
+ typename view_traits::image_type& im (statements_.image ());
+
+ if (tc_.grow (im, statements_.image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.image_version ())
+ {
+ binding& b (statements_.image_binding ());
+ tc_.bind (b.bind, im);
+ statements_.image_version (im.version);
+ b.version++;
+ statement_->refetch ();
+ }
+ // Fall throught.
+ }
+ case select_statement::success:
+ {
+ break;
+ }
+ case select_statement::no_data:
+ {
+ this->end_ = true;
+ break;
+ }
+ }
+ }
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ cache ()
+ {
+ if (!this->end_ && !statement_->cached ())
+ {
+ statement_->cache ();
+
+ if (count_ == statement_->result_size ())
+ {
+ statement_->free_result ();
+ count_++; // One past the result size.
+ this->end_ = true;
+ }
+ }
+ }
+
+ template <typename T>
+ std::size_t view_result_impl<T>::
+ size ()
+ {
+ if (!this->end_)
+ {
+ if (!statement_->cached ())
+ throw result_not_cached ();
+
+ return statement_->result_size ();
+ }
+ else
+ return count_ - 1; // One past the result size.
+ }
+ }
+}
diff --git a/libodb-mysql/odb/mysql/view-statements.hxx b/libodb-mysql/odb/mysql/view-statements.hxx
new file mode 100644
index 0000000..cff4bc1
--- /dev/null
+++ b/libodb-mysql/odb/mysql/view-statements.hxx
@@ -0,0 +1,88 @@
+// file : odb/mysql/view-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_MYSQL_VIEW_STATEMENTS_HXX
+#define ODB_MYSQL_VIEW_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/mysql/mysql.hxx>
+#include <odb/mysql/version.hxx>
+#include <odb/mysql/statement.hxx>
+#include <odb/mysql/statements-base.hxx>
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ class view_statements: public statements_base
+ {
+ public:
+ typedef T view_type;
+ typedef view_traits_impl<view_type, id_mysql> view_traits;
+ typedef typename view_traits::pointer_type pointer_type;
+ typedef typename view_traits::image_type image_type;
+
+ public:
+ view_statements (connection_type&);
+
+ virtual
+ ~view_statements ();
+
+ // View image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ std::size_t
+ image_version () const
+ {
+ return image_version_;
+ }
+
+ void
+ image_version (std::size_t v)
+ {
+ image_version_ = v;
+ }
+
+ binding&
+ image_binding ()
+ {
+ return image_binding_;
+ }
+
+ my_bool*
+ image_truncated ()
+ {
+ return image_truncated_;
+ }
+
+ private:
+ view_statements (const view_statements&);
+ view_statements& operator= (const view_statements&);
+
+ private:
+ image_type image_;
+ std::size_t image_version_;
+ binding image_binding_;
+ MYSQL_BIND image_bind_[view_traits::column_count];
+ my_bool image_truncated_[view_traits::column_count];
+ };
+ }
+}
+
+#include <odb/mysql/view-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_MYSQL_VIEW_STATEMENTS_HXX
diff --git a/libodb-mysql/odb/mysql/view-statements.txx b/libodb-mysql/odb/mysql/view-statements.txx
new file mode 100644
index 0000000..b425227
--- /dev/null
+++ b/libodb-mysql/odb/mysql/view-statements.txx
@@ -0,0 +1,33 @@
+// file : odb/mysql/view-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace mysql
+ {
+ template <typename T>
+ view_statements<T>::
+ ~view_statements ()
+ {
+ }
+
+ template <typename T>
+ view_statements<T>::
+ view_statements (connection_type& conn)
+ : statements_base (conn),
+ image_binding_ (image_bind_, view_traits::column_count)
+ {
+ image_.version = 0;
+ image_version_ = 0;
+
+ std::memset (image_bind_, 0, sizeof (image_bind_));
+ std::memset (image_truncated_, 0, sizeof (image_truncated_));
+
+ for (std::size_t i (0); i < view_traits::column_count; ++i)
+ image_bind_[i].error = image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-mysql/tests/.gitignore b/libodb-mysql/tests/.gitignore
new file mode 100644
index 0000000..e54525b
--- /dev/null
+++ b/libodb-mysql/tests/.gitignore
@@ -0,0 +1 @@
+driver
diff --git a/libodb-mysql/tests/basics/buildfile b/libodb-mysql/tests/basics/buildfile
new file mode 100644
index 0000000..8899347
--- /dev/null
+++ b/libodb-mysql/tests/basics/buildfile
@@ -0,0 +1,6 @@
+# file : tests/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libs = libodb-mysql%lib{odb-mysql}
+
+exe{driver}: {hxx cxx}{*} $libs
diff --git a/libodb-mysql/tests/basics/driver.cxx b/libodb-mysql/tests/basics/driver.cxx
new file mode 100644
index 0000000..81ebb19
--- /dev/null
+++ b/libodb-mysql/tests/basics/driver.cxx
@@ -0,0 +1,37 @@
+// file : tests/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Basic test to make sure the library is usable. Functionality testing
+// is done in the odb-tests package.
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/mysql/database.hxx>
+#include <odb/mysql/exceptions.hxx>
+#include <odb/mysql/transaction.hxx>
+
+using namespace odb::mysql;
+
+int
+main ()
+{
+ {
+ std::ostringstream os;
+ database::print_usage (os);
+ assert (!os.str ().empty ());
+ }
+
+ // We can't really do much here since that would require a database. We can
+ // create a fake database object as long as we don't expect to get a valid
+ // connection.
+ //
+ database db ("john", "secret", "dummy whammy");
+
+ try
+ {
+ transaction t (db.begin ());
+ assert (false);
+ }
+ catch (const database_exception&) {}
+}
diff --git a/libodb-mysql/tests/build/.gitignore b/libodb-mysql/tests/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-mysql/tests/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-mysql/tests/build/bootstrap.build b/libodb-mysql/tests/build/bootstrap.build
new file mode 100644
index 0000000..6ee38db
--- /dev/null
+++ b/libodb-mysql/tests/build/bootstrap.build
@@ -0,0 +1,8 @@
+# file : tests/build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = # Unnamed subproject.
+
+using config
+using dist
+using test
diff --git a/libodb-mysql/tests/build/root.build b/libodb-mysql/tests/build/root.build
new file mode 100644
index 0000000..6c5a90b
--- /dev/null
+++ b/libodb-mysql/tests/build/root.build
@@ -0,0 +1,23 @@
+# file : tests/build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+cxx{*}: extension = cxx
+
+if ($cxx.target.system == 'win32-msvc')
+ cxx.poptions += -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS
+
+if ($cxx.class == 'msvc')
+ cxx.coptions += /wd4251 /wd4275 /wd4800
+
+# Every exe{} in this subproject is by default a test.
+#
+exe{*}: test = true
+
+# Specify the test target for cross-testing.
+#
+test.target = $cxx.target
diff --git a/libodb-mysql/tests/buildfile b/libodb-mysql/tests/buildfile
new file mode 100644
index 0000000..57588a4
--- /dev/null
+++ b/libodb-mysql/tests/buildfile
@@ -0,0 +1,4 @@
+# file : tests/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/}