summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2024-01-25 17:29:20 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2024-01-25 17:29:20 +0300
commit79d486cab2ee860fcd2861c45d0fc5c48fc92cc0 (patch)
treed2c0c247ff34a4f3594773d25972e5b54f4c279a
parenta4d3aa21247990f9c7936bc3e47d64bc02f35ff9 (diff)
parentd9f372d7b1bc1abbff5fdf9735118290cd024d5e (diff)
Merge branch 'libodb-pgsql' into multi-package
-rw-r--r--libodb-pgsql/.gitignore25
-rw-r--r--libodb-pgsql/GPLv2340
-rw-r--r--libodb-pgsql/INSTALL6
-rw-r--r--libodb-pgsql/LICENSE20
-rw-r--r--libodb-pgsql/README20
-rw-r--r--libodb-pgsql/build/.gitignore3
-rw-r--r--libodb-pgsql/build/bootstrap.build10
-rw-r--r--libodb-pgsql/build/export.build9
-rw-r--r--libodb-pgsql/build/root.build19
-rw-r--r--libodb-pgsql/buildfile9
-rw-r--r--libodb-pgsql/manifest23
-rw-r--r--libodb-pgsql/odb/pgsql/auto-handle.cxx24
-rw-r--r--libodb-pgsql/odb/pgsql/auto-handle.hxx90
-rw-r--r--libodb-pgsql/odb/pgsql/binding.hxx74
-rw-r--r--libodb-pgsql/odb/pgsql/buildfile155
-rw-r--r--libodb-pgsql/odb/pgsql/connection-factory.cxx159
-rw-r--r--libodb-pgsql/odb/pgsql/connection-factory.hxx134
-rw-r--r--libodb-pgsql/odb/pgsql/connection.cxx141
-rw-r--r--libodb-pgsql/odb/pgsql/connection.hxx183
-rw-r--r--libodb-pgsql/odb/pgsql/connection.ixx44
-rw-r--r--libodb-pgsql/odb/pgsql/container-statements.hxx425
-rw-r--r--libodb-pgsql/odb/pgsql/container-statements.txx154
-rw-r--r--libodb-pgsql/odb/pgsql/database.cxx424
-rw-r--r--libodb-pgsql/odb/pgsql/database.hxx527
-rw-r--r--libodb-pgsql/odb/pgsql/database.ixx638
-rw-r--r--libodb-pgsql/odb/pgsql/details/.gitignore1
-rw-r--r--libodb-pgsql/odb/pgsql/details/build2/config-stub.h5
-rw-r--r--libodb-pgsql/odb/pgsql/details/build2/config-vc-stub.h5
-rw-r--r--libodb-pgsql/odb/pgsql/details/build2/config-vc.h15
-rw-r--r--libodb-pgsql/odb/pgsql/details/build2/config.h17
-rw-r--r--libodb-pgsql/odb/pgsql/details/config-vc.h5
-rw-r--r--libodb-pgsql/odb/pgsql/details/config.h.in12
-rw-r--r--libodb-pgsql/odb/pgsql/details/config.hxx21
-rw-r--r--libodb-pgsql/odb/pgsql/details/conversion.hxx58
-rw-r--r--libodb-pgsql/odb/pgsql/details/endian-traits.cxx30
-rw-r--r--libodb-pgsql/odb/pgsql/details/endian-traits.hxx155
-rw-r--r--libodb-pgsql/odb/pgsql/details/export.hxx78
-rw-r--r--libodb-pgsql/odb/pgsql/details/options.cli56
-rw-r--r--libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.cxx1114
-rw-r--r--libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.hxx562
-rw-r--r--libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.ixx372
-rw-r--r--libodb-pgsql/odb/pgsql/error.cxx86
-rw-r--r--libodb-pgsql/odb/pgsql/error.hxx41
-rw-r--r--libodb-pgsql/odb/pgsql/error.ixx31
-rw-r--r--libodb-pgsql/odb/pgsql/exceptions.cxx79
-rw-r--r--libodb-pgsql/odb/pgsql/exceptions.hxx80
-rw-r--r--libodb-pgsql/odb/pgsql/forward.hxx91
-rw-r--r--libodb-pgsql/odb/pgsql/no-id-object-result.hxx77
-rw-r--r--libodb-pgsql/odb/pgsql/no-id-object-result.txx114
-rw-r--r--libodb-pgsql/odb/pgsql/no-id-object-statements.hxx147
-rw-r--r--libodb-pgsql/odb/pgsql/no-id-object-statements.txx50
-rw-r--r--libodb-pgsql/odb/pgsql/pgsql-fwd.hxx19
-rw-r--r--libodb-pgsql/odb/pgsql/pgsql-oid.hxx47
-rw-r--r--libodb-pgsql/odb/pgsql/pgsql-types.hxx59
-rw-r--r--libodb-pgsql/odb/pgsql/polymorphic-object-result.hxx94
-rw-r--r--libodb-pgsql/odb/pgsql/polymorphic-object-result.txx287
-rw-r--r--libodb-pgsql/odb/pgsql/polymorphic-object-statements.hxx513
-rw-r--r--libodb-pgsql/odb/pgsql/polymorphic-object-statements.txx158
-rw-r--r--libodb-pgsql/odb/pgsql/prepared-query.cxx15
-rw-r--r--libodb-pgsql/odb/pgsql/prepared-query.hxx34
-rw-r--r--libodb-pgsql/odb/pgsql/query-const-expr.cxx14
-rw-r--r--libodb-pgsql/odb/pgsql/query-dynamic.cxx157
-rw-r--r--libodb-pgsql/odb/pgsql/query-dynamic.hxx32
-rw-r--r--libodb-pgsql/odb/pgsql/query-dynamic.ixx26
-rw-r--r--libodb-pgsql/odb/pgsql/query-dynamic.txx20
-rw-r--r--libodb-pgsql/odb/pgsql/query.cxx444
-rw-r--r--libodb-pgsql/odb/pgsql/query.hxx2181
-rw-r--r--libodb-pgsql/odb/pgsql/query.ixx34
-rw-r--r--libodb-pgsql/odb/pgsql/query.txx168
-rw-r--r--libodb-pgsql/odb/pgsql/section-statements.hxx220
-rw-r--r--libodb-pgsql/odb/pgsql/section-statements.txx51
-rw-r--r--libodb-pgsql/odb/pgsql/simple-object-result.hxx85
-rw-r--r--libodb-pgsql/odb/pgsql/simple-object-result.txx158
-rw-r--r--libodb-pgsql/odb/pgsql/simple-object-statements.cxx15
-rw-r--r--libodb-pgsql/odb/pgsql/simple-object-statements.hxx657
-rw-r--r--libodb-pgsql/odb/pgsql/simple-object-statements.ixx68
-rw-r--r--libodb-pgsql/odb/pgsql/simple-object-statements.txx190
-rw-r--r--libodb-pgsql/odb/pgsql/statement-cache.hxx57
-rw-r--r--libodb-pgsql/odb/pgsql/statement-cache.txx60
-rw-r--r--libodb-pgsql/odb/pgsql/statement.cxx1545
-rw-r--r--libodb-pgsql/odb/pgsql/statement.hxx450
-rw-r--r--libodb-pgsql/odb/pgsql/statements-base.cxx15
-rw-r--r--libodb-pgsql/odb/pgsql/statements-base.hxx63
-rw-r--r--libodb-pgsql/odb/pgsql/tracer.cxx60
-rw-r--r--libodb-pgsql/odb/pgsql/tracer.hxx61
-rw-r--r--libodb-pgsql/odb/pgsql/traits-calls.hxx214
-rw-r--r--libodb-pgsql/odb/pgsql/traits.cxx143
-rw-r--r--libodb-pgsql/odb/pgsql/traits.hxx944
-rw-r--r--libodb-pgsql/odb/pgsql/transaction-impl.cxx107
-rw-r--r--libodb-pgsql/odb/pgsql/transaction-impl.hxx49
-rw-r--r--libodb-pgsql/odb/pgsql/transaction.cxx26
-rw-r--r--libodb-pgsql/odb/pgsql/transaction.hxx88
-rw-r--r--libodb-pgsql/odb/pgsql/transaction.ixx57
-rw-r--r--libodb-pgsql/odb/pgsql/version-build2-stub.hxx4
-rw-r--r--libodb-pgsql/odb/pgsql/version-build2.hxx0
-rw-r--r--libodb-pgsql/odb/pgsql/version-build2.hxx.in42
-rw-r--r--libodb-pgsql/odb/pgsql/version.hxx48
-rw-r--r--libodb-pgsql/odb/pgsql/view-result.hxx77
-rw-r--r--libodb-pgsql/odb/pgsql/view-result.txx114
-rw-r--r--libodb-pgsql/odb/pgsql/view-statements.hxx88
-rw-r--r--libodb-pgsql/odb/pgsql/view-statements.txx33
-rw-r--r--libodb-pgsql/tests/.gitignore1
-rw-r--r--libodb-pgsql/tests/basics/buildfile6
-rw-r--r--libodb-pgsql/tests/basics/driver.cxx37
-rw-r--r--libodb-pgsql/tests/build/.gitignore3
-rw-r--r--libodb-pgsql/tests/build/bootstrap.build8
-rw-r--r--libodb-pgsql/tests/build/root.build23
-rw-r--r--libodb-pgsql/tests/buildfile4
108 files changed, 17131 insertions, 0 deletions
diff --git a/libodb-pgsql/.gitignore b/libodb-pgsql/.gitignore
new file mode 100644
index 0000000..1c363a0
--- /dev/null
+++ b/libodb-pgsql/.gitignore
@@ -0,0 +1,25 @@
+# Compiler/linker output.
+#
+*.d
+*.t
+*.i
+*.i.*
+*.ii
+*.ii.*
+*.o
+*.obj
+*.gcm
+*.pcm
+*.ifc
+*.so
+*.dylib
+*.dll
+*.a
+*.lib
+*.exp
+*.pdb
+*.ilk
+*.exe
+*.exe.dlls/
+*.exe.manifest
+*.pc
diff --git a/libodb-pgsql/GPLv2 b/libodb-pgsql/GPLv2
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/libodb-pgsql/GPLv2
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/libodb-pgsql/INSTALL b/libodb-pgsql/INSTALL
new file mode 100644
index 0000000..32df42c
--- /dev/null
+++ b/libodb-pgsql/INSTALL
@@ -0,0 +1,6 @@
+The easiest way to build this package is with the bpkg package manager:
+
+$ bpkg build libodb-pgsql
+
+But if you don't want to use the package manager, then you can also build it
+manually using the standard build2 build system.
diff --git a/libodb-pgsql/LICENSE b/libodb-pgsql/LICENSE
new file mode 100644
index 0000000..9d92da1
--- /dev/null
+++ b/libodb-pgsql/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-pgsql/README b/libodb-pgsql/README
new file mode 100644
index 0000000..c4c3e28
--- /dev/null
+++ b/libodb-pgsql/README
@@ -0,0 +1,20 @@
+ODB is an object-relational mapping (ORM) system for C++. It provides
+tools, APIs, and library support that allow you to persist C++ objects
+to a relational database (RDBMS) without having to deal with tables,
+columns, or SQL and without manually writing any of the mapping code.
+For more information see:
+
+http://www.codesynthesis.com/products/odb/
+
+This package contains the PostgreSQL ODB runtime library. Every
+application that includes code generated for the PostgreSQL database
+will need to link to this library.
+
+See the NEWS file for the user-visible changes from the previous release.
+
+See the LICENSE file for distribution conditions.
+
+See the INSTALL file for prerequisites and installation instructions.
+
+Send questions, bug reports, or any other feedback to the
+odb-users@codesynthesis.com mailing list.
diff --git a/libodb-pgsql/build/.gitignore b/libodb-pgsql/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-pgsql/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-pgsql/build/bootstrap.build b/libodb-pgsql/build/bootstrap.build
new file mode 100644
index 0000000..fb6b7df
--- /dev/null
+++ b/libodb-pgsql/build/bootstrap.build
@@ -0,0 +1,10 @@
+# file : build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = libodb-pgsql
+
+using version
+using config
+using dist
+using test
+using install
diff --git a/libodb-pgsql/build/export.build b/libodb-pgsql/build/export.build
new file mode 100644
index 0000000..80aba23
--- /dev/null
+++ b/libodb-pgsql/build/export.build
@@ -0,0 +1,9 @@
+# file : build/export.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+$out_root/
+{
+ include odb/pgsql/
+}
+
+export $out_root/odb/pgsql/lib{odb-pgsql}
diff --git a/libodb-pgsql/build/root.build b/libodb-pgsql/build/root.build
new file mode 100644
index 0000000..329cc45
--- /dev/null
+++ b/libodb-pgsql/build/root.build
@@ -0,0 +1,19 @@
+# file : build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+config [bool] config.libodb_pgsql.develop ?= false
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+ixx{*}: extension = ixx
+txx{*}: extension = txx
+cxx{*}: extension = cxx
+
+if ($cxx.target.system == 'win32-msvc')
+ cxx.poptions += -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS
+
+if ($cxx.class == 'msvc')
+ cxx.coptions += /wd4251 /wd4275 /wd4800
diff --git a/libodb-pgsql/buildfile b/libodb-pgsql/buildfile
new file mode 100644
index 0000000..a04e206
--- /dev/null
+++ b/libodb-pgsql/buildfile
@@ -0,0 +1,9 @@
+# file : buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/} doc{INSTALL NEWS README} legal{GPLv2 LICENSE} manifest
+
+# Don't install tests or the INSTALL file.
+#
+tests/: install = false
+doc{INSTALL}@./: install = false
diff --git a/libodb-pgsql/manifest b/libodb-pgsql/manifest
new file mode 100644
index 0000000..83b1a10
--- /dev/null
+++ b/libodb-pgsql/manifest
@@ -0,0 +1,23 @@
+: 1
+name: libodb-pgsql
+version: 2.5.0-b.26.z
+project: odb
+summary: PostgreSQL ODB runtime library
+license: GPL-2.0-only
+license: other: proprietary ; Not free/open source.
+topics: C++, ORM, PostgreSQL, SQL
+description-file: README
+changes-file: NEWS
+url: https://www.codesynthesis.com/products/odb/
+doc-url: https://www.codesynthesis.com/products/odb/doc/manual.xhtml
+src-url: https://git.codesynthesis.com/cgit/odb/odb/
+email: odb-users@codesynthesis.com
+build-warning-email: odb-builds@codesynthesis.com
+builds: all
+builds: -wasm
+requires: c++11
+depends: * build2 >= 0.16.0-
+depends: * bpkg >= 0.16.0-
+depends: libpq >=7.4.0
+depends: libodb == $
+depends: * cli ^1.2.0- ? ($config.libodb_pgsql.develop)
diff --git a/libodb-pgsql/odb/pgsql/auto-handle.cxx b/libodb-pgsql/odb/pgsql/auto-handle.cxx
new file mode 100644
index 0000000..d6c0565
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/auto-handle.cxx
@@ -0,0 +1,24 @@
+// file : odb/pgsql/auto-handle.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <libpq-fe.h>
+
+#include <odb/pgsql/auto-handle.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ void handle_traits<PGconn>::
+ release (PGconn* h)
+ {
+ PQfinish (h);
+ }
+
+ void handle_traits<PGresult>::
+ release (PGresult* h)
+ {
+ PQclear (h);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/auto-handle.hxx b/libodb-pgsql/odb/pgsql/auto-handle.hxx
new file mode 100644
index 0000000..49b396d
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/auto-handle.hxx
@@ -0,0 +1,90 @@
+// file : odb/pgsql/auto-handle.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_AUTO_HANDLE_HXX
+#define ODB_PGSQL_AUTO_HANDLE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/pgsql-fwd.hxx> // PGconn, PGresult
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename H>
+ struct handle_traits;
+
+ template <>
+ struct LIBODB_PGSQL_EXPORT handle_traits<PGconn>
+ {
+ static void
+ release (PGconn*);
+ };
+
+ template <>
+ struct LIBODB_PGSQL_EXPORT handle_traits<PGresult>
+ {
+ static void
+ release (PGresult*);
+ };
+
+ template <typename H>
+ class auto_handle
+ {
+ public:
+ auto_handle (H* h = 0)
+ : h_ (h)
+ {
+ }
+
+ ~auto_handle ()
+ {
+ if (h_ != 0)
+ handle_traits<H>::release (h_);
+ }
+
+ H*
+ get () const
+ {
+ return h_;
+ }
+
+ void
+ reset (H* h = 0)
+ {
+ if (h_ != 0)
+ handle_traits<H>::release (h_);
+
+ h_ = h;
+ }
+
+ H*
+ release ()
+ {
+ H* h (h_);
+ h_ = 0;
+ return h;
+ }
+
+ operator H* () const
+ {
+ return h_;
+ }
+
+ private:
+ auto_handle (const auto_handle&);
+ auto_handle& operator= (const auto_handle&);
+
+ private:
+ H* h_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_AUTO_HANDLE_HXX
diff --git a/libodb-pgsql/odb/pgsql/binding.hxx b/libodb-pgsql/odb/pgsql/binding.hxx
new file mode 100644
index 0000000..1adf144
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/binding.hxx
@@ -0,0 +1,74 @@
+// file : odb/pgsql/binding.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_BINDING_HXX
+#define ODB_PGSQL_BINDING_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/pgsql-types.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class native_binding
+ {
+ public:
+ native_binding (char** v,
+ int* l,
+ int* f,
+ std::size_t n)
+ : values (v), lengths (l), formats (f), count (n)
+ {
+ }
+
+ char** values;
+ int* lengths;
+ int* formats;
+ std::size_t count;
+
+ private:
+ native_binding (const native_binding&);
+ native_binding& operator= (const native_binding&);
+ };
+
+ class binding
+ {
+ public:
+ typedef pgsql::bind bind_type;
+
+ binding ()
+ : bind (0), count (0), version (0),
+ batch (0), skip (0), status (0) {}
+
+ binding (bind_type* b, std::size_t n)
+ : bind (b), count (n), version (0),
+ batch (0), skip (0), status (0) {}
+
+ binding (bind_type* b, std::size_t n,
+ std::size_t bt, std::size_t s, unsigned long long* st)
+ : bind (b), count (n), version (0),
+ batch (bt), skip (s), status (st) {}
+
+ bind_type* bind;
+ std::size_t count;
+ std::size_t version;
+
+ std::size_t batch;
+ std::size_t skip;
+ unsigned long long* status; // Batch status array.
+
+ private:
+ binding (const binding&);
+ binding& operator= (const binding&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_BINDING_HXX
diff --git a/libodb-pgsql/odb/pgsql/buildfile b/libodb-pgsql/odb/pgsql/buildfile
new file mode 100644
index 0000000..f1d05b6
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/buildfile
@@ -0,0 +1,155 @@
+# file : odb/pgsql/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+define cli: file
+cli{*}: extension = cli
+
+import int_libs = libodb%lib{odb}
+import imp_libs = libpq%lib{pq}
+
+lib{odb-pgsql}: {hxx ixx txx cxx}{* -version-build2} {hxx}{version-build2} \
+ details/{hxx ixx txx cxx}{* -options} \
+ details/build2/{h}{*} \
+ $imp_libs $int_libs
+
+# Include the generated version header into the distribution (so that we don't
+# pick up an installed one) and don't remove it when cleaning in src (so that
+# clean results in a state identical to distributed).
+#
+hxx{version-build2}: in{version-build2} $src_root/manifest
+hxx{version-build2}:
+{
+ dist = true
+ clean = ($src_root != $out_root)
+}
+
+# Build options.
+#
+cxx.poptions =+ "-I$out_root" "-I$src_root" -DLIBODB_PGSQL_BUILD2
+
+obja{*}: cxx.poptions += -DLIBODB_PGSQL_STATIC_BUILD
+objs{*}: cxx.poptions += -DLIBODB_PGSQL_SHARED_BUILD
+
+# Export options.
+#
+lib{odb-pgsql}:
+{
+ cxx.export.poptions = "-I$out_root" "-I$src_root" -DLIBODB_PGSQL_BUILD2
+ cxx.export.libs = $int_libs
+}
+
+liba{odb-pgsql}: cxx.export.poptions += -DLIBODB_PGSQL_STATIC
+libs{odb-pgsql}: cxx.export.poptions += -DLIBODB_PGSQL_SHARED
+
+# For pre-releases use the complete version to make sure they cannot be used
+# in place of another pre-release or the final version. See the version module
+# for details on the version.* variable values.
+#
+if $version.pre_release
+ lib{odb-pgsql}: bin.lib.version = @"-$version.project_id"
+else
+ lib{odb-pgsql}: bin.lib.version = @"-$version.major.$version.minor"
+
+develop = $config.libodb_pgsql.develop
+
+## Consumption build ($develop == false).
+#
+
+# Use pregenerated versions in the consumption build.
+#
+lib{odb-pgsql}: details/pregenerated/{hxx ixx cxx}{**}: include = (!$develop)
+
+if! $develop
+ cxx.poptions =+ "-I($src_base/details/pregenerated)" # Note: must come first.
+
+# Don't install pregenerated headers since they are only used internally in
+# the database implementation (also below).
+#
+details/pregenerated/{hxx ixx}{*}: install = false
+
+# Distribute pregenerated versions only in the consumption build.
+#
+details/pregenerated/{hxx ixx cxx}{*}: dist = (!$develop)
+
+#
+##
+
+## Development build ($develop == true).
+#
+
+lib{odb-pgsql}: details/{hxx ixx cxx}{options}: include = $develop
+
+if $develop
+ import! [metadata] cli = cli%exe{cli}
+
+# In the development build distribute regenerated {hxx ixx cxx}{options},
+# remapping their locations to the paths of the pregenerated versions (which
+# are only distributed in the consumption build; see above). This way we make
+# sure that the distributed files are always up-to-date.
+#
+<details/{hxx ixx cxx}{options}>: details/cli{options} $cli
+{
+ install = false
+ dist = ($develop ? pregenerated/odb/pgsql/details/ : false)
+
+ # Symlink the generated code in src for convenience of development.
+ #
+ backlink = true
+}
+%
+if $develop
+{{
+ options = --include-with-brackets --include-prefix odb/pgsql/details \
+ --guard-prefix LIBODB_PGSQL_DETAILS --generate-file-scanner \
+ --cli-namespace odb::pgsql::details::cli --long-usage \
+ --generate-specifier --no-combined-flags
+
+ $cli $options -o $out_base/details/ $path($<[0])
+
+ # If the result differs from the pregenerated version, copy it over.
+ #
+ d = [dir_path] $src_base/details/pregenerated/odb/pgsql/details/
+
+ if diff $d/options.hxx $path($>[0]) >- && \
+ diff $d/options.ixx $path($>[1]) >- && \
+ diff $d/options.cxx $path($>[2]) >-
+ exit
+ end
+
+ cp $path($>[0]) $d/options.hxx
+ cp $path($>[1]) $d/options.ixx
+ cp $path($>[2]) $d/options.cxx
+}}
+
+# Install into the odb/pgsql/ subdirectory of, say, /usr/include/
+# recreating subdirectories.
+#
+install_include = [dir_path] include/odb/pgsql/
+
+{hxx ixx txx}{*}:
+{
+ install = $install_include
+ install.subdirs = true
+}
+
+# We want these to be picked up whether LIBODB_PGSQL_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-pgsql/odb/pgsql/connection-factory.cxx b/libodb-pgsql/odb/pgsql/connection-factory.cxx
new file mode 100644
index 0000000..855a9bc
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/connection-factory.cxx
@@ -0,0 +1,159 @@
+// file : odb/pgsql/connection-factory.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/connection-factory.hxx>
+#include <odb/pgsql/exceptions.hxx>
+
+#include <odb/details/lock.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ using namespace details;
+
+ namespace pgsql
+ {
+ // new_connection_factory
+ //
+ connection_ptr new_connection_factory::
+ connect ()
+ {
+ return connection_ptr (new (shared) connection (*this));
+ }
+
+ // connection_pool_factory
+ //
+ connection_pool_factory::pooled_connection_ptr connection_pool_factory::
+ create ()
+ {
+ return pooled_connection_ptr (new (shared) pooled_connection (*this));
+ }
+
+ connection_pool_factory::
+ ~connection_pool_factory ()
+ {
+ // Wait for all the connections currently in use to return to
+ // the pool.
+ //
+ lock l (mutex_);
+ while (in_use_ != 0)
+ {
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ connection_ptr connection_pool_factory::
+ connect ()
+ {
+ lock l (mutex_);
+
+ while (true)
+ {
+ // See if we have a spare connection.
+ //
+ if (connections_.size () != 0)
+ {
+ shared_ptr<pooled_connection> c (connections_.back ());
+ connections_.pop_back ();
+
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // See if we can create a new one.
+ //
+ if (max_ == 0 || in_use_ < max_)
+ {
+ shared_ptr<pooled_connection> c (create ());
+ c->callback_ = &c->cb_;
+ in_use_++;
+ return c;
+ }
+
+ // Wait until someone releases a connection.
+ //
+ waiters_++;
+ cond_.wait (l);
+ waiters_--;
+ }
+ }
+
+ void connection_pool_factory::
+ database (database_type& db)
+ {
+ bool first (db_ == 0);
+
+ connection_factory::database (db);
+
+ if (!first)
+ return;
+
+ if (min_ > 0)
+ {
+ connections_.reserve (min_);
+
+ for (size_t i (0); i < min_; ++i)
+ connections_.push_back (create ());
+ }
+ }
+
+ bool connection_pool_factory::
+ release (pooled_connection* c)
+ {
+ c->callback_ = 0;
+
+ lock l (mutex_);
+
+ // Determine if we need to keep or free this connection.
+ //
+ bool keep (!c->failed () &&
+ (waiters_ != 0 ||
+ min_ == 0 ||
+ (connections_.size () + in_use_ <= min_)));
+
+ in_use_--;
+
+ if (keep)
+ {
+ connections_.push_back (pooled_connection_ptr (inc_ref (c)));
+ connections_.back ()->recycle ();
+ }
+
+ if (waiters_ != 0)
+ cond_.signal ();
+
+ return !keep;
+ }
+
+ //
+ // connection_pool_factory::pooled_connection
+ //
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f)
+ : connection (f)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ connection_pool_factory::pooled_connection::
+ pooled_connection (connection_pool_factory& f, PGconn* handle)
+ : connection (f, handle)
+ {
+ cb_.arg = this;
+ cb_.zero_counter = &zero_counter;
+ }
+
+ bool connection_pool_factory::pooled_connection::
+ zero_counter (void* arg)
+ {
+ pooled_connection* c (static_cast<pooled_connection*> (arg));
+ return static_cast<connection_pool_factory&> (c->factory_).release (c);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/connection-factory.hxx b/libodb-pgsql/odb/pgsql/connection-factory.hxx
new file mode 100644
index 0000000..2f300a8
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/connection-factory.hxx
@@ -0,0 +1,134 @@
+// file : odb/pgsql/connection-factory.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_CONNECTION_FACTORY_HXX
+#define ODB_PGSQL_CONNECTION_FACTORY_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/details/mutex.hxx>
+#include <odb/details/condition.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/connection.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class LIBODB_PGSQL_EXPORT new_connection_factory: public connection_factory
+ {
+ public:
+ new_connection_factory () {}
+
+ virtual connection_ptr
+ connect ();
+
+ private:
+ new_connection_factory (const new_connection_factory&);
+ new_connection_factory& operator= (const new_connection_factory&);
+ };
+
+ class LIBODB_PGSQL_EXPORT connection_pool_factory:
+ public connection_factory
+ {
+ public:
+ // The max_connections argument specifies the maximum number of
+ // concurrent connections this pool will maintain. If this value
+ // is 0 then the pool will create a new connection every time all
+ // of the existing connections are in use.
+ //
+ // The min_connections argument specifies the minimum number of
+ // connections that should be maintained by the pool. If the
+ // number of connections maintained by the pool exceeds this
+ // number and there are no active waiters for a new connection,
+ // then the pool will release the excess connections. If this
+ // value is 0 then the pool will maintain all the connections
+ // that were ever created.
+ //
+ connection_pool_factory (std::size_t max_connections = 0,
+ std::size_t min_connections = 0)
+ : max_ (max_connections),
+ min_ (min_connections),
+ in_use_ (0),
+ waiters_ (0),
+ cond_ (mutex_)
+ {
+ // max_connections == 0 means unlimited.
+ //
+ assert (max_connections == 0 || max_connections >= min_connections);
+ }
+
+ virtual connection_ptr
+ connect ();
+
+ virtual void
+ database (database_type&);
+
+ virtual
+ ~connection_pool_factory ();
+
+ private:
+ connection_pool_factory (const connection_pool_factory&);
+ connection_pool_factory& operator= (const connection_pool_factory&);
+
+ protected:
+ class LIBODB_PGSQL_EXPORT pooled_connection: public connection
+ {
+ public:
+ pooled_connection (connection_pool_factory&);
+ pooled_connection (connection_pool_factory&, PGconn*);
+
+ private:
+ static bool
+ zero_counter (void*);
+
+ private:
+ friend class connection_pool_factory;
+
+ shared_base::refcount_callback cb_;
+ };
+
+ friend class pooled_connection;
+
+ typedef details::shared_ptr<pooled_connection> pooled_connection_ptr;
+ typedef std::vector<pooled_connection_ptr> connections;
+
+ // This function is called whenever the pool needs to create a new
+ // connection.
+ //
+ virtual pooled_connection_ptr
+ create ();
+
+ protected:
+ // Return true if the connection should be deleted, false otherwise.
+ //
+ bool
+ release (pooled_connection*);
+
+ protected:
+ const std::size_t max_;
+ const std::size_t min_;
+
+ std::size_t in_use_; // Number of connections currently in use.
+ std::size_t waiters_; // Number of threads waiting for a connection.
+
+ connections connections_;
+
+ details::mutex mutex_;
+ details::condition cond_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_CONNECTION_FACTORY_HXX
diff --git a/libodb-pgsql/odb/pgsql/connection.cxx b/libodb-pgsql/odb/pgsql/connection.cxx
new file mode 100644
index 0000000..80a1dd2
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/connection.cxx
@@ -0,0 +1,141 @@
+// file : odb/pgsql/connection.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <new> // std::bad_alloc
+#include <string>
+#include <cstring> // std::strcmp
+#include <cstdlib> // std::atol
+
+#include <libpq-fe.h>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/transaction.hxx>
+#include <odb/pgsql/error.hxx>
+#include <odb/pgsql/exceptions.hxx>
+#include <odb/pgsql/statement-cache.hxx>
+
+using namespace std;
+
+extern "C" void
+odb_pgsql_process_notice (void*, const char*)
+{
+}
+
+namespace odb
+{
+ namespace pgsql
+ {
+ connection::
+ connection (connection_factory& cf)
+ : odb::connection (cf), failed_ (false)
+ {
+ database_type& db (database ());
+ handle_.reset (PQconnectdb (db.conninfo ().c_str ()));
+
+ if (handle_ == 0)
+ throw bad_alloc ();
+ else if (PQstatus (handle_) == CONNECTION_BAD)
+ throw database_exception (PQerrorMessage (handle_));
+
+ init ();
+ }
+
+ connection::
+ connection (connection_factory& cf, PGconn* handle)
+ : odb::connection (cf), handle_ (handle), failed_ (false)
+ {
+ init ();
+ }
+
+ void connection::
+ init ()
+ {
+ // Establish whether date/time values are represented as
+ // 8-byte integers.
+ //
+ if (strcmp (PQparameterStatus (handle_, "integer_datetimes"), "on") != 0)
+ throw database_exception ("unsupported binary format for PostgreSQL "
+ "date-time SQL types");
+
+ // Suppress server notifications to stdout.
+ //
+ PQsetNoticeProcessor (handle_, &odb_pgsql_process_notice, 0);
+
+ // Create statement cache.
+ //
+ statement_cache_.reset (new statement_cache_type (*this));
+ }
+
+ connection::
+ ~connection ()
+ {
+ // Destroy prepared query statements before freeing the connections.
+ //
+ recycle ();
+ clear_prepared_map ();
+ }
+
+ int connection::
+ server_version () const
+ {
+ return PQserverVersion (handle_);
+ }
+
+ transaction_impl* connection::
+ begin ()
+ {
+ return new transaction_impl (connection_ptr (inc_ref (this)));
+ }
+
+ unsigned long long connection::
+ execute (const char* s, std::size_t n)
+ {
+ // The string may not be '\0'-terminated.
+ //
+ string str (s, n);
+
+ {
+ odb::tracer* t;
+ if ((t = transaction_tracer ()) ||
+ (t = tracer ()) ||
+ (t = database ().tracer ()))
+ t->execute (*this, str.c_str ());
+ }
+
+ auto_handle<PGresult> h (PQexec (handle_, str.c_str ()));
+
+ unsigned long long count (0);
+
+ if (!is_good_result (h))
+ translate_error (*this, h);
+ else if (PGRES_TUPLES_OK == PQresultStatus (h))
+ count = static_cast<unsigned long long> (PQntuples (h));
+ else
+ {
+ const char* s (PQcmdTuples (h));
+
+ if (s[0] != '\0' && s[1] == '\0')
+ count = static_cast<unsigned long long> (s[0] - '0');
+ else
+ count = static_cast<unsigned long long> (atol (s));
+ }
+
+ return count;
+ }
+
+ // connection_factory
+ //
+ connection_factory::
+ ~connection_factory ()
+ {
+ }
+
+ void connection_factory::
+ database (database_type& db)
+ {
+ odb::connection_factory::db_ = &db;
+ db_ = &db;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/connection.hxx b/libodb-pgsql/odb/pgsql/connection.hxx
new file mode 100644
index 0000000..d779273
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/connection.hxx
@@ -0,0 +1,183 @@
+// file : odb/pgsql/connection.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_CONNECTION_HXX
+#define ODB_PGSQL_CONNECTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/connection.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/unique-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/query.hxx>
+#include <odb/pgsql/tracer.hxx>
+#include <odb/pgsql/transaction-impl.hxx>
+#include <odb/pgsql/auto-handle.hxx>
+#include <odb/pgsql/pgsql-fwd.hxx> // PGconn
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class statement_cache;
+ class connection_factory;
+
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+
+ class LIBODB_PGSQL_EXPORT connection: public odb::connection
+ {
+ public:
+ typedef pgsql::statement_cache statement_cache_type;
+ typedef pgsql::database database_type;
+
+ virtual
+ ~connection ();
+
+ connection (connection_factory&);
+ connection (connection_factory&, PGconn* handle);
+
+ database_type&
+ database ();
+
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ public:
+ using odb::connection::execute;
+
+ virtual unsigned long long
+ execute (const char* statement, std::size_t length);
+
+ // Query preparation.
+ //
+ public:
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const pgsql::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef pgsql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::connection::tracer (t);
+ }
+
+ using odb::connection::tracer;
+
+ public:
+ bool
+ failed () const
+ {
+ return failed_;
+ }
+
+ void
+ mark_failed ()
+ {
+ failed_ = true;
+ }
+
+ public:
+ PGconn*
+ handle ()
+ {
+ return handle_;
+ }
+
+ // Server version as returned by PQserverVersion(), for example, 90200
+ // (9.2.0), 90201 (9.2.1), 100000 (10.0), 110001 (11.1).
+ //
+ int
+ server_version () const;
+
+ statement_cache_type&
+ statement_cache ()
+ {
+ return *statement_cache_;
+ }
+
+ private:
+ connection (const connection&);
+ connection& operator= (const connection&);
+
+ private:
+ void
+ init ();
+
+ private:
+ friend class transaction_impl; // invalidate_results()
+
+ private:
+ auto_handle<PGconn> handle_;
+ bool failed_;
+
+ // Keep statement_cache_ after handle_ so that it is destroyed before
+ // the connection is closed.
+ //
+ details::unique_ptr<statement_cache_type> statement_cache_;
+ };
+
+ class LIBODB_PGSQL_EXPORT connection_factory:
+ public odb::connection_factory
+ {
+ public:
+ typedef pgsql::database database_type;
+
+ virtual void
+ database (database_type&);
+
+ database_type&
+ database () {return *db_;}
+
+ virtual connection_ptr
+ connect () = 0;
+
+ virtual
+ ~connection_factory ();
+
+ connection_factory (): db_ (0) {}
+
+ // Needed to break the circular connection_factory-database dependency
+ // (odb::connection_factory has the odb::database member).
+ //
+ protected:
+ database_type* db_;
+ };
+ }
+}
+
+#include <odb/pgsql/connection.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_CONNECTION_HXX
diff --git a/libodb-pgsql/odb/pgsql/connection.ixx b/libodb-pgsql/odb/pgsql/connection.ixx
new file mode 100644
index 0000000..76d558b
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/connection.ixx
@@ -0,0 +1,44 @@
+// file : odb/pgsql/connection.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace pgsql
+ {
+ inline database& connection::
+ database ()
+ {
+ return static_cast<connection_factory&> (factory_).database ();
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const pgsql::query_base& q)
+ {
+ return query_<T, id_pgsql>::call (*this, n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, pgsql::query_base (q));
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/container-statements.hxx b/libodb-pgsql/odb/pgsql/container-statements.hxx
new file mode 100644
index 0000000..1856bb9
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/container-statements.hxx
@@ -0,0 +1,425 @@
+// file : odb/pgsql/container-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_CONTAINER_STATEMENTS_HXX
+#define ODB_PGSQL_CONTAINER_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/pgsql-fwd.hxx> // Oid
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class connection;
+
+ // Template argument is the generated abstract container traits type.
+ // That is, it doesn't need to provide column counts and statements.
+ //
+ template <typename T>
+ class container_statements
+ {
+ public:
+ typedef T traits;
+
+ typedef typename traits::data_image_type data_image_type;
+ typedef typename traits::functions_type functions_type;
+
+ typedef pgsql::insert_statement insert_statement_type;
+ typedef pgsql::select_statement select_statement_type;
+ typedef pgsql::delete_statement delete_statement_type;
+
+ typedef pgsql::connection connection_type;
+
+ container_statements (connection_type&,
+ binding& id,
+ native_binding& idn,
+ const Oid* idt);
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Functions.
+ //
+ functions_type&
+ functions ()
+ {
+ return functions_;
+ }
+
+ // Schema version.
+ //
+ const schema_version_migration&
+ version_migration () const {return *svm_;}
+
+ void
+ version_migration (const schema_version_migration& svm) {svm_ = &svm;}
+
+ // Id image binding (external).
+ //
+ const binding&
+ id_binding ()
+ {
+ return id_binding_;
+ }
+
+ // Data image. The image is split into the id (that comes as a
+ // binding) and index/key plus value which are in data_image_type.
+ // The select binding is a subset of the full binding (no id).
+ //
+ data_image_type&
+ data_image ()
+ {
+ return data_image_;
+ }
+
+ bind*
+ data_bind ()
+ {
+ return insert_image_binding_.bind;
+ }
+
+ bool
+ data_binding_test_version () const
+ {
+ return data_id_binding_version_ != id_binding_.version ||
+ data_image_version_ != data_image_.version ||
+ insert_image_binding_.version == 0;
+ }
+
+ void
+ data_binding_update_version ()
+ {
+ data_id_binding_version_ = id_binding_.version;
+ data_image_version_ = data_image_.version;
+ insert_image_binding_.version++;
+ select_image_binding_.version++;
+ }
+
+ bool*
+ select_image_truncated ()
+ {
+ return select_image_truncated_;
+ }
+
+ //
+ // Statements.
+ //
+
+ insert_statement_type&
+ insert_statement ()
+ {
+ if (insert_ == 0)
+ insert_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ insert_name_,
+ insert_text_,
+ versioned_, // Process if versioned.
+ insert_types_,
+ insert_count_,
+ insert_image_binding_,
+ insert_image_native_binding_,
+ 0,
+ false));
+
+ return *insert_;
+ }
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ select_name_,
+ select_text_,
+ versioned_, // Process if versioned.
+ false, // Don't optimize.
+ id_types_,
+ id_binding_.count,
+ id_binding_,
+ id_native_binding_,
+ select_image_binding_,
+ false));
+
+ return *select_;
+ }
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (delete_ == 0)
+ delete_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ delete_name_,
+ delete_text_,
+ id_types_,
+ id_binding_.count,
+ id_binding_,
+ id_native_binding_,
+ false));
+
+ return *delete_;
+ }
+
+ private:
+ container_statements (const container_statements&);
+ container_statements& operator= (const container_statements&);
+
+ protected:
+ connection_type& conn_;
+ binding& id_binding_;
+ native_binding& id_native_binding_;
+ const Oid* id_types_;
+
+ functions_type functions_;
+
+ data_image_type data_image_;
+ std::size_t data_image_version_;
+ std::size_t data_id_binding_version_;
+
+ binding insert_image_binding_;
+ native_binding insert_image_native_binding_;
+
+ binding select_image_binding_;
+ bool* select_image_truncated_;
+
+ const char* insert_name_;
+ const char* insert_text_;
+ const Oid* insert_types_;
+ std::size_t insert_count_;
+
+ const char* select_name_;
+ const char* select_text_;
+
+ const char* delete_name_;
+ const char* delete_text_;
+
+ bool versioned_;
+ const schema_version_migration* svm_;
+
+ details::shared_ptr<insert_statement_type> insert_;
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<delete_statement_type> delete_;
+ };
+
+ template <typename T>
+ class smart_container_statements: public container_statements<T>
+ {
+ public:
+ typedef T traits;
+ typedef typename traits::cond_image_type cond_image_type;
+
+ typedef pgsql::update_statement update_statement_type;
+ typedef pgsql::delete_statement delete_statement_type;
+
+ typedef pgsql::connection connection_type;
+
+ smart_container_statements (connection_type&,
+ binding& id,
+ native_binding& idn,
+ const Oid* idt);
+
+ // Condition image. The image is split into the id (that comes as
+ // a binding) and index/key/value which is in cond_image_type.
+ //
+ cond_image_type&
+ cond_image ()
+ {
+ return cond_image_;
+ }
+
+ bind*
+ cond_bind ()
+ {
+ return cond_image_binding_.bind;
+ }
+
+ bool
+ cond_binding_test_version () const
+ {
+ return cond_id_binding_version_ != this->id_binding_.version ||
+ cond_image_version_ != cond_image_.version ||
+ cond_image_binding_.version == 0;
+ }
+
+ void
+ cond_binding_update_version ()
+ {
+ cond_id_binding_version_ = this->id_binding_.version;
+ cond_image_version_ = cond_image_.version;
+ cond_image_binding_.version++;
+ }
+
+ // Update image. The image is split as follows: value comes
+ // from the data image, id comes as binding, and index/key
+ // comes from the condition image.
+ //
+ bind*
+ update_bind ()
+ {
+ return update_image_binding_.bind;
+ }
+
+ bool
+ update_binding_test_version () const
+ {
+ return update_id_binding_version_ != this->id_binding_.version ||
+ update_cond_image_version_ != cond_image_.version ||
+ update_data_image_version_ != this->data_image_.version ||
+ update_image_binding_.version == 0;
+ }
+
+ void
+ update_binding_update_version ()
+ {
+ update_id_binding_version_ = this->id_binding_.version;
+ update_cond_image_version_ = cond_image_.version;
+ update_data_image_version_ = this->data_image_.version;
+ update_image_binding_.version++;
+ }
+
+ //
+ // Statements.
+ //
+
+ delete_statement_type&
+ delete_statement ()
+ {
+ if (this->delete_ == 0)
+ this->delete_.reset (
+ new (details::shared) delete_statement_type (
+ this->conn_,
+ this->delete_name_,
+ this->delete_text_,
+ delete_types_,
+ delete_count_,
+ cond_image_binding_,
+ cond_image_native_binding_,
+ false));
+
+ return *this->delete_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ this->conn_,
+ update_name_,
+ update_text_,
+ this->versioned_, // Process if versioned.
+ update_types_,
+ update_count_,
+ update_image_binding_,
+ update_image_native_binding_,
+ false));
+
+ return *update_;
+ }
+
+ protected:
+ cond_image_type cond_image_;
+ std::size_t cond_image_version_;
+ std::size_t cond_id_binding_version_;
+ binding cond_image_binding_;
+ native_binding cond_image_native_binding_;
+
+ std::size_t update_id_binding_version_;
+ std::size_t update_cond_image_version_;
+ std::size_t update_data_image_version_;
+ binding update_image_binding_;
+ native_binding update_image_native_binding_;
+
+ const char* update_name_;
+ const char* update_text_;
+ const Oid* update_types_;
+ std::size_t update_count_;
+
+ const Oid* delete_types_;
+ std::size_t delete_count_;
+
+ details::shared_ptr<update_statement_type> update_;
+ };
+
+ // Template argument is the generated concrete container traits type.
+ //
+ template <typename T>
+ class container_statements_impl: public T::statements_type
+ {
+ public:
+ typedef T traits;
+ typedef typename T::statements_type base;
+ typedef pgsql::connection connection_type;
+
+ container_statements_impl (connection_type&,
+ binding&,
+ native_binding&,
+ const Oid*);
+ private:
+ container_statements_impl (const container_statements_impl&);
+ container_statements_impl& operator= (const container_statements_impl&);
+
+ private:
+ bind data_image_bind_[traits::data_column_count];
+ char* data_image_values_[traits::data_column_count];
+ int data_image_lengths_[traits::data_column_count];
+ int data_image_formats_[traits::data_column_count];
+
+ bool select_image_truncated_array_[traits::data_column_count -
+ traits::id_column_count];
+ };
+
+ template <typename T>
+ class smart_container_statements_impl: public container_statements_impl<T>
+ {
+ public:
+ typedef T traits;
+ typedef pgsql::connection connection_type;
+
+ smart_container_statements_impl (connection_type&,
+ binding&,
+ native_binding&,
+ const Oid*);
+ private:
+ bind cond_image_bind_[traits::cond_column_count];
+ char* cond_image_values_[traits::cond_column_count];
+ int cond_image_lengths_[traits::cond_column_count];
+ int cond_image_formats_[traits::cond_column_count];
+
+ bind update_image_bind_[traits::value_column_count +
+ traits::cond_column_count];
+ char* update_image_values_[traits::value_column_count +
+ traits::cond_column_count];
+ int update_image_lengths_[traits::value_column_count +
+ traits::cond_column_count];
+ int update_image_formats_[traits::value_column_count +
+ traits::cond_column_count];
+ };
+ }
+}
+
+#include <odb/pgsql/container-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_CONTAINER_STATEMENTS_HXX
diff --git a/libodb-pgsql/odb/pgsql/container-statements.txx b/libodb-pgsql/odb/pgsql/container-statements.txx
new file mode 100644
index 0000000..34eb99f
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/container-statements.txx
@@ -0,0 +1,154 @@
+// file : odb/pgsql/container-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // container_statements
+ //
+ template <typename T>
+ container_statements<T>::
+ container_statements (connection_type& conn,
+ binding& id,
+ native_binding& idn,
+ const Oid* idt)
+ : conn_ (conn),
+ id_binding_ (id),
+ id_native_binding_ (idn),
+ id_types_ (idt),
+ functions_ (this),
+ insert_image_binding_ (0, 0), // Initialized by impl.
+ insert_image_native_binding_ (0, 0, 0, 0), // Initialized by impl.
+ select_image_binding_ (0, 0), // Initialized by impl.
+ svm_ (0)
+ {
+ functions_.insert_ = &traits::insert;
+ functions_.select_ = &traits::select;
+ functions_.delete__ = &traits::delete_;
+
+ data_image_.version = 0;
+ data_image_version_ = 0;
+ data_id_binding_version_ = 0;
+ }
+
+ // smart_container_statements
+ //
+ template <typename T>
+ smart_container_statements<T>::
+ smart_container_statements (connection_type& conn,
+ binding& id,
+ native_binding& idn,
+ const Oid* idt)
+ : container_statements<T> (conn, id, idn, idt),
+ cond_image_binding_ (0, 0), // Initialized by impl.
+ cond_image_native_binding_ (0, 0, 0, 0), // Initialized by impl.
+ update_image_binding_ (0, 0), // Initialized by impl.
+ update_image_native_binding_ (0, 0, 0, 0) // Initialized by impl.
+ {
+ this->functions_.update_ = &traits::update;
+
+ cond_image_.version = 0;
+ cond_image_version_ = 0;
+ cond_id_binding_version_ = 0;
+
+ update_id_binding_version_ = 0;
+ update_cond_image_version_ = 0;
+ update_data_image_version_ = 0;
+ }
+
+ // container_statements_impl
+ //
+ template <typename T>
+ container_statements_impl<T>::
+ container_statements_impl (connection_type& conn,
+ binding& id,
+ native_binding& idn,
+ const Oid* idt)
+ : base (conn, id, idn, idt)
+ {
+ this->select_image_truncated_ = select_image_truncated_array_;
+
+ this->insert_image_binding_.bind = data_image_bind_;
+ this->insert_image_binding_.count = traits::data_column_count;
+
+ this->select_image_binding_.bind = data_image_bind_ +
+ traits::id_column_count;
+ this->select_image_binding_.count = traits::data_column_count -
+ traits::id_column_count;
+
+ this->insert_image_native_binding_.values = data_image_values_;
+ this->insert_image_native_binding_.lengths = data_image_lengths_;
+ this->insert_image_native_binding_.formats = data_image_formats_;
+ this->insert_image_native_binding_.count = traits::data_column_count;
+
+ std::memset (data_image_bind_, 0, sizeof (data_image_bind_));
+ std::memset (select_image_truncated_array_,
+ 0,
+ sizeof (select_image_truncated_array_));
+
+ for (std::size_t i (0);
+ i < traits::data_column_count - traits::id_column_count;
+ ++i)
+ data_image_bind_[i + traits::id_column_count].truncated =
+ select_image_truncated_array_ + i;
+
+ this->insert_name_ = traits::insert_name;
+ this->insert_text_ = traits::insert_statement;
+ this->insert_types_ = traits::insert_types;
+ this->insert_count_ = traits::data_column_count;
+
+ this->select_name_ = traits::select_name;
+ this->select_text_ = traits::select_statement;
+
+ this->delete_name_ = traits::delete_name;
+ this->delete_text_ = traits::delete_statement;
+
+ this->versioned_ = traits::versioned;
+ }
+
+ // smart_container_statements_impl
+ //
+ template <typename T>
+ smart_container_statements_impl<T>::
+ smart_container_statements_impl (connection_type& conn,
+ binding& id,
+ native_binding& idn,
+ const Oid* idt)
+ : container_statements_impl<T> (conn, id, idn, idt)
+ {
+ this->cond_image_binding_.bind = cond_image_bind_;
+ this->cond_image_binding_.count = traits::cond_column_count;
+
+ this->update_image_binding_.bind = update_image_bind_;
+ this->update_image_binding_.count = traits::value_column_count +
+ traits::cond_column_count;
+
+ this->cond_image_native_binding_.values = cond_image_values_;
+ this->cond_image_native_binding_.lengths = cond_image_lengths_;
+ this->cond_image_native_binding_.formats = cond_image_formats_;
+ this->cond_image_native_binding_.count = traits::cond_column_count;
+
+ this->update_image_native_binding_.values = update_image_values_;
+ this->update_image_native_binding_.lengths = update_image_lengths_;
+ this->update_image_native_binding_.formats = update_image_formats_;
+ this->update_image_native_binding_.count = traits::value_column_count +
+ traits::cond_column_count;
+
+ std::memset (cond_image_bind_, 0, sizeof (cond_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+ this->update_name_ = traits::update_name;
+ this->update_text_ = traits::update_statement;
+ this->update_types_ = traits::update_types;
+ this->update_count_ = traits::value_column_count +
+ traits::cond_column_count;
+
+ this->delete_types_ = traits::delete_types;
+ this->delete_count_ = traits::cond_column_count;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/database.cxx b/libodb-pgsql/odb/pgsql/database.cxx
new file mode 100644
index 0000000..09bf6f0
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/database.cxx
@@ -0,0 +1,424 @@
+// file : odb/pgsql/database.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // strlen()
+#include <sstream>
+
+#include <odb/pgsql/traits.hxx>
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/connection-factory.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/transaction.hxx>
+#include <odb/pgsql/exceptions.hxx>
+
+#include <odb/pgsql/details/options.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ using odb::details::transfer_ptr;
+
+ database::
+ database (const string& user,
+ const string& password,
+ const string& db,
+ const string& host,
+ unsigned int port,
+ const string& extra_conninfo,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_pgsql),
+ user_ (user),
+ password_ (password),
+ db_ (db),
+ host_ (host),
+ port_ (port),
+ extra_conninfo_ (extra_conninfo),
+ factory_ (factory.transfer ())
+ {
+ ostringstream ss;
+
+ if (!user.empty ())
+ ss << "user='" << user << "' ";
+
+ if (!password.empty ())
+ ss << "password='" << password << "' ";
+
+ if (!db.empty ())
+ ss << "dbname='" << db << "' ";
+
+ if (!host.empty ())
+ ss << "host='" << host << "' ";
+
+ if (port)
+ ss << "port=" << port << " ";
+
+ // Only the last occurence of keyword/value pair is used by libpq.
+ // extra_conninfo specified options take precedence.
+ //
+ if (!extra_conninfo.empty ())
+ ss << extra_conninfo;
+
+ conninfo_ = ss.str ();
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const string& user,
+ const string& password,
+ const string& db,
+ const string& host,
+ const string& socket_ext,
+ const string& extra_conninfo,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_pgsql),
+ user_ (user),
+ password_ (password),
+ db_ (db),
+ host_ (host),
+ port_ (0),
+ socket_ext_ (socket_ext),
+ extra_conninfo_ (extra_conninfo),
+ factory_ (factory.transfer ())
+ {
+ ostringstream ss;
+
+ if (!user.empty ())
+ ss << "user='" << user << "' ";
+
+ if (!password.empty ())
+ ss << "password='" << password << "' ";
+
+ if (!db.empty ())
+ ss << "dbname='" << db << "' ";
+
+ if (!host.empty ())
+ ss << "host='" << host << "' ";
+
+ if (!socket_ext.empty ())
+ ss << "port='" << socket_ext << "' ";
+
+ // Only the last occurence of keyword/value pair is used by libpq.
+ // extra_conninfo specified options take precedence.
+ //
+ if (!extra_conninfo.empty ())
+ ss << extra_conninfo;
+
+ conninfo_ = ss.str ();
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (const string& conninfo, transfer_ptr<connection_factory> factory)
+ : odb::database (id_pgsql),
+ port_ (0),
+ conninfo_ (conninfo),
+ factory_ (factory.transfer ())
+ {
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ database::
+ database (int& argc,
+ char* argv[],
+ bool erase,
+ const string& extra_conninfo,
+ transfer_ptr<connection_factory> factory)
+ : odb::database (id_pgsql), port_ (0), factory_ (factory.transfer ())
+ {
+ using namespace details;
+
+ try
+ {
+ cli::argv_file_scanner scan (argc, argv, "--options-file", erase);
+ options ops (scan, cli::unknown_mode::skip, cli::unknown_mode::skip);
+
+ ostringstream oss;
+
+ if (ops.user_specified ())
+ {
+ user_ = ops.user ();
+ oss << "user='" << user_ << "' ";
+ }
+
+ if (ops.password_specified ())
+ {
+ password_ = ops.password ();
+ oss << "password='" << password_ << "' ";
+ }
+
+ if (ops.database_specified ())
+ {
+ db_ = ops.database ();
+ oss << "dbname='" << db_ << "' ";
+ }
+
+ if (ops.host_specified ())
+ {
+ host_ = ops.host ();
+ oss << "host='" << host_ << "' ";
+ }
+
+ if (ops.port_specified ())
+ {
+ istringstream iss (ops.port ());
+
+ if (iss >> port_ && iss.eof ())
+ oss << " port=" << port_ << " ";
+ else
+ {
+ port_ = 0;
+ socket_ext_ = ops.port ();
+ oss << "port='" << socket_ext_ << "' ";
+ }
+ }
+
+ if (!extra_conninfo.empty ())
+ oss << extra_conninfo;
+
+ conninfo_ = oss.str ();
+ }
+ catch (const cli::exception& e)
+ {
+ ostringstream oss;
+ oss << e;
+ throw cli_exception (oss.str ());
+ }
+
+ if (!factory_)
+ factory_.reset (new connection_pool_factory ());
+
+ factory_->database (*this);
+ }
+
+ void database::
+ print_usage (std::ostream& os)
+ {
+ details::options::print_usage (os);
+ }
+
+ database::
+ ~database ()
+ {
+ }
+
+ transaction_impl* database::
+ begin ()
+ {
+ return new transaction_impl (*this);
+ }
+
+ odb::connection* database::
+ connection_ ()
+ {
+ connection_ptr c (factory_->connect ());
+ return c.release ();
+ }
+
+ const database::schema_version_info& database::
+ load_schema_version (const string& name) const
+ {
+ schema_version_info& svi (schema_version_map_[name]);
+
+ // Quoted table name.
+ //
+ const char* table (
+ !svi.version_table.empty () ? svi.version_table.c_str () :
+ !schema_version_table_.empty () ? schema_version_table_.c_str () :
+ /* */ "\"schema_version\"");
+
+ // Construct the SELECT statement text.
+ //
+ string text ("SELECT \"version\", \"migration\" FROM ");
+ text += table;
+ text += " WHERE \"name\" = $1";
+
+ // Bind parameters and results.
+ //
+ char* pbuf[1] = {const_cast<char*> (name.c_str ())};
+ size_t psize[1] = {name.size ()};
+ bool pnull[1] = {false};
+ bind pbind[1] = {{bind::text,
+ &pbuf[0],
+ &psize[0],
+ psize[0],
+ &pnull[0],
+ 0}};
+ binding param (pbind, 1);
+ param.version++;
+
+ unsigned int param_types[1] = {text_oid};
+
+ char* values[1];
+ int lengths[1];
+ int formats[1];
+ native_binding nparam (values, lengths, formats, 1);
+
+ long long version;
+ bool rnull[2];
+ bind rbind[2] = {{bind::bigint, &version, 0, 0, &rnull[0], 0},
+ {bind::boolean_, &svi.migration, 0, 0, &rnull[1], 0}};
+ binding result (rbind, 2);
+ result.version++;
+
+ // If we are not in transaction, PostgreSQL will start an implicit one
+ // which suits us just fine.
+ //
+ connection_ptr cp;
+ if (!transaction::has_current ())
+ cp = factory_->connect ();
+
+ pgsql::connection& c (
+ cp != 0
+ ? *cp
+ : transaction::current ().connection (const_cast<database&> (*this)));
+
+ // If we are in the user's transaction then things are complicated. When
+ // we try to execute SELECT on a non-existent table, PG "poisons" the
+ // transaction (those "current transaction is aborted, commands ignored
+ // until end of transaction block" messages in the log). Which means all
+ // the user's schema creation statements that are likely to follow will
+ // fail.
+ //
+ // There doesn't seem to be a better way to solve this than to check for
+ // the table's existence. It is relatively easy to do with to_regclass()
+ // in 9.4+ and a real pain in earlier versions. So we are going to do
+ // this for 9.4+ and for older versions the workaround is to "pre-call"
+ // database::schema_version() outside of any transaction.
+ //
+ bool exists (true);
+ if (cp == 0 && c.server_version () >= 90400)
+ {
+ char* pbuf[1] = {const_cast<char*> (table)};
+ size_t psize[1] = {strlen (table)};
+ bool pnull[1] = {false};
+ bind pbind[1] = {{bind::text,
+ &pbuf[0],
+ &psize[0],
+ psize[0],
+ &pnull[0],
+ 0}};
+ binding param (pbind, 1);
+ param.version++;
+
+ unsigned int param_types[1] = {text_oid};
+
+ char* values[1];
+ int lengths[1];
+ int formats[1];
+ native_binding nparam (values, lengths, formats, 1);
+
+ bool rnull[1];
+ bind rbind[1] = {{bind::boolean_, &exists, 0, 0, &rnull[0], 0}};
+ binding result (rbind, 1);
+ result.version++;
+
+ // Note that to_regclass() seems happy to accept a quoted table name.
+ //
+ // Also note that starting 9.6 it requires text type rather than
+ // cstring type.
+ //
+ select_statement st (c,
+ "odb_database_schema_version_exists",
+ c.server_version () >= 90600
+ ? "SELECT to_regclass($1::text) IS NOT NULL"
+ : "SELECT to_regclass($1::cstring) IS NOT NULL",
+ false, // Don't process.
+ false, // Don't optimize.
+ param_types,
+ 1,
+ param,
+ nparam,
+ result,
+ false);
+
+ st.execute ();
+ auto_result ar (st);
+
+ switch (st.fetch ())
+ {
+ case select_statement::success:
+ {
+ assert (st.fetch () == select_statement::no_data);
+ break;
+ }
+ case select_statement::no_data:
+ case select_statement::truncated:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ // Assume no schema until determined otherwise.
+ //
+ svi.version = 0;
+
+ if (exists)
+ {
+ try
+ {
+ select_statement st (c,
+ "odb_database_schema_version_query",
+ text.c_str (),
+ false, // Don't process.
+ false, // Don't optimize.
+ param_types,
+ 1,
+ param,
+ nparam,
+ result,
+ false);
+ st.execute ();
+ auto_result ar (st);
+
+ switch (st.fetch ())
+ {
+ case select_statement::success:
+ {
+ value_traits<unsigned long long, id_bigint>::set_value (
+ svi.version, version, rnull[0]);
+ assert (st.fetch () == select_statement::no_data);
+ break;
+ }
+ case select_statement::no_data:
+ {
+ // No schema.
+ break;
+ }
+ case select_statement::truncated:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+ catch (const database_exception& e)
+ {
+ // Detect the case where there is no version table (the implicit
+ // transaction case).
+ //
+ if (e.sqlstate () != "42P01")
+ throw;
+ }
+ }
+
+ return svi;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/database.hxx b/libodb-pgsql/odb/pgsql/database.hxx
new file mode 100644
index 0000000..950cad1
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/database.hxx
@@ -0,0 +1,527 @@
+// file : odb/pgsql/database.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_DATABASE_HXX
+#define ODB_PGSQL_DATABASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <iosfwd> // std::ostream
+
+#include <odb/database.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/transfer-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/query.hxx>
+#include <odb/pgsql/tracer.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/connection-factory.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class transaction_impl;
+
+ class LIBODB_PGSQL_EXPORT database: public odb::database
+ {
+ public:
+ database (const std::string& user,
+ const std::string& password,
+ const std::string& db,
+ const std::string& host = "",
+ unsigned int port = 0,
+ const std::string& extra_conninfo = "",
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ database (const std::string& user,
+ const std::string& password,
+ const std::string& db,
+ const std::string& host,
+ const std::string& socket_ext,
+ const std::string& extra_conninfo = "",
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ explicit
+ database (const std::string& conninfo,
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Extract the database parameters from the command line. The
+ // following options are recognized:
+ //
+ // --user | --username
+ // --password
+ // --database | --dbname
+ // --host
+ // --port
+ // --options-file
+ //
+ // For more information, see the output of the print_usage() function
+ // below. If erase is true, the above options are removed from the
+ // argv array and the argc count is updated accordingly. This
+ // constructor may throw the cli_exception exception.
+ //
+ database (int& argc,
+ char* argv[],
+ bool erase = false,
+ const std::string& extra_conninfo = "",
+ details::transfer_ptr<connection_factory> =
+ details::transfer_ptr<connection_factory> ());
+
+ // Move-constructible but not move-assignable.
+ //
+ // Note: noexcept is not specified since odb::database(odb::database&&)
+ // can throw.
+ //
+#ifdef ODB_CXX11
+ database (database&&);
+#endif
+
+ static void
+ print_usage (std::ostream&);
+
+ // Object persistence API.
+ //
+ public:
+
+ // Make the object persistent.
+ //
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const T& object);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ typename object_traits<T>::id_type
+ persist (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ typename object_traits<T>::id_type
+ persist (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk persist. Can be a range of references or pointers (including
+ // smart pointers) to objects.
+ //
+ template <typename I>
+ void
+ persist (I begin, I end, bool continue_failed = true);
+
+ // Load an object. Throw object_not_persistent if not found.
+ //
+ template <typename T>
+ typename object_traits<T>::pointer_type
+ load (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ void
+ load (const typename object_traits<T>::id_type& id, T& object);
+
+ // Load (or reload, if it is already loaded) a section of an object.
+ //
+ template <typename T>
+ void
+ load (T& object, section&);
+
+ // Reload an object.
+ //
+ template <typename T>
+ void
+ reload (T& object);
+
+ template <typename T>
+ void
+ reload (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ reload (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ reload (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ reload (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Loan an object if found. Return NULL/false if not found.
+ //
+ template <typename T>
+ typename object_traits<T>::pointer_type
+ find (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ bool
+ find (const typename object_traits<T>::id_type& id, T& object);
+
+ // Update the state of a modified objects.
+ //
+ template <typename T>
+ void
+ update (T& object);
+
+ template <typename T>
+ void
+ update (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ update (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ update (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ update (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk update. Can be a range of references or pointers (including
+ // smart pointers) to objects.
+ //
+ template <typename I>
+ void
+ update (I begin, I end, bool continue_failed = true);
+
+ // Update a section of an object. Throws the section_not_loaded
+ // exception if the section is not loaded. Note also that this
+ // function does not clear the changed flag if it is set.
+ //
+ template <typename T>
+ void
+ update (const T& object, const section&);
+
+ // Make the object transient. Throw object_not_persistent if not
+ // found.
+ //
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::id_type& id);
+
+ template <typename T>
+ void
+ erase (T& object);
+
+ template <typename T>
+ void
+ erase (T* obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (const P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (const P<T, A1>& obj_ptr);
+
+ template <typename T, template <typename> class P>
+ void
+ erase (P<T>& obj_ptr);
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ void
+ erase (P<T, A1>& obj_ptr);
+
+ template <typename T>
+ void
+ erase (const typename object_traits<T>::pointer_type& obj_ptr);
+
+ // Bulk erase.
+ //
+ template <typename T, typename I>
+ void
+ erase (I id_begin, I id_end, bool continue_failed = true);
+
+ // Can be a range of references or pointers (including smart pointers)
+ // to objects.
+ //
+ template <typename I>
+ void
+ erase (I obj_begin, I obj_end, bool continue_failed = true);
+
+ // Erase multiple objects matching a query predicate.
+ //
+ template <typename T>
+ unsigned long long
+ erase_query ();
+
+ template <typename T>
+ unsigned long long
+ erase_query (const char*);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const std::string&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const pgsql::query_base&);
+
+ template <typename T>
+ unsigned long long
+ erase_query (const odb::query_base&);
+
+ // Query API.
+ //
+ template <typename T>
+ result<T>
+ query ();
+
+ template <typename T>
+ result<T>
+ query (const char*);
+
+ template <typename T>
+ result<T>
+ query (const std::string&);
+
+ template <typename T>
+ result<T>
+ query (const pgsql::query_base&);
+
+ template <typename T>
+ result<T>
+ query (const odb::query_base&);
+
+ // Query one API.
+ //
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one ();
+
+ template <typename T>
+ bool
+ query_one (T& object);
+
+ template <typename T>
+ T
+ query_value ();
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const char*);
+
+ template <typename T>
+ bool
+ query_one (const char*, T& object);
+
+ template <typename T>
+ T
+ query_value (const char*);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const std::string&);
+
+ template <typename T>
+ bool
+ query_one (const std::string&, T& object);
+
+ template <typename T>
+ T
+ query_value (const std::string&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const pgsql::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const pgsql::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const pgsql::query_base&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const odb::query_base&);
+
+ template <typename T>
+ bool
+ query_one (const odb::query_base&, T& object);
+
+ template <typename T>
+ T
+ query_value (const odb::query_base&);
+
+ // Query preparation.
+ //
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const char*);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const std::string&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const pgsql::query_base&);
+
+ template <typename T>
+ prepared_query<T>
+ prepare_query (const char* name, const odb::query_base&);
+
+ // Transactions.
+ //
+ public:
+ virtual transaction_impl*
+ begin ();
+
+ public:
+ connection_ptr
+ connection ();
+
+ // Database schema version.
+ //
+ protected:
+ virtual const schema_version_info&
+ load_schema_version (const std::string& schema_name) const;
+
+ public:
+ // Database id constant (useful for meta-programming).
+ //
+ static const odb::database_id database_id = id_pgsql;
+
+ public:
+ virtual
+ ~database ();
+
+ protected:
+ virtual odb::connection*
+ connection_ ();
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef pgsql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::database::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::database::tracer (t);
+ }
+
+ using odb::database::tracer;
+
+ public:
+ const std::string&
+ user () const
+ {
+ return user_;
+ }
+
+ const std::string&
+ password () const
+ {
+ return password_;
+ }
+
+ const std::string&
+ db () const
+ {
+ return db_;
+ }
+
+ const std::string&
+ host () const
+ {
+ return host_;
+ }
+
+ unsigned int
+ port () const
+ {
+ return port_;
+ }
+
+ const std::string&
+ socket_ext () const
+ {
+ return socket_ext_;
+ }
+
+ const std::string&
+ extra_conninfo () const
+ {
+ return extra_conninfo_;
+ }
+
+ const std::string&
+ conninfo () const
+ {
+ return conninfo_;
+ }
+
+ private:
+ // Note: remember to update move ctor if adding any new members.
+ //
+ std::string user_;
+ std::string password_;
+ std::string db_;
+ std::string host_;
+ unsigned int port_;
+ std::string socket_ext_;
+ std::string extra_conninfo_;
+ std::string conninfo_;
+ details::unique_ptr<connection_factory> factory_;
+ };
+ }
+}
+
+#include <odb/pgsql/database.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_DATABASE_HXX
diff --git a/libodb-pgsql/odb/pgsql/database.ixx b/libodb-pgsql/odb/pgsql/database.ixx
new file mode 100644
index 0000000..f04c3e6
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/database.ixx
@@ -0,0 +1,638 @@
+// file : odb/pgsql/database.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <utility> // move()
+
+#include <odb/pgsql/transaction.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+#ifdef ODB_CXX11
+ inline database::
+ database (database&& db) // Has to be inline.
+ : odb::database (std::move (db)),
+ user_ (std::move (db.user_)),
+ password_ (std::move (db.password_)),
+ db_ (std::move (db.db_)),
+ host_ (std::move (db.host_)),
+ port_ (db.port_),
+ socket_ext_ (std::move (db.socket_ext_)),
+ extra_conninfo_ (std::move (db.extra_conninfo_)),
+ conninfo_ (std::move (db.conninfo_)),
+ factory_ (std::move (db.factory_))
+ {
+ factory_->database (*this); // New database instance.
+ }
+#endif
+
+ inline connection_ptr database::
+ connection ()
+ {
+ // Go through the virtual connection_() function instead of
+ // directly to allow overriding.
+ //
+ return connection_ptr (
+ static_cast<pgsql::connection*> (connection_ ()));
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T& obj)
+ {
+ return persist_<T, id_pgsql> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const T& obj)
+ {
+ return persist_<const T, id_pgsql> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ return persist_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T>& p)
+ {
+ const P<T>& cr (p);
+ return persist<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline typename object_traits<T>::id_type database::
+ persist (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ return persist<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const typename object_traits<T>::pointer_type& pobj)
+ {
+ return persist_<T, id_pgsql> (pobj);
+ }
+
+ template <typename I>
+ inline void database::
+ persist (I b, I e, bool cont)
+ {
+ persist_<I, id_pgsql> (b, e, cont);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ load (const typename object_traits<T>::id_type& id)
+ {
+ return load_<T, id_pgsql> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ load (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return load_<T, id_pgsql> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ load (T& obj, section& s)
+ {
+ return load_<T, id_pgsql> (obj, s);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type database::
+ find (const typename object_traits<T>::id_type& id)
+ {
+ return find_<T, id_pgsql> (id);
+ }
+
+ template <typename T>
+ inline bool database::
+ find (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return find_<T, id_pgsql> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T& obj)
+ {
+ reload_<T, id_pgsql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T* p)
+ {
+ reload<T> (*p);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (const P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (const P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ reload (P<T>& p)
+ {
+ reload (odb::pointer_traits< P<T> >::get_ref (p));
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ reload (P<T, A1>& p)
+ {
+ reload (odb::pointer_traits< P<T, A1> >::get_ref (p));
+ }
+
+ template <typename T>
+ inline void database::
+ reload (const typename object_traits<T>::pointer_type& pobj)
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ reload (odb::pointer_traits<pointer_type>::get_ref (pobj));
+ }
+
+ template <typename T>
+ inline void database::
+ update (T& obj)
+ {
+ update_<T, id_pgsql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ update (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ update_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ update (P<T>& p)
+ {
+ const P<T>& cr (p);
+ update<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ update (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ update<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const typename object_traits<T>::pointer_type& pobj)
+ {
+ update_<T, id_pgsql> (pobj);
+ }
+
+ template <typename I>
+ inline void database::
+ update (I b, I e, bool cont)
+ {
+ update_<I, id_pgsql> (b, e, cont);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const T& obj, const section& s)
+ {
+ update_<T, id_pgsql> (obj, s);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::id_type& id)
+ {
+ return erase_<T, id_pgsql> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T& obj)
+ {
+ return erase_<T, id_pgsql> (obj);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T* p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (const P<T>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (const P<T, A1>& p)
+ {
+ typedef typename object_traits<T>::pointer_type object_pointer;
+
+ // The passed pointer should be the same or implicit-convertible
+ // to the object pointer. This way we make sure the object pointer
+ // does not assume ownership of the passed object.
+ //
+ const object_pointer& pobj (p);
+
+ erase_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, template <typename> class P>
+ inline void database::
+ erase (P<T>& p)
+ {
+ const P<T>& cr (p);
+ erase<T, P> (cr);
+ }
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ inline void database::
+ erase (P<T, A1>& p)
+ {
+ const P<T, A1>& cr (p);
+ erase<T, A1, P> (cr);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::pointer_type& pobj)
+ {
+ erase_<T, id_pgsql> (pobj);
+ }
+
+ template <typename T, typename I>
+ inline void database::
+ erase (I idb, I ide, bool cont)
+ {
+ erase_id_<I, T, id_pgsql> (idb, ide, cont);
+ }
+
+ template <typename I>
+ inline void database::
+ erase (I ob, I oe, bool cont)
+ {
+ erase_object_<I, id_pgsql> (ob, oe, cont);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query ()
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (pgsql::query_base ());
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const char* q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const std::string& q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const pgsql::query_base& q)
+ {
+ // T is always object_type.
+ //
+ return object_traits_impl<T, id_pgsql>::erase_query (*this, q);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return erase_query<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query ()
+ {
+ return query<T> (pgsql::query_base ());
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const char* q)
+ {
+ return query<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const std::string& q)
+ {
+ return query<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const pgsql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_<T, id_pgsql>::call (*this, q);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one ()
+ {
+ return query_one<T> (pgsql::query_base ());
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (T& o)
+ {
+ return query_one<T> (pgsql::query_base (), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value ()
+ {
+ return query_value<T> (pgsql::query_base ());
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const char* q)
+ {
+ return query_one<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const char* q, T& o)
+ {
+ return query_one<T> (pgsql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const char* q)
+ {
+ return query_value<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const std::string& q)
+ {
+ return query_one<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const std::string& q, T& o)
+ {
+ return query_one<T> (pgsql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const std::string& q)
+ {
+ return query_value<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const pgsql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_pgsql> (q);
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const pgsql::query_base& q, T& o)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_one_<T, id_pgsql> (q, o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const pgsql::query_base& q)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ return query_value_<T, id_pgsql> (q);
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const odb::query_base& q, T& o)
+ {
+ // Translate to native query.
+ //
+ return query_one<T> (pgsql::query_base (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return query_value<T> (pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, pgsql::query_base (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const pgsql::query_base& q)
+ {
+ // Throws if not in transaction.
+ //
+ pgsql::connection& c (transaction::current ().connection (*this));
+ return c.prepare_query<T> (n, q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const odb::query_base& q)
+ {
+ // Translate to native query.
+ //
+ return prepare_query<T> (n, pgsql::query_base (q));
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/details/.gitignore b/libodb-pgsql/odb/pgsql/details/.gitignore
new file mode 100644
index 0000000..b298f89
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/.gitignore
@@ -0,0 +1 @@
+/options.?xx
diff --git a/libodb-pgsql/odb/pgsql/details/build2/config-stub.h b/libodb-pgsql/odb/pgsql/details/build2/config-stub.h
new file mode 100644
index 0000000..00c5940
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/build2/config-stub.h
@@ -0,0 +1,5 @@
+/* file : odb/pgsql/details/build2/config-stub.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+#include <odb/pgsql/details/config.h>
diff --git a/libodb-pgsql/odb/pgsql/details/build2/config-vc-stub.h b/libodb-pgsql/odb/pgsql/details/build2/config-vc-stub.h
new file mode 100644
index 0000000..473cd96
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/build2/config-vc-stub.h
@@ -0,0 +1,5 @@
+/* file : odb/pgsql/details/build2/config-vc-stub.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+#include <odb/pgsql/details/config-vc.h>
diff --git a/libodb-pgsql/odb/pgsql/details/build2/config-vc.h b/libodb-pgsql/odb/pgsql/details/build2/config-vc.h
new file mode 100644
index 0000000..e9d9ade
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/build2/config-vc.h
@@ -0,0 +1,15 @@
+/* file : odb/pgsql/details/build2/config-vc.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* Configuration file for Windows/VC++ for the build2 build. */
+
+#ifndef ODB_PGSQL_DETAILS_CONFIG_VC_H
+#define ODB_PGSQL_DETAILS_CONFIG_VC_H
+
+/* Define LIBODB_PGSQL_BUILD2 for the installed case. */
+#ifndef LIBODB_PGSQL_BUILD2
+# define LIBODB_PGSQL_BUILD2
+#endif
+
+#endif /* ODB_PGSQL_DETAILS_CONFIG_VC_H */
diff --git a/libodb-pgsql/odb/pgsql/details/build2/config.h b/libodb-pgsql/odb/pgsql/details/build2/config.h
new file mode 100644
index 0000000..b2a81b5
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/build2/config.h
@@ -0,0 +1,17 @@
+/* file : odb/pgsql/details/build2/config.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* Static configuration file for the build2 build. The installed case
+ (when LIBODB_PGSQL_BUILD2 is not necessarily defined) is the only
+ reason we have it. */
+
+#ifndef ODB_PGSQL_DETAILS_CONFIG_H
+#define ODB_PGSQL_DETAILS_CONFIG_H
+
+/* Define LIBODB_PGSQL_BUILD2 for the installed case. */
+#ifndef LIBODB_PGSQL_BUILD2
+# define LIBODB_PGSQL_BUILD2
+#endif
+
+#endif /* ODB_PGSQL_DETAILS_CONFIG_H */
diff --git a/libodb-pgsql/odb/pgsql/details/config-vc.h b/libodb-pgsql/odb/pgsql/details/config-vc.h
new file mode 100644
index 0000000..77a0be5
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/config-vc.h
@@ -0,0 +1,5 @@
+/* file : odb/pgsql/details/config-vc.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* Dummy configuration file for Windows/VC++. */
diff --git a/libodb-pgsql/odb/pgsql/details/config.h.in b/libodb-pgsql/odb/pgsql/details/config.h.in
new file mode 100644
index 0000000..889023c
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/config.h.in
@@ -0,0 +1,12 @@
+/* file : odb/pgsql/details/config.h.in
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* This file is automatically processed by configure. */
+
+#ifndef ODB_PGSQL_DETAILS_CONFIG_H
+#define ODB_PGSQL_DETAILS_CONFIG_H
+
+#undef LIBODB_PGSQL_STATIC_LIB
+
+#endif /* ODB_PGSQL_DETAILS_CONFIG_H */
diff --git a/libodb-pgsql/odb/pgsql/details/config.hxx b/libodb-pgsql/odb/pgsql/details/config.hxx
new file mode 100644
index 0000000..1340622
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/config.hxx
@@ -0,0 +1,21 @@
+// file : odb/pgsql/details/config.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_DETAILS_CONFIG_HXX
+#define ODB_PGSQL_DETAILS_CONFIG_HXX
+
+// no pre
+
+#ifdef ODB_COMPILER
+# error libodb-pgsql header included in odb-compiled header
+#elif !defined(LIBODB_PGSQL_BUILD2)
+# ifdef _MSC_VER
+# include <odb/pgsql/details/config-vc.h>
+# else
+# include <odb/pgsql/details/config.h>
+# endif
+#endif
+
+// no post
+
+#endif // ODB_PGSQL_DETAILS_CONFIG_HXX
diff --git a/libodb-pgsql/odb/pgsql/details/conversion.hxx b/libodb-pgsql/odb/pgsql/details/conversion.hxx
new file mode 100644
index 0000000..b4d7e4f
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/conversion.hxx
@@ -0,0 +1,58 @@
+// file : odb/pgsql/details/conversion.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_DETAILS_CONVERSION_HXX
+#define ODB_PGSQL_DETAILS_CONVERSION_HXX
+
+#include <odb/pgsql/traits.hxx>
+
+#include <odb/details/meta/answer.hxx>
+
+namespace odb
+{
+ // @@ Revise this.
+ //
+ namespace details {}
+
+ namespace pgsql
+ {
+ namespace details
+ {
+ using namespace odb::details;
+
+ // Detect whether conversion is specified in type_traits.
+ //
+ template <typename T>
+ meta::yes
+ conversion_p_test (typename type_traits<T>::conversion*);
+
+ template <typename T>
+ meta::no
+ conversion_p_test (...);
+
+ template <typename T>
+ struct conversion_p
+ {
+ static const bool value =
+ sizeof (conversion_p_test<T> (0)) == sizeof (meta::yes);
+ };
+
+ template <typename T, bool = conversion_p<T>::value>
+ struct conversion;
+
+ template <typename T>
+ struct conversion<T, true>
+ {
+ static const char* to () {return type_traits<T>::conversion::to ();}
+ };
+
+ template <typename T>
+ struct conversion<T, false>
+ {
+ static const char* to () {return 0;}
+ };
+ }
+ }
+}
+
+#endif // ODB_PGSQL_DETAILS_CONVERSION_HXX
diff --git a/libodb-pgsql/odb/pgsql/details/endian-traits.cxx b/libodb-pgsql/odb/pgsql/details/endian-traits.cxx
new file mode 100644
index 0000000..77fdc95
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/endian-traits.cxx
@@ -0,0 +1,30 @@
+// file : odb/pgsql/details/endian-traits.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/details/endian-traits.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ namespace
+ {
+ endian_traits::endian
+ infer_host_endian ()
+ {
+ short s (1);
+ char* c (reinterpret_cast<char*> (&s));
+
+ return *c == 0 ?
+ endian_traits::big :
+ endian_traits::little;
+ }
+ }
+
+ const endian_traits::endian endian_traits::host_endian (
+ infer_host_endian ());
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/details/endian-traits.hxx b/libodb-pgsql/odb/pgsql/details/endian-traits.hxx
new file mode 100644
index 0000000..532a4a3
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/endian-traits.hxx
@@ -0,0 +1,155 @@
+// file : odb/pgsql/details/endian-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_DETAILS_ENDIAN_TRAITS_HXX
+#define ODB_PGSQL_DETAILS_ENDIAN_TRAITS_HXX
+
+#include <cstddef> // std::size_t
+#include <algorithm> // std::reverse
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ // @@ Revise this.
+ //
+ namespace details
+ {
+ }
+
+ namespace pgsql
+ {
+ namespace details
+ {
+ using namespace odb::details;
+
+ template <typename T, std::size_t S = sizeof (T)>
+ struct swap_endian;
+
+ template <typename T>
+ struct swap_endian<T, 1>
+ {
+ static T
+ swap (T x)
+ {
+ return x;
+ }
+ };
+
+ template <typename T>
+ struct swap_endian<T, 2>
+ {
+ static T
+ swap (T x)
+ {
+ union u2
+ {
+ T t;
+ char c[2];
+ };
+
+ u2 u;
+ u.t = x;
+
+ char tmp (u.c[0]);
+ u.c[0] = u.c[1];
+ u.c[1] = tmp;
+
+ return u.t;
+ }
+ };
+
+ template <typename T>
+ struct swap_endian<T, 4>
+ {
+ static T
+ swap (T x)
+ {
+ union u4
+ {
+ T t;
+ char c[4];
+ };
+
+ u4 u;
+ u.t = x;
+
+ char tmp (u.c[0]);
+ u.c[0] = u.c[3];
+ u.c[3] = tmp;
+
+ tmp = u.c[1];
+ u.c[1] = u.c[2];
+ u.c[2] = tmp;
+
+ return u.t;
+ }
+ };
+
+ template <typename T>
+ struct swap_endian<T, 8>
+ {
+ static T
+ swap (T x)
+ {
+ union u8
+ {
+ T t;
+ char c[8];
+ };
+
+ u8 u;
+ u.t = x;
+
+ char tmp (u.c[0]);
+ u.c[0] = u.c[7];
+ u.c[7] = tmp;
+
+ tmp = u.c[1];
+ u.c[1] = u.c[6];
+ u.c[6] = tmp;
+
+ tmp = u.c[2];
+ u.c[2] = u.c[5];
+ u.c[5] = tmp;
+
+ tmp = u.c[3];
+ u.c[3] = u.c[4];
+ u.c[4] = tmp;
+
+ return u.t;
+ }
+ };
+
+ class LIBODB_PGSQL_EXPORT endian_traits
+ {
+ public:
+ enum endian
+ {
+ big,
+ little
+ };
+
+ public:
+ static const endian host_endian;
+
+ public:
+ template <typename T>
+ static T
+ hton (T x)
+ {
+ return host_endian == big ? x : swap_endian<T>::swap (x);
+ }
+
+ template <typename T>
+ static T
+ ntoh (T x)
+ {
+ return host_endian == big ? x : swap_endian<T>::swap (x);
+ }
+ };
+ }
+ }
+}
+
+#endif // ODB_PGSQL_DETAILS_ENDIAN_TRAITS_HXX
diff --git a/libodb-pgsql/odb/pgsql/details/export.hxx b/libodb-pgsql/odb/pgsql/details/export.hxx
new file mode 100644
index 0000000..81ff6a0
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/export.hxx
@@ -0,0 +1,78 @@
+// file : odb/pgsql/details/export.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_DETAILS_EXPORT_HXX
+#define ODB_PGSQL_DETAILS_EXPORT_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/pgsql/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_PGSQL_BUILD2
+
+#if defined(LIBODB_PGSQL_STATIC) // Using static.
+# define LIBODB_PGSQL_EXPORT
+#elif defined(LIBODB_PGSQL_STATIC_BUILD) // Building static.
+# define LIBODB_PGSQL_EXPORT
+#elif defined(LIBODB_PGSQL_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBODB_PGSQL_EXPORT __declspec(dllimport)
+# else
+# define LIBODB_PGSQL_EXPORT
+# endif
+#elif defined(LIBODB_PGSQL_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBODB_PGSQL_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_PGSQL_EXPORT
+# endif
+#else
+// If none of the above macros are defined, then we assume we are being used
+// by some third-party build system that cannot/doesn't signal the library
+// type. Note that this fallback works for both static and shared but in case
+// of shared will be sub-optimal compared to having dllimport.
+//
+# define LIBODB_PGSQL_EXPORT // Using static or shared.
+#endif
+
+#else // LIBODB_PGSQL_BUILD2
+
+#ifdef LIBODB_PGSQL_STATIC_LIB
+# define LIBODB_PGSQL_EXPORT
+#else
+# ifdef _WIN32
+# ifdef _MSC_VER
+# ifdef LIBODB_PGSQL_DYNAMIC_LIB
+# define LIBODB_PGSQL_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_PGSQL_EXPORT __declspec(dllimport)
+# endif
+# else
+# ifdef LIBODB_PGSQL_DYNAMIC_LIB
+# ifdef DLL_EXPORT
+# define LIBODB_PGSQL_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_PGSQL_EXPORT
+# endif
+# else
+# define LIBODB_PGSQL_EXPORT __declspec(dllimport)
+# endif
+# endif
+# else
+# define LIBODB_PGSQL_EXPORT
+# endif
+#endif
+
+#endif // LIBODB_PGSQL_BUILD2
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_DETAILS_EXPORT_HXX
diff --git a/libodb-pgsql/odb/pgsql/details/options.cli b/libodb-pgsql/odb/pgsql/details/options.cli
new file mode 100644
index 0000000..f568236
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/options.cli
@@ -0,0 +1,56 @@
+// file : odb/pgsql/details/options.cli
+// license : GNU GPL v2; see accompanying LICENSE file
+
+include <string>;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ class options
+ {
+ std::string --user | --username
+ {
+ "<name>",
+ "PostgreSQL database user."
+ };
+
+ std::string --password
+ {
+ "<str>",
+ "PostgreSQL database password."
+ };
+
+ std::string --database | --dbname
+ {
+ "<name>",
+ "PostgreSQL database name."
+ };
+
+ std::string --host
+ {
+ "<str>",
+ "PostgreSQL database host name or address (localhost by default)."
+ };
+
+ std::string --port
+ {
+ "<str>",
+ "PostgreSQL database port number or socket file name extension for
+ Unix-domain connections."
+ };
+
+ std::string --options-file
+ {
+ "<file>",
+ "Read additional options from <file>. Each option should appear on a
+ separate line optionally followed by space or equal sign (\cb{=})
+ and an option value. Empty lines and lines starting with \cb{#} are
+ ignored."
+ };
+ };
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.cxx b/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.cxx
new file mode 100644
index 0000000..a4a5da6
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.cxx
@@ -0,0 +1,1114 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <odb/pgsql/details/options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <utility>
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <fstream>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // unknown_option
+ //
+ unknown_option::
+ ~unknown_option () throw ()
+ {
+ }
+
+ void unknown_option::
+ print (::std::ostream& os) const
+ {
+ os << "unknown option '" << option ().c_str () << "'";
+ }
+
+ const char* unknown_option::
+ what () const throw ()
+ {
+ return "unknown option";
+ }
+
+ // unknown_argument
+ //
+ unknown_argument::
+ ~unknown_argument () throw ()
+ {
+ }
+
+ void unknown_argument::
+ print (::std::ostream& os) const
+ {
+ os << "unknown argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unknown_argument::
+ what () const throw ()
+ {
+ return "unknown argument";
+ }
+
+ // missing_value
+ //
+ missing_value::
+ ~missing_value () throw ()
+ {
+ }
+
+ void missing_value::
+ print (::std::ostream& os) const
+ {
+ os << "missing value for option '" << option ().c_str () << "'";
+ }
+
+ const char* missing_value::
+ what () const throw ()
+ {
+ return "missing option value";
+ }
+
+ // invalid_value
+ //
+ invalid_value::
+ ~invalid_value () throw ()
+ {
+ }
+
+ void invalid_value::
+ print (::std::ostream& os) const
+ {
+ os << "invalid value '" << value ().c_str () << "' for option '"
+ << option ().c_str () << "'";
+
+ if (!message ().empty ())
+ os << ": " << message ().c_str ();
+ }
+
+ const char* invalid_value::
+ what () const throw ()
+ {
+ return "invalid option value";
+ }
+
+ // eos_reached
+ //
+ void eos_reached::
+ print (::std::ostream& os) const
+ {
+ os << what ();
+ }
+
+ const char* eos_reached::
+ what () const throw ()
+ {
+ return "end of argument stream reached";
+ }
+
+ // file_io_failure
+ //
+ file_io_failure::
+ ~file_io_failure () throw ()
+ {
+ }
+
+ void file_io_failure::
+ print (::std::ostream& os) const
+ {
+ os << "unable to open file '" << file ().c_str () << "' or read failure";
+ }
+
+ const char* file_io_failure::
+ what () const throw ()
+ {
+ return "unable to open file or read failure";
+ }
+
+ // unmatched_quote
+ //
+ unmatched_quote::
+ ~unmatched_quote () throw ()
+ {
+ }
+
+ void unmatched_quote::
+ print (::std::ostream& os) const
+ {
+ os << "unmatched quote in argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unmatched_quote::
+ what () const throw ()
+ {
+ return "unmatched quote";
+ }
+
+ // scanner
+ //
+ scanner::
+ ~scanner ()
+ {
+ }
+
+ // argv_scanner
+ //
+ bool argv_scanner::
+ more ()
+ {
+ return i_ < argc_;
+ }
+
+ const char* argv_scanner::
+ peek ()
+ {
+ if (i_ < argc_)
+ return argv_[i_];
+ else
+ throw eos_reached ();
+ }
+
+ const char* argv_scanner::
+ next ()
+ {
+ if (i_ < argc_)
+ {
+ const char* r (argv_[i_]);
+
+ if (erase_)
+ {
+ for (int i (i_ + 1); i < argc_; ++i)
+ argv_[i - 1] = argv_[i];
+
+ --argc_;
+ argv_[argc_] = 0;
+ }
+ else
+ ++i_;
+
+ ++start_position_;
+ return r;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ void argv_scanner::
+ skip ()
+ {
+ if (i_ < argc_)
+ {
+ ++i_;
+ ++start_position_;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ std::size_t argv_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ // argv_file_scanner
+ //
+ int argv_file_scanner::zero_argc_ = 0;
+ std::string argv_file_scanner::empty_string_;
+
+ bool argv_file_scanner::
+ more ()
+ {
+ if (!args_.empty ())
+ return true;
+
+ while (base::more ())
+ {
+ // See if the next argument is the file option.
+ //
+ const char* a (base::peek ());
+ const option_info* oi = 0;
+ const char* ov = 0;
+
+ if (!skip_)
+ {
+ if ((oi = find (a)) != 0)
+ {
+ base::next ();
+
+ if (!base::more ())
+ throw missing_value (a);
+
+ ov = base::next ();
+ }
+ else if (std::strncmp (a, "-", 1) == 0)
+ {
+ if ((ov = std::strchr (a, '=')) != 0)
+ {
+ std::string o (a, 0, ov - a);
+ if ((oi = find (o.c_str ())) != 0)
+ {
+ base::next ();
+ ++ov;
+ }
+ }
+ }
+ }
+
+ if (oi != 0)
+ {
+ if (oi->search_func != 0)
+ {
+ std::string f (oi->search_func (ov, oi->arg));
+
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ load (ov);
+
+ if (!args_.empty ())
+ return true;
+ }
+ else
+ {
+ if (!skip_)
+ skip_ = (std::strcmp (a, "--") == 0);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ const char* argv_file_scanner::
+ peek ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? base::peek () : args_.front ().value.c_str ();
+ }
+
+ const std::string& argv_file_scanner::
+ peek_file ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? empty_string_ : *args_.front ().file;
+ }
+
+ std::size_t argv_file_scanner::
+ peek_line ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? 0 : args_.front ().line;
+ }
+
+ const char* argv_file_scanner::
+ next ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::next ();
+ else
+ {
+ hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);
+ args_.pop_front ();
+ ++start_position_;
+ return hold_[i_].c_str ();
+ }
+ }
+
+ void argv_file_scanner::
+ skip ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::skip ();
+ else
+ {
+ args_.pop_front ();
+ ++start_position_;
+ }
+ }
+
+ const argv_file_scanner::option_info* argv_file_scanner::
+ find (const char* a) const
+ {
+ for (std::size_t i (0); i < options_count_; ++i)
+ if (std::strcmp (a, options_[i].option) == 0)
+ return &options_[i];
+
+ return 0;
+ }
+
+ std::size_t argv_file_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ void argv_file_scanner::
+ load (const std::string& file)
+ {
+ using namespace std;
+
+ ifstream is (file.c_str ());
+
+ if (!is.is_open ())
+ throw file_io_failure (file);
+
+ files_.push_back (file);
+
+ arg a;
+ a.file = &*files_.rbegin ();
+
+ for (a.line = 1; !is.eof (); ++a.line)
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ throw file_io_failure (file);
+
+ string::size_type n (line.size ());
+
+ // Trim the line from leading and trailing whitespaces.
+ //
+ if (n != 0)
+ {
+ const char* f (line.c_str ());
+ const char* l (f + n);
+
+ const char* of (f);
+ while (f < l && (*f == ' ' || *f == '\t' || *f == '\r'))
+ ++f;
+
+ --l;
+
+ const char* ol (l);
+ while (l > f && (*l == ' ' || *l == '\t' || *l == '\r'))
+ --l;
+
+ if (f != of || l != ol)
+ line = f <= l ? string (f, l - f + 1) : string ();
+ }
+
+ // Ignore empty lines, those that start with #.
+ //
+ if (line.empty () || line[0] == '#')
+ continue;
+
+ string::size_type p (string::npos);
+ if (line.compare (0, 1, "-") == 0)
+ {
+ p = line.find (' ');
+
+ string::size_type q (line.find ('='));
+ if (q != string::npos && q < p)
+ p = q;
+ }
+
+ string s1;
+ if (p != string::npos)
+ {
+ s1.assign (line, 0, p);
+
+ // Skip leading whitespaces in the argument.
+ //
+ if (line[p] == '=')
+ ++p;
+ else
+ {
+ n = line.size ();
+ for (++p; p < n; ++p)
+ {
+ char c (line[p]);
+ if (c != ' ' && c != '\t' && c != '\r')
+ break;
+ }
+ }
+ }
+ else if (!skip_)
+ skip_ = (line == "--");
+
+ string s2 (line, p != string::npos ? p : 0);
+
+ // If the string (which is an option value or argument) is
+ // wrapped in quotes, remove them.
+ //
+ n = s2.size ();
+ char cf (s2[0]), cl (s2[n - 1]);
+
+ if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
+ {
+ if (n == 1 || cf != cl)
+ throw unmatched_quote (s2);
+
+ s2 = string (s2, 1, n - 2);
+ }
+
+ if (!s1.empty ())
+ {
+ // See if this is another file option.
+ //
+ const option_info* oi;
+ if (!skip_ && (oi = find (s1.c_str ())))
+ {
+ if (s2.empty ())
+ throw missing_value (oi->option);
+
+ if (oi->search_func != 0)
+ {
+ string f (oi->search_func (s2.c_str (), oi->arg));
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ {
+ // If the path of the file being parsed is not simple and the
+ // path of the file that needs to be loaded is relative, then
+ // complete the latter using the former as a base.
+ //
+#ifndef _WIN32
+ string::size_type p (file.find_last_of ('/'));
+ bool c (p != string::npos && s2[0] != '/');
+#else
+ string::size_type p (file.find_last_of ("/\\"));
+ bool c (p != string::npos && s2[1] != ':');
+#endif
+ if (c)
+ s2.insert (0, file, 0, p + 1);
+
+ load (s2);
+ }
+
+ continue;
+ }
+
+ a.value = s1;
+ args_.push_back (a);
+ }
+
+ a.value = s2;
+ args_.push_back (a);
+ }
+ }
+
+ template <typename X>
+ struct parser
+ {
+ static void
+ parse (X& x, bool& xs, scanner& s)
+ {
+ using namespace std;
+
+ const char* o (s.next ());
+ if (s.more ())
+ {
+ string v (s.next ());
+ istringstream is (v);
+ if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<bool>
+ {
+ static void
+ parse (bool& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ const char* v (s.next ());
+
+ if (std::strcmp (v, "1") == 0 ||
+ std::strcmp (v, "true") == 0 ||
+ std::strcmp (v, "TRUE") == 0 ||
+ std::strcmp (v, "True") == 0)
+ x = true;
+ else if (std::strcmp (v, "0") == 0 ||
+ std::strcmp (v, "false") == 0 ||
+ std::strcmp (v, "FALSE") == 0 ||
+ std::strcmp (v, "False") == 0)
+ x = false;
+ else
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<std::string>
+ {
+ static void
+ parse (std::string& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ x = s.next ();
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X>
+ struct parser<std::pair<X, std::size_t> >
+ {
+ static void
+ parse (std::pair<X, std::size_t>& x, bool& xs, scanner& s)
+ {
+ x.second = s.position ();
+ parser<X>::parse (x.first, xs, s);
+ }
+ };
+
+ template <typename X>
+ struct parser<std::vector<X> >
+ {
+ static void
+ parse (std::vector<X>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.push_back (x);
+ xs = true;
+ }
+ };
+
+ template <typename X, typename C>
+ struct parser<std::set<X, C> >
+ {
+ static void
+ parse (std::set<X, C>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.insert (x);
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::map<K, V, C> >
+ {
+ static void
+ parse (std::map<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m[k] = v;
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::multimap<K, V, C> >
+ {
+ static void
+ parse (std::multimap<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m.insert (typename std::multimap<K, V, C>::value_type (k, v));
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X, typename T, T X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, s);
+ }
+
+ template <typename X, bool X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ s.next ();
+ x.*M = true;
+ }
+
+ template <typename X, typename T, T X::*M, bool X::*S>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, x.*S, s);
+ }
+ }
+ }
+ }
+}
+
+#include <map>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ // options
+ //
+
+ options::
+ options ()
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ bool erase,
+ ::odb::pgsql::details::cli::unknown_mode opt,
+ ::odb::pgsql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::pgsql::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ ::odb::pgsql::details::cli::unknown_mode opt,
+ ::odb::pgsql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::pgsql::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ options::
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::pgsql::details::cli::unknown_mode opt,
+ ::odb::pgsql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::pgsql::details::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::odb::pgsql::details::cli::unknown_mode opt,
+ ::odb::pgsql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ ::odb::pgsql::details::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ options::
+ options (::odb::pgsql::details::cli::scanner& s,
+ ::odb::pgsql::details::cli::unknown_mode opt,
+ ::odb::pgsql::details::cli::unknown_mode arg)
+ : user_ (),
+ user_specified_ (false),
+ password_ (),
+ password_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ host_ (),
+ host_specified_ (false),
+ port_ (),
+ port_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false)
+ {
+ _parse (s, opt, arg);
+ }
+
+ ::odb::pgsql::details::cli::usage_para options::
+ print_usage (::std::ostream& os, ::odb::pgsql::details::cli::usage_para p)
+ {
+ CLI_POTENTIALLY_UNUSED (os);
+
+ if (p != ::odb::pgsql::details::cli::usage_para::none)
+ os << ::std::endl;
+
+ os << "--user|--username <name> PostgreSQL database user." << ::std::endl;
+
+ os << std::endl
+ << "--password <str> PostgreSQL database password." << ::std::endl;
+
+ os << std::endl
+ << "--database|--dbname <name> PostgreSQL database name." << ::std::endl;
+
+ os << std::endl
+ << "--host <str> PostgreSQL database host name or address (localhost" << ::std::endl
+ << " by default)." << ::std::endl;
+
+ os << std::endl
+ << "--port <str> PostgreSQL database port number or socket file name" << ::std::endl
+ << " extension for Unix-domain connections." << ::std::endl;
+
+ os << std::endl
+ << "--options-file <file> Read additional options from <file>. Each option" << ::std::endl
+ << " should appear on a separate line optionally followed" << ::std::endl
+ << " by space or equal sign (=) and an option value." << ::std::endl
+ << " Empty lines and lines starting with # are ignored." << ::std::endl;
+
+ p = ::odb::pgsql::details::cli::usage_para::option;
+
+ return p;
+ }
+
+ typedef
+ std::map<std::string, void (*) (options&, ::odb::pgsql::details::cli::scanner&)>
+ _cli_options_map;
+
+ static _cli_options_map _cli_options_map_;
+
+ struct _cli_options_map_init
+ {
+ _cli_options_map_init ()
+ {
+ _cli_options_map_["--user"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::user_,
+ &options::user_specified_ >;
+ _cli_options_map_["--username"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::user_,
+ &options::user_specified_ >;
+ _cli_options_map_["--password"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::password_,
+ &options::password_specified_ >;
+ _cli_options_map_["--database"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["--dbname"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["--host"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::host_,
+ &options::host_specified_ >;
+ _cli_options_map_["--port"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::port_,
+ &options::port_specified_ >;
+ _cli_options_map_["--options-file"] =
+ &::odb::pgsql::details::cli::thunk< options, std::string, &options::options_file_,
+ &options::options_file_specified_ >;
+ }
+ };
+
+ static _cli_options_map_init _cli_options_map_init_;
+
+ bool options::
+ _parse (const char* o, ::odb::pgsql::details::cli::scanner& s)
+ {
+ _cli_options_map::const_iterator i (_cli_options_map_.find (o));
+
+ if (i != _cli_options_map_.end ())
+ {
+ (*(i->second)) (*this, s);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool options::
+ _parse (::odb::pgsql::details::cli::scanner& s,
+ ::odb::pgsql::details::cli::unknown_mode opt_mode,
+ ::odb::pgsql::details::cli::unknown_mode arg_mode)
+ {
+ bool r = false;
+ bool opt = true;
+
+ while (s.more ())
+ {
+ const char* o = s.peek ();
+
+ if (std::strcmp (o, "--") == 0)
+ {
+ opt = false;
+ s.skip ();
+ r = true;
+ continue;
+ }
+
+ if (opt)
+ {
+ if (_parse (o, s))
+ {
+ r = true;
+ continue;
+ }
+
+ if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
+ {
+ // Handle combined option values.
+ //
+ std::string co;
+ if (const char* v = std::strchr (o, '='))
+ {
+ co.assign (o, 0, v - o);
+ ++v;
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (co.c_str ()),
+ const_cast<char*> (v)
+ };
+
+ ::odb::pgsql::details::cli::argv_scanner ns (0, ac, av);
+
+ if (_parse (co.c_str (), ns))
+ {
+ // Parsed the option but not its value?
+ //
+ if (ns.end () != 2)
+ throw ::odb::pgsql::details::cli::invalid_value (co, v);
+
+ s.next ();
+ r = true;
+ continue;
+ }
+ else
+ {
+ // Set the unknown option and fall through.
+ //
+ o = co.c_str ();
+ }
+ }
+
+ switch (opt_mode)
+ {
+ case ::odb::pgsql::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::pgsql::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::pgsql::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::pgsql::details::cli::unknown_option (o);
+ }
+ }
+
+ break;
+ }
+ }
+
+ switch (arg_mode)
+ {
+ case ::odb::pgsql::details::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::odb::pgsql::details::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::odb::pgsql::details::cli::unknown_mode::fail:
+ {
+ throw ::odb::pgsql::details::cli::unknown_argument (o);
+ }
+ }
+
+ break;
+ }
+
+ return r;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
diff --git a/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.hxx b/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.hxx
new file mode 100644
index 0000000..4d264d4
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.hxx
@@ -0,0 +1,562 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+#ifndef LIBODB_PGSQL_DETAILS_OPTIONS_HXX
+#define LIBODB_PGSQL_DETAILS_OPTIONS_HXX
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <list>
+#include <deque>
+#include <iosfwd>
+#include <string>
+#include <cstddef>
+#include <exception>
+
+#ifndef CLI_POTENTIALLY_UNUSED
+# if defined(_MSC_VER) || defined(__xlC__)
+# define CLI_POTENTIALLY_UNUSED(x) (void*)&x
+# else
+# define CLI_POTENTIALLY_UNUSED(x) (void)x
+# endif
+#endif
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ class usage_para
+ {
+ public:
+ enum value
+ {
+ none,
+ text,
+ option
+ };
+
+ usage_para (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ class unknown_mode
+ {
+ public:
+ enum value
+ {
+ skip,
+ stop,
+ fail
+ };
+
+ unknown_mode (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ // Exceptions.
+ //
+
+ class exception: public std::exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const = 0;
+ };
+
+ ::std::ostream&
+ operator<< (::std::ostream&, const exception&);
+
+ class unknown_option: public exception
+ {
+ public:
+ virtual
+ ~unknown_option () throw ();
+
+ unknown_option (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class unknown_argument: public exception
+ {
+ public:
+ virtual
+ ~unknown_argument () throw ();
+
+ unknown_argument (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ class missing_value: public exception
+ {
+ public:
+ virtual
+ ~missing_value () throw ();
+
+ missing_value (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class invalid_value: public exception
+ {
+ public:
+ virtual
+ ~invalid_value () throw ();
+
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message = std::string ());
+
+ const std::string&
+ option () const;
+
+ const std::string&
+ value () const;
+
+ const std::string&
+ message () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ std::string value_;
+ std::string message_;
+ };
+
+ class eos_reached: public exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class file_io_failure: public exception
+ {
+ public:
+ virtual
+ ~file_io_failure () throw ();
+
+ file_io_failure (const std::string& file);
+
+ const std::string&
+ file () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string file_;
+ };
+
+ class unmatched_quote: public exception
+ {
+ public:
+ virtual
+ ~unmatched_quote () throw ();
+
+ unmatched_quote (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ // Command line argument scanner interface.
+ //
+ // The values returned by next() are guaranteed to be valid
+ // for the two previous arguments up until a call to a third
+ // peek() or next().
+ //
+ // The position() function returns a monotonically-increasing
+ // number which, if stored, can later be used to determine the
+ // relative position of the argument returned by the following
+ // call to next(). Note that if multiple scanners are used to
+ // extract arguments from multiple sources, then the end
+ // position of the previous scanner should be used as the
+ // start position of the next.
+ //
+ class scanner
+ {
+ public:
+ virtual
+ ~scanner ();
+
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+
+ virtual std::size_t
+ position () = 0;
+ };
+
+ class argv_scanner: public scanner
+ {
+ public:
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ int
+ end () const;
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ protected:
+ std::size_t start_position_;
+ int i_;
+ int& argc_;
+ char** argv_;
+ bool erase_;
+ };
+
+ class argv_file_scanner: public argv_scanner
+ {
+ public:
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t start_position = 0);
+
+ struct option_info
+ {
+ // If search_func is not NULL, it is called, with the arg
+ // value as the second argument, to locate the options file.
+ // If it returns an empty string, then the file is ignored.
+ //
+ const char* option;
+ std::string (*search_func) (const char*, void* arg);
+ void* arg;
+ };
+
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const option_info* options = 0,
+ std::size_t options_count = 0,
+ std::size_t start_position = 0);
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ // Return the file path if the peeked at argument came from a file and
+ // the empty string otherwise. The reference is guaranteed to be valid
+ // till the end of the scanner lifetime.
+ //
+ const std::string&
+ peek_file ();
+
+ // Return the 1-based line number if the peeked at argument came from
+ // a file and zero otherwise.
+ //
+ std::size_t
+ peek_line ();
+
+ private:
+ const option_info*
+ find (const char*) const;
+
+ void
+ load (const std::string& file);
+
+ typedef argv_scanner base;
+
+ const std::string option_;
+ option_info option_info_;
+ const option_info* options_;
+ std::size_t options_count_;
+
+ struct arg
+ {
+ std::string value;
+ const std::string* file;
+ std::size_t line;
+ };
+
+ std::deque<arg> args_;
+ std::list<std::string> files_;
+
+ // Circular buffer of two arguments.
+ //
+ std::string hold_[2];
+ std::size_t i_;
+
+ bool skip_;
+
+ static int zero_argc_;
+ static std::string empty_string_;
+ };
+
+ template <typename X>
+ struct parser;
+ }
+ }
+ }
+}
+
+#include <string>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ class options
+ {
+ public:
+ options ();
+
+ options (int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::pgsql::details::cli::unknown_mode option = ::odb::pgsql::details::cli::unknown_mode::fail,
+ ::odb::pgsql::details::cli::unknown_mode argument = ::odb::pgsql::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ ::odb::pgsql::details::cli::unknown_mode option = ::odb::pgsql::details::cli::unknown_mode::fail,
+ ::odb::pgsql::details::cli::unknown_mode argument = ::odb::pgsql::details::cli::unknown_mode::stop);
+
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::pgsql::details::cli::unknown_mode option = ::odb::pgsql::details::cli::unknown_mode::fail,
+ ::odb::pgsql::details::cli::unknown_mode argument = ::odb::pgsql::details::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::odb::pgsql::details::cli::unknown_mode option = ::odb::pgsql::details::cli::unknown_mode::fail,
+ ::odb::pgsql::details::cli::unknown_mode argument = ::odb::pgsql::details::cli::unknown_mode::stop);
+
+ options (::odb::pgsql::details::cli::scanner&,
+ ::odb::pgsql::details::cli::unknown_mode option = ::odb::pgsql::details::cli::unknown_mode::fail,
+ ::odb::pgsql::details::cli::unknown_mode argument = ::odb::pgsql::details::cli::unknown_mode::stop);
+
+ // Option accessors.
+ //
+ const std::string&
+ user () const;
+
+ bool
+ user_specified () const;
+
+ const std::string&
+ password () const;
+
+ bool
+ password_specified () const;
+
+ const std::string&
+ database () const;
+
+ bool
+ database_specified () const;
+
+ const std::string&
+ host () const;
+
+ bool
+ host_specified () const;
+
+ const std::string&
+ port () const;
+
+ bool
+ port_specified () const;
+
+ const std::string&
+ options_file () const;
+
+ bool
+ options_file_specified () const;
+
+ // Print usage information.
+ //
+ static ::odb::pgsql::details::cli::usage_para
+ print_usage (::std::ostream&,
+ ::odb::pgsql::details::cli::usage_para = ::odb::pgsql::details::cli::usage_para::none);
+
+ // Implementation details.
+ //
+ protected:
+ bool
+ _parse (const char*, ::odb::pgsql::details::cli::scanner&);
+
+ private:
+ bool
+ _parse (::odb::pgsql::details::cli::scanner&,
+ ::odb::pgsql::details::cli::unknown_mode option,
+ ::odb::pgsql::details::cli::unknown_mode argument);
+
+ public:
+ std::string user_;
+ bool user_specified_;
+ std::string password_;
+ bool password_specified_;
+ std::string database_;
+ bool database_specified_;
+ std::string host_;
+ bool host_specified_;
+ std::string port_;
+ bool port_specified_;
+ std::string options_file_;
+ bool options_file_specified_;
+ };
+ }
+ }
+}
+
+#include <odb/pgsql/details/options.ixx>
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
+#endif // LIBODB_PGSQL_DETAILS_OPTIONS_HXX
diff --git a/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.ixx b/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.ixx
new file mode 100644
index 0000000..340789e
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/details/pregenerated/odb/pgsql/details/options.ixx
@@ -0,0 +1,372 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <cassert>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ namespace cli
+ {
+ // usage_para
+ //
+ inline usage_para::
+ usage_para (value v)
+ : v_ (v)
+ {
+ }
+
+ // unknown_mode
+ //
+ inline unknown_mode::
+ unknown_mode (value v)
+ : v_ (v)
+ {
+ }
+
+ // exception
+ //
+ inline ::std::ostream&
+ operator<< (::std::ostream& os, const exception& e)
+ {
+ e.print (os);
+ return os;
+ }
+
+ // unknown_option
+ //
+ inline unknown_option::
+ unknown_option (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& unknown_option::
+ option () const
+ {
+ return option_;
+ }
+
+ // unknown_argument
+ //
+ inline unknown_argument::
+ unknown_argument (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unknown_argument::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // missing_value
+ //
+ inline missing_value::
+ missing_value (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& missing_value::
+ option () const
+ {
+ return option_;
+ }
+
+ // invalid_value
+ //
+ inline invalid_value::
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message)
+ : option_ (option),
+ value_ (value),
+ message_ (message)
+ {
+ }
+
+ inline const std::string& invalid_value::
+ option () const
+ {
+ return option_;
+ }
+
+ inline const std::string& invalid_value::
+ value () const
+ {
+ return value_;
+ }
+
+ inline const std::string& invalid_value::
+ message () const
+ {
+ return message_;
+ }
+
+ // file_io_failure
+ //
+ inline file_io_failure::
+ file_io_failure (const std::string& file)
+ : file_ (file)
+ {
+ }
+
+ inline const std::string& file_io_failure::
+ file () const
+ {
+ return file_;
+ }
+
+ // unmatched_quote
+ //
+ inline unmatched_quote::
+ unmatched_quote (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unmatched_quote::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // argv_scanner
+ //
+ inline argv_scanner::
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + 1),
+ i_ (1),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline argv_scanner::
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + static_cast<std::size_t> (start)),
+ i_ (start),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline int argv_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ // argv_file_scanner
+ //
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+
+ load (file);
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const option_info* options,
+ std::size_t options_count,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ load (file);
+ }
+ }
+ }
+ }
+}
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace details
+ {
+ // options
+ //
+
+ inline const std::string& options::
+ user () const
+ {
+ return this->user_;
+ }
+
+ inline bool options::
+ user_specified () const
+ {
+ return this->user_specified_;
+ }
+
+ inline const std::string& options::
+ password () const
+ {
+ return this->password_;
+ }
+
+ inline bool options::
+ password_specified () const
+ {
+ return this->password_specified_;
+ }
+
+ inline const std::string& options::
+ database () const
+ {
+ return this->database_;
+ }
+
+ inline bool options::
+ database_specified () const
+ {
+ return this->database_specified_;
+ }
+
+ inline const std::string& options::
+ host () const
+ {
+ return this->host_;
+ }
+
+ inline bool options::
+ host_specified () const
+ {
+ return this->host_specified_;
+ }
+
+ inline const std::string& options::
+ port () const
+ {
+ return this->port_;
+ }
+
+ inline bool options::
+ port_specified () const
+ {
+ return this->port_specified_;
+ }
+
+ inline const std::string& options::
+ options_file () const
+ {
+ return this->options_file_;
+ }
+
+ inline bool options::
+ options_file_specified () const
+ {
+ return this->options_file_specified_;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
diff --git a/libodb-pgsql/odb/pgsql/error.cxx b/libodb-pgsql/odb/pgsql/error.cxx
new file mode 100644
index 0000000..ba8451e
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/error.cxx
@@ -0,0 +1,86 @@
+// file : odb/pgsql/error.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <string>
+#include <cassert>
+
+#include <odb/pgsql/error.hxx>
+#include <odb/pgsql/exceptions.hxx>
+#include <odb/pgsql/connection.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ void
+ translate_error (connection& c, PGresult* r,
+ size_t pos, multiple_exceptions* mex)
+ {
+ if (!r)
+ {
+ if (PQstatus (c.handle ()) == CONNECTION_BAD)
+ {
+ c.mark_failed ();
+ throw connection_lost ();
+ }
+ else
+ throw bad_alloc ();
+ }
+
+ // Note that we expect the caller to handle PGRES_PIPELINE_ABORTED since
+ // it's not really an error but rather an indication that no attempt was
+ // made to execute this statement.
+ //
+ string ss;
+ switch (PQresultStatus (r))
+ {
+ case PGRES_BAD_RESPONSE:
+ {
+ throw database_exception ("bad server response");
+ }
+ case PGRES_FATAL_ERROR:
+ {
+ const char* s (PQresultErrorField (r, PG_DIAG_SQLSTATE));
+ ss = (s != 0 ? s : "?????");
+
+ // Deadlock detected.
+ //
+ if (ss == "40001" || ss == "40P01")
+ throw deadlock ();
+ else if (PQstatus (c.handle ()) == CONNECTION_BAD)
+ {
+ c.mark_failed ();
+ throw connection_lost ();
+ }
+ break;
+ }
+ default:
+ assert (false);
+ break;
+ }
+
+ string msg;
+ {
+ // Can be NULL in case of PGRES_BAD_RESPONSE.
+ //
+ const char* m (PQresultErrorMessage (r));
+ msg = (m != 0 ? m : "bad server response");
+
+ // Get rid of the trailing newline if there is one.
+ //
+ string::size_type n (msg.size ());
+ if (n != 0 && msg[n - 1] == '\n')
+ msg.resize (n - 1);
+ }
+
+ if (mex == 0)
+ throw database_exception (ss, msg);
+ else
+ // In PosgreSQL all errors are fatal.
+ //
+ mex->insert (pos, database_exception (ss, msg), true);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/error.hxx b/libodb-pgsql/odb/pgsql/error.hxx
new file mode 100644
index 0000000..8d2793d
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/error.hxx
@@ -0,0 +1,41 @@
+// file : odb/pgsql/error.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_ERROR_HXX
+#define ODB_PGSQL_ERROR_HXX
+
+#include <odb/pre.hxx>
+
+#include <libpq-fe.h>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx> // connection, multiple_exceptions
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // Translate an error condition involving PGresult* and throw (or return,
+ // in case multiple_exceptions is not NULL) an appropriate exception. If
+ // result is NULL, it is assumed that the error was caused due to a bad
+ // connection or a memory allocation error.
+ //
+ LIBODB_PGSQL_EXPORT void
+ translate_error (connection& c, PGresult* r,
+ std::size_t pos = 0, multiple_exceptions* = 0);
+
+ // Return true if PGresult is not NULL and is not in an error state. If
+ // both s and r are non-NULL, the pointed to value will be populated with
+ // the result status. Otherwise, s is ignored.
+ //
+ LIBODB_PGSQL_EXPORT bool
+ is_good_result (PGresult* r, ExecStatusType* s = 0);
+ }
+}
+
+#include <odb/pgsql/error.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_ERROR_HXX
diff --git a/libodb-pgsql/odb/pgsql/error.ixx b/libodb-pgsql/odb/pgsql/error.ixx
new file mode 100644
index 0000000..6a010aa
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/error.ixx
@@ -0,0 +1,31 @@
+// file : odb/pgsql/error.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace pgsql
+ {
+ inline bool
+ is_good_result (PGresult* r, ExecStatusType* s)
+ {
+ if (r != 0)
+ {
+ ExecStatusType status (PQresultStatus (r));
+
+ if (s != 0)
+ *s = status;
+
+ return
+ status != PGRES_BAD_RESPONSE
+ && status != PGRES_NONFATAL_ERROR
+ && status != PGRES_FATAL_ERROR
+#ifdef LIBPQ_HAS_PIPELINING
+ && status != PGRES_PIPELINE_ABORTED
+#endif
+ ;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/exceptions.cxx b/libodb-pgsql/odb/pgsql/exceptions.cxx
new file mode 100644
index 0000000..28e7fc4
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/exceptions.cxx
@@ -0,0 +1,79 @@
+// file : odb/pgsql/exceptions.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/pgsql/exceptions.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // database_exception
+ //
+
+ database_exception::
+ database_exception (const string& message)
+ : message_ (message), what_ (message)
+ {
+ }
+
+ database_exception::
+ database_exception (const string& sqlstate,
+ const string& message)
+ : sqlstate_ (sqlstate), message_ (message)
+ {
+ if (!sqlstate_.empty ())
+ what_ = sqlstate_ + ": " + message_;
+ else
+ what_ = message_;
+ }
+
+ database_exception::
+ ~database_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* database_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ database_exception* database_exception::
+ clone () const
+ {
+ return new database_exception (*this);
+ }
+
+ //
+ // cli_exception
+ //
+
+ cli_exception::
+ cli_exception (const string& what)
+ : what_ (what)
+ {
+ }
+
+ cli_exception::
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* cli_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ cli_exception* cli_exception::
+ clone () const
+ {
+ return new cli_exception (*this);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/exceptions.hxx b/libodb-pgsql/odb/pgsql/exceptions.hxx
new file mode 100644
index 0000000..8417c1a
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/exceptions.hxx
@@ -0,0 +1,80 @@
+// file : odb/pgsql/exceptions.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_EXCEPTIONS_HXX
+#define ODB_PGSQL_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+
+#include <odb/exceptions.hxx>
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ struct LIBODB_PGSQL_EXPORT database_exception: odb::database_exception
+ {
+ database_exception (const std::string& message);
+
+ database_exception (const std::string& sqlstate,
+ const std::string& message);
+
+ ~database_exception () ODB_NOTHROW_NOEXCEPT;
+
+ const std::string&
+ message () const
+ {
+ return message_;
+ }
+
+ const std::string&
+ sqlstate () const
+ {
+ return sqlstate_;
+ }
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual database_exception*
+ clone () const;
+
+ private:
+ std::string sqlstate_;
+ std::string message_;
+ std::string what_;
+ };
+
+ struct LIBODB_PGSQL_EXPORT cli_exception: odb::exception
+ {
+ cli_exception (const std::string& what);
+ ~cli_exception () ODB_NOTHROW_NOEXCEPT;
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual cli_exception*
+ clone () const;
+
+ private:
+ std::string what_;
+ };
+
+ namespace core
+ {
+ using pgsql::database_exception;
+ using pgsql::cli_exception;
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_EXCEPTIONS_HXX
diff --git a/libodb-pgsql/odb/pgsql/forward.hxx b/libodb-pgsql/odb/pgsql/forward.hxx
new file mode 100644
index 0000000..1186b28
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/forward.hxx
@@ -0,0 +1,91 @@
+// file : odb/pgsql/forward.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_FORWARD_HXX
+#define ODB_PGSQL_FORWARD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ namespace core
+ {
+ using namespace odb::common;
+ }
+
+ //
+ //
+ class database;
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+ class connection_factory;
+ class statement;
+ class transaction;
+ class tracer;
+
+ namespace core
+ {
+ using pgsql::database;
+ using pgsql::connection;
+ using pgsql::connection_ptr;
+ using pgsql::transaction;
+ using pgsql::statement;
+ }
+
+ // Implementation details.
+ //
+ enum statement_kind
+ {
+ statement_select,
+ statement_insert,
+ statement_update,
+ statement_delete
+ };
+
+ class binding;
+ class select_statement;
+
+ template <typename T>
+ class object_statements;
+
+ template <typename T>
+ class polymorphic_root_object_statements;
+
+ template <typename T>
+ class polymorphic_derived_object_statements;
+
+ template <typename T>
+ class no_id_object_statements;
+
+ template <typename T>
+ class view_statements;
+
+ template <typename T>
+ class container_statements;
+
+ template <typename T>
+ class smart_container_statements;
+
+ template <typename T, typename ST>
+ class section_statements;
+
+ class query_base;
+ }
+
+ namespace details
+ {
+ template <>
+ struct counter_type<pgsql::connection>
+ {
+ typedef shared_base counter;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_FORWARD_HXX
diff --git a/libodb-pgsql/odb/pgsql/no-id-object-result.hxx b/libodb-pgsql/odb/pgsql/no-id-object-result.hxx
new file mode 100644
index 0000000..734d4a7
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/no-id-object-result.hxx
@@ -0,0 +1,77 @@
+// file : odb/pgsql/no-id-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_NO_ID_OBJECT_RESULT_HXX
+#define ODB_PGSQL_NO_ID_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/no-id-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx> // query_base
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ class no_id_object_result_impl: public odb::no_id_object_result_impl<T>
+ {
+ public:
+ typedef odb::no_id_object_result_impl<T> base_type;
+
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_pgsql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~no_id_object_result_impl ();
+
+ no_id_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/pgsql/no-id-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_NO_ID_OBJECT_RESULT_HXX
diff --git a/libodb-pgsql/odb/pgsql/no-id-object-result.txx b/libodb-pgsql/odb/pgsql/no-id-object-result.txx
new file mode 100644
index 0000000..0bae952
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/no-id-object-result.txx
@@ -0,0 +1,114 @@
+// file : odb/pgsql/no-id-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+
+#include <odb/pgsql/no-id-object-statements.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ no_id_object_result_impl<T>::
+ ~no_id_object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ no_id_object_result_impl<T>::
+ no_id_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ load (object_type& obj)
+ {
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ statement_->reload ();
+ }
+ }
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+ tc_.init (obj, im, &this->db_);
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (statement_->next ())
+ count_++;
+ else
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t no_id_object_result_impl<T>::
+ size ()
+ {
+ return this->end_ ? count_ : statement_->result_size ();
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/no-id-object-statements.hxx b/libodb-pgsql/odb/pgsql/no-id-object-statements.hxx
new file mode 100644
index 0000000..baa1b2a
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/no-id-object-statements.hxx
@@ -0,0 +1,147 @@
+// file : odb/pgsql/no-id-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_NO_ID_OBJECT_STATEMENTS_HXX
+#define ODB_PGSQL_NO_ID_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/pgsql-types.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/statements-base.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // Implementation for objects without object id.
+ //
+
+ template <typename T>
+ class no_id_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_pgsql> object_traits;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef pgsql::insert_statement insert_statement_type;
+
+ public:
+ no_id_object_statements (connection_type&);
+
+ virtual
+ ~no_id_object_statements ();
+
+ // Object image.
+ //
+ image_type&
+ image (std::size_t i = 0)
+ {
+ return image_[i];
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Select binding (needed for query support).
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement_name,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ object_traits::persist_statement_types,
+ insert_column_count,
+ insert_image_binding_,
+ insert_image_native_binding_,
+ 0,
+ false));
+
+ return *persist_;
+ }
+
+ public:
+ // select = total
+ // insert = total - inverse; inverse == 0 for object without id
+ //
+ static const std::size_t insert_column_count =
+ object_traits::column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count;
+
+ private:
+ no_id_object_statements (const no_id_object_statements&);
+ no_id_object_statements& operator= (const no_id_object_statements&);
+
+ private:
+ image_type image_[object_traits::batch];
+ unsigned long long status_[object_traits::batch];
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ bind select_image_bind_[select_column_count];
+ bool select_image_truncated_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+ native_binding insert_image_native_binding_;
+ char* insert_image_values_[insert_column_count];
+ int insert_image_lengths_[insert_column_count];
+ int insert_image_formats_[insert_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ };
+ }
+}
+
+#include <odb/pgsql/no-id-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_NO_ID_OBJECT_STATEMENTS_HXX
diff --git a/libodb-pgsql/odb/pgsql/no-id-object-statements.txx b/libodb-pgsql/odb/pgsql/no-id-object-statements.txx
new file mode 100644
index 0000000..0c340ab
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/no-id-object-statements.txx
@@ -0,0 +1,50 @@
+// file : odb/pgsql/no-id-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // no_id_object_statements
+ //
+
+ template <typename T>
+ no_id_object_statements<T>::
+ ~no_id_object_statements ()
+ {
+ }
+
+ template <typename T>
+ no_id_object_statements<T>::
+ no_id_object_statements (connection_type& conn)
+ : statements_base (conn),
+ // select
+ select_image_binding_ (select_image_bind_, select_column_count),
+ // insert
+ insert_image_binding_ (insert_image_bind_,
+ insert_column_count,
+ object_traits::batch,
+ sizeof (image_type),
+ status_),
+ insert_image_native_binding_ (insert_image_values_,
+ insert_image_lengths_,
+ insert_image_formats_,
+ insert_column_count)
+ {
+ image_[0].version = 0; // Only version in the first element used.
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/pgsql-fwd.hxx b/libodb-pgsql/odb/pgsql/pgsql-fwd.hxx
new file mode 100644
index 0000000..e60bc37
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/pgsql-fwd.hxx
@@ -0,0 +1,19 @@
+// file : odb/pgsql/pgsql-fwd.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_PGSQL_FWD_HXX
+#define ODB_PGSQL_PGSQL_FWD_HXX
+
+#include <odb/pre.hxx>
+
+// Forward declaration for some of the types defined in libpq-fe.h. This
+// allows us to avoid having to include libpq-fe.h in public headers.
+//
+typedef unsigned int Oid;
+
+typedef struct pg_conn PGconn;
+typedef struct pg_result PGresult;
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_PGSQL_FWD_HXX
diff --git a/libodb-pgsql/odb/pgsql/pgsql-oid.hxx b/libodb-pgsql/odb/pgsql/pgsql-oid.hxx
new file mode 100644
index 0000000..e2ef1af
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/pgsql-oid.hxx
@@ -0,0 +1,47 @@
+// file : odb/pgsql/pgsql-oid.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Generated file of OIDs extracted from the PostgreSQL 8.4.8 source file
+// src/include/catalog/pg_type.h
+//
+
+#ifndef ODB_PGSQL_PGSQL_OID_HXX
+#define ODB_PGSQL_PGSQL_OID_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/pgsql/version.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ enum
+ {
+ bool_oid = 16,
+ int2_oid = 21,
+ int4_oid = 23,
+ int8_oid = 20,
+
+ numeric_oid = 1700,
+
+ float4_oid = 700,
+ float8_oid = 701,
+
+ date_oid = 1082,
+ time_oid = 1083,
+ timestamp_oid = 1114,
+
+ text_oid = 25,
+ bytea_oid = 17,
+ bit_oid = 1560,
+ varbit_oid = 1562,
+
+ uuid_oid = 2950
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_PGSQL_OID_HXX
diff --git a/libodb-pgsql/odb/pgsql/pgsql-types.hxx b/libodb-pgsql/odb/pgsql/pgsql-types.hxx
new file mode 100644
index 0000000..117a41e
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/pgsql-types.hxx
@@ -0,0 +1,59 @@
+// file : odb/pgsql/pgsql-types.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_PGSQL_TYPES_HXX
+#define ODB_PGSQL_PGSQL_TYPES_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/pgsql/pgsql-fwd.hxx> // Oid
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // The libpq result binding. This data structures is roughly modeled
+ // after MYSQL_BIND from MySQL.
+ //
+ // Types that may need to grow are bound as pointers to pointers to char
+ // array (normally in details::buffer) in order to allow simple offsetting
+ // in bulk operation support. Note that if we were to do the same for
+ // capacity, we could get rid of the buffer growth tracking altogether.
+ //
+ struct bind
+ {
+ enum buffer_type
+ {
+ boolean_, // Buffer is a bool; size, capacity, truncated are unused.
+ smallint, // Buffer is short; size, capacity, truncated are unused.
+ integer, // Buffer is int; size, capacity, truncated are unused.
+ bigint, // Buffer is long long; size, capacity, truncated are unused.
+ real, // Buffer is float; size, capacity, truncated are unused.
+ double_, // Buffer is double; size, capacity, truncated are unused.
+ numeric, // Buffer is a pointer to pointer to char array.
+ date, // Buffer is int; size, capacity, truncated are unused.
+ time, // Buffer is long long; size, capacity, truncated are unused.
+ timestamp,// Buffer is long long; size, capacity, truncated are unused.
+ text, // Buffer is a pointer to pointer to char array.
+ bytea, // Buffer is a pointer to pointer to char array.
+ bit, // Buffer is a pointer to char array.
+ varbit, // Buffer is a pointer to pointer to char array.
+ uuid // Buffer is a 16-byte char array; size capacity, truncated
+ // are unused. Note: big-endian, in RFC 4122/4.1.2 order.
+ };
+
+ buffer_type type;
+ void* buffer;
+ std::size_t* size;
+ std::size_t capacity;
+ bool* is_null;
+ bool* truncated;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_PGSQL_TYPES_HXX
diff --git a/libodb-pgsql/odb/pgsql/polymorphic-object-result.hxx b/libodb-pgsql/odb/pgsql/polymorphic-object-result.hxx
new file mode 100644
index 0000000..a1cd108
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/polymorphic-object-result.hxx
@@ -0,0 +1,94 @@
+// file : odb/pgsql/polymorphic-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_POLYMORPHIC_OBJECT_RESULT_HXX
+#define ODB_PGSQL_POLYMORPHIC_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/polymorphic-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx> // query_base
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ class polymorphic_object_result_impl:
+ public odb::polymorphic_object_result_impl<T>
+ {
+ public:
+ typedef odb::polymorphic_object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_pgsql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename base_type::root_type root_type;
+ typedef typename base_type::discriminator_type discriminator_type;
+
+ typedef object_traits_impl<root_type, id_pgsql> root_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~polymorphic_object_result_impl ();
+
+ polymorphic_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type*, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual discriminator_type
+ load_discriminator ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ load_image ();
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/pgsql/polymorphic-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_POLYMORPHIC_OBJECT_RESULT_HXX
diff --git a/libodb-pgsql/odb/pgsql/polymorphic-object-result.txx b/libodb-pgsql/odb/pgsql/polymorphic-object-result.txx
new file mode 100644
index 0000000..bad2091
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/polymorphic-object-result.txx
@@ -0,0 +1,287 @@
+// file : odb/pgsql/polymorphic-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx> // object_not_persistent
+
+#include <odb/pgsql/polymorphic-object-statements.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ ~polymorphic_object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ polymorphic_object_result_impl<T>::
+ polymorphic_object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> st,
+ statements_type& sts,
+ const schema_version_migration* svm)
+ : base_type (sts.connection ()),
+ statement_ (st),
+ statements_ (sts),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ load (object_type* pobj, bool fetch)
+ {
+ if (fetch)
+ load_image ();
+
+ typename statements_type::root_statements_type& rsts (
+ statements_.root_statements ());
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!rsts.locked ());
+ typename statements_type::auto_lock l (rsts);
+
+ typename object_traits::image_type& i (statements_.image ());
+ typename root_traits::image_type& ri (rsts.image ());
+
+ id_type id (root_traits::id (ri));
+
+ // Determine this object's dynamic type.
+ //
+ typedef typename root_traits::info_type info_type;
+ discriminator_type d (root_traits::discriminator (ri));
+ discriminator_type disc (d);
+
+ // Use the polymorphic_info() helper to get concrete_info if
+ // object_type is concrete and NULL if it is abstract.
+ //
+ const info_type* spi (polymorphic_info (object_traits::info));
+ const info_type& pi (
+ spi != 0 && spi->discriminator == d
+ ? *spi
+ : root_traits::map->find (d));
+
+ typedef typename root_traits::pointer_type root_pointer_type;
+ typedef typename root_traits::pointer_traits root_pointer_traits;
+
+ typename object_traits::pointer_cache_traits::insert_guard ig;
+
+ if (pobj == 0)
+ {
+ // Need to create a new instance of the dynamic type.
+ //
+ root_pointer_type rp (pi.create ());
+ pointer_type p (
+ root_pointer_traits::template static_pointer_cast<object_type> (rp));
+
+ // Insert it as a root pointer (for non-unique pointers, rp should
+ // still be valid and for unique pointers this is a no-op).
+ //
+ ig.reset (
+ object_traits::pointer_cache_traits::insert (this->db_, id, rp));
+
+ pobj = &pointer_traits::get_ref (p);
+ current (p);
+ }
+ else
+ {
+ // We are loading into an existing instance. If the static and
+ // dynamic types differ, then make sure the instance is at least
+ // of the dynamic type.
+ //
+ if (&pi != &object_traits::info)
+ {
+ const info_type& dpi (root_traits::map->find (typeid (*pobj)));
+
+ if (&dpi != &pi && dpi.derived (pi))
+ throw object_not_persistent (); // @@ type_mismatch ?
+ }
+ }
+
+ callback_event ce (callback_event::pre_load);
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+
+ tc_.init (*pobj, i, &this->db_);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, dynamic part, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ root_traits::init (idi, id);
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, *pobj, false);
+
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ if (&pi != &object_traits::info)
+ {
+ std::size_t d (object_traits::depth);
+ pi.dispatch (info_type::call_load, this->db_, pobj, &d);
+ };
+
+ rsts.load_delayed (tc_.version ());
+ l.unlock ();
+
+ ce = callback_event::post_load;
+ pi.dispatch (info_type::call_callback, this->db_, pobj, &ce);
+ object_traits::pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::id_type
+ polymorphic_object_result_impl<T>::
+ load_id ()
+ {
+ load_image ();
+ return root_traits::id (statements_.root_statements ().image ());
+ }
+
+ template <typename T>
+ typename polymorphic_object_result_impl<T>::discriminator_type
+ polymorphic_object_result_impl<T>::
+ load_discriminator ()
+ {
+ load_image ();
+ return root_traits::discriminator (
+ statements_.root_statements ().image ());
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (statement_->next ())
+ count_++;
+ else
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T, typename R>
+ struct polymorphic_image_rebind
+ {
+ // Derived type version.
+ //
+ typedef object_traits_impl<T, id_pgsql> traits;
+
+ static bool
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (traits::check_version (sts.select_image_versions (), im))
+ {
+ binding& b (sts.select_image_binding (traits::depth));
+ object_traits_calls<T> tc (svm);
+ tc.bind (b.bind, 0, 0, im, statement_select);
+ traits::update_version (
+ sts.select_image_versions (), im, sts.select_image_bindings ());
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ template <typename R>
+ struct polymorphic_image_rebind<R, R>
+ {
+ // Root type version.
+ //
+ typedef object_traits_impl<R, id_pgsql> traits;
+
+ static bool
+ rebind (typename traits::statements_type& sts,
+ const schema_version_migration* svm)
+ {
+ typename traits::image_type& im (sts.image ());
+
+ if (im.version != sts.select_image_version ())
+ {
+ binding& b (sts.select_image_binding ());
+ object_traits_calls<R> tc (svm);
+ tc.bind (b.bind, im, statement_select);
+ sts.select_image_version (im.version);
+ b.version++;
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ load_image ()
+ {
+ typedef polymorphic_image_rebind<object_type, root_type> image_rebind;
+
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ image_rebind::rebind (statements_, tc_.version ());
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (image_rebind::rebind (statements_, tc_.version ()))
+ statement_->reload ();
+ }
+ }
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t polymorphic_object_result_impl<T>::
+ size ()
+ {
+ return this->end_ ? count_ : statement_->result_size ();
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/polymorphic-object-statements.hxx b/libodb-pgsql/odb/pgsql/polymorphic-object-statements.hxx
new file mode 100644
index 0000000..717410a
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/polymorphic-object-statements.hxx
@@ -0,0 +1,513 @@
+// file : odb/pgsql/polymorphic-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+#define ODB_PGSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/pgsql-types.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/statements-base.hxx>
+#include <odb/pgsql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // Implementation for polymorphic objects.
+ //
+
+ template <typename T>
+ class polymorphic_root_object_statements: public object_statements<T>
+ {
+ public:
+ typedef typename object_statements<T>::connection_type connection_type;
+ typedef typename object_statements<T>::object_traits object_traits;
+ typedef typename object_statements<T>::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::discriminator_image_type
+ discriminator_image_type;
+
+ typedef
+ typename object_statements<T>::select_statement_type
+ select_statement_type;
+
+ public:
+ // Interface compatibility with derived_object_statements.
+ //
+ typedef polymorphic_root_object_statements root_statements_type;
+
+ root_statements_type&
+ root_statements ()
+ {
+ return *this;
+ }
+
+ public:
+ // Discriminator binding.
+ //
+ discriminator_image_type&
+ discriminator_image () {return discriminator_image_;}
+
+ std::size_t
+ discriminator_image_version () const
+ {return discriminator_image_version_;}
+
+ void
+ discriminator_image_version (std::size_t v)
+ {discriminator_image_version_ = v;}
+
+ binding&
+ discriminator_image_binding () {return discriminator_image_binding_;}
+
+ bool*
+ discriminator_image_truncated () {return discriminator_image_truncated_;}
+
+ // Id binding for discriminator retrieval.
+ //
+ id_image_type&
+ discriminator_id_image () {return discriminator_id_image_;}
+
+ std::size_t
+ discriminator_id_image_version () const
+ {return discriminator_id_image_version_;}
+
+ void
+ discriminator_id_image_version (std::size_t v)
+ {discriminator_id_image_version_ = v;}
+
+ binding&
+ discriminator_id_image_binding ()
+ {return discriminator_id_image_binding_;}
+
+ // Expose id native binding from object_statements.
+ //
+ native_binding&
+ id_image_native_binding () {return this->id_image_native_binding_;}
+
+ //
+ //
+ select_statement_type&
+ find_discriminator_statement ()
+ {
+ if (find_discriminator_ == 0)
+ find_discriminator_.reset (
+ new (details::shared) select_statement_type (
+ this->conn_,
+ object_traits::find_discriminator_statement_name,
+ object_traits::find_discriminator_statement,
+ false, // Doesn't need to be processed.
+ false, // Don't optimize.
+ object_traits::find_statement_types, // The same as find (id).
+ id_column_count,
+ discriminator_id_image_binding_,
+ discriminator_id_image_native_binding_,
+ discriminator_image_binding_,
+ false));
+
+ return *find_discriminator_;
+ }
+
+ public:
+ polymorphic_root_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_root_object_statements ();
+
+ // Static "override" (statements type).
+ //
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (this->locked ());
+
+ if (!this->delayed_.empty ())
+ this->template load_delayed_<polymorphic_root_object_statements> (
+ svm);
+ }
+
+ public:
+ static const std::size_t id_column_count =
+ object_statements<T>::id_column_count;
+
+ static const std::size_t discriminator_column_count =
+ object_traits::discriminator_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ private:
+ // Discriminator image.
+ //
+ discriminator_image_type discriminator_image_;
+ std::size_t discriminator_image_version_;
+ binding discriminator_image_binding_;
+ bind discriminator_image_bind_[discriminator_column_count +
+ managed_optimistic_column_count];
+ bool discriminator_image_truncated_[discriminator_column_count +
+ managed_optimistic_column_count];
+
+ // Id image for discriminator retrieval (only used as a parameter).
+ //
+ id_image_type discriminator_id_image_;
+ std::size_t discriminator_id_image_version_;
+ binding discriminator_id_image_binding_;
+ native_binding discriminator_id_image_native_binding_;
+ bind discriminator_id_image_bind_[id_column_count];
+ char* discriminator_id_image_values_[id_column_count];
+ int discriminator_id_image_lengths_[id_column_count];
+ int discriminator_id_image_formats_[id_column_count];
+
+ details::shared_ptr<select_statement_type> find_discriminator_;
+ };
+
+ template <typename T>
+ class polymorphic_derived_object_statements: public statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_pgsql> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::id_image_type id_image_type;
+ typedef typename object_traits::image_type image_type;
+
+ typedef typename object_traits::root_type root_type;
+ typedef
+ polymorphic_root_object_statements<root_type>
+ root_statements_type;
+
+ typedef typename object_traits::base_type base_type;
+ typedef
+ typename object_traits::base_traits::statements_type
+ base_statements_type;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef pgsql::insert_statement insert_statement_type;
+ typedef pgsql::select_statement select_statement_type;
+ typedef pgsql::update_statement update_statement_type;
+ typedef pgsql::delete_statement delete_statement_type;
+
+ typedef typename root_statements_type::auto_lock auto_lock;
+
+ public:
+ polymorphic_derived_object_statements (connection_type&);
+
+ virtual
+ ~polymorphic_derived_object_statements ();
+
+ public:
+ // Delayed loading.
+ //
+ static void
+ delayed_loader (odb::database&,
+ const id_type&,
+ root_type&,
+ const schema_version_migration*);
+ public:
+ // Root and immediate base statements.
+ //
+ root_statements_type&
+ root_statements ()
+ {
+ return root_statements_;
+ }
+
+ base_statements_type&
+ base_statements ()
+ {
+ return base_statements_;
+ }
+
+ public:
+ // Object image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ std::size_t
+ insert_id_binding_version () const { return insert_id_binding_version_;}
+
+ void
+ insert_id_binding_version (std::size_t v) {insert_id_binding_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t*
+ select_image_versions () { return select_image_versions_;}
+
+ binding*
+ select_image_bindings () {return select_image_bindings_;}
+
+ binding&
+ select_image_binding (std::size_t d)
+ {
+ return select_image_bindings_[object_traits::depth - d];
+ }
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Object id binding (comes from the root statements).
+ //
+ id_image_type&
+ id_image () {return root_statements_.id_image ();}
+
+ std::size_t
+ id_image_version () const {return root_statements_.id_image_version ();}
+
+ void
+ id_image_version (std::size_t v) {root_statements_.id_image_version (v);}
+
+ binding&
+ id_image_binding () {return root_statements_.id_image_binding ();}
+
+ binding&
+ optimistic_id_image_binding () {
+ return root_statements_.optimistic_id_image_binding ();}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement_name,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ object_traits::persist_statement_types,
+ insert_column_count,
+ insert_image_binding_,
+ insert_image_native_binding_,
+ 0,
+ false));
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement (std::size_t d)
+ {
+ std::size_t i (object_traits::depth - d);
+ details::shared_ptr<select_statement_type>& p (find_[i]);
+
+ if (p == 0)
+ p.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statement_names[i],
+ object_traits::find_statements[i],
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ object_traits::find_statement_types,
+ id_column_count,
+ root_statements_.id_image_binding (),
+ root_statements_.id_image_native_binding (),
+ select_image_bindings_[i],
+ false));
+
+ return *p;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement_name,
+ object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
+ object_traits::update_statement_types,
+ update_column_count + id_column_count,
+ update_image_binding_,
+ update_image_native_binding_,
+ false));
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement_name,
+ object_traits::erase_statement,
+ object_traits::find_statement_types, // The same as find (id).
+ id_column_count,
+ root_statements_.id_image_binding (),
+ root_statements_.id_image_native_binding (),
+ false));
+
+ return *erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ image_,
+ id_image (),
+ id_image_binding (),
+ &id_image_binding (), // Note, not id+version.
+ root_statements_.id_image_native_binding (),
+ object_traits::find_statement_types);
+ }
+
+ public:
+ // select = total - id - separate_load + base::select
+ // insert = total - inverse
+ // update = total - inverse - id - readonly - separate_update
+ //
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ id_column_count -
+ object_traits::separate_load_column_count +
+ base_statements_type::select_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count;
+
+ static const std::size_t update_column_count =
+ insert_column_count -
+ object_traits::id_column_count -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ private:
+ polymorphic_derived_object_statements (
+ const polymorphic_derived_object_statements&);
+
+ polymorphic_derived_object_statements&
+ operator= (const polymorphic_derived_object_statements&);
+
+ private:
+ root_statements_type& root_statements_;
+ base_statements_type& base_statements_;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ image_type image_;
+
+ // Select binding. Here we are have an array of statements/bindings
+ // one for each depth. In other words, if we have classes root, base,
+ // and derived, then we have the following array of statements:
+ //
+ // [0] d + b + r
+ // [1] d + b
+ // [2] d
+ //
+ // Also, because we have a chain of images bound to these statements,
+ // we have an array of versions, one entry for each base plus one for
+ // our own image.
+ //
+ // A poly-abstract class only needs the first statement and in this
+ // case we have only one entry in the the bindings and statements
+ // arrays (but not versions; we still have a chain of images).
+ //
+ std::size_t select_image_versions_[object_traits::depth];
+ binding select_image_bindings_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ bind select_image_bind_[select_column_count];
+ bool select_image_truncated_[select_column_count];
+
+ // Insert binding. The id binding is copied from the hierarchy root.
+ //
+ std::size_t insert_image_version_;
+ std::size_t insert_id_binding_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[insert_column_count];
+ native_binding insert_image_native_binding_;
+ char* insert_image_values_[insert_column_count];
+ int insert_image_lengths_[insert_column_count];
+ int insert_image_formats_[insert_column_count];
+
+ // Update binding. The id suffix binding is copied from the hierarchy
+ // root.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+ binding update_image_binding_;
+ bind update_image_bind_[update_column_count + id_column_count];
+ native_binding update_image_native_binding_;
+ char* update_image_values_[update_column_count + id_column_count];
+ int update_image_lengths_[update_column_count + id_column_count];
+ int update_image_formats_[update_column_count + id_column_count];
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_[
+ object_traits::abstract ? 1 : object_traits::depth];
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+ };
+ }
+}
+
+#include <odb/pgsql/polymorphic-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_POLYMORPHIC_OBJECT_STATEMENTS_HXX
diff --git a/libodb-pgsql/odb/pgsql/polymorphic-object-statements.txx b/libodb-pgsql/odb/pgsql/polymorphic-object-statements.txx
new file mode 100644
index 0000000..8472fca
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/polymorphic-object-statements.txx
@@ -0,0 +1,158 @@
+// file : odb/pgsql/polymorphic-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/transaction.hxx>
+#include <odb/pgsql/statement-cache.hxx>
+#include <odb/pgsql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // polymorphic_root_object_statements
+ //
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ ~polymorphic_root_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_root_object_statements<T>::
+ polymorphic_root_object_statements (connection_type& conn)
+ : object_statements<T> (conn),
+ discriminator_image_binding_ (discriminator_image_bind_,
+ discriminator_column_count +
+ managed_optimistic_column_count),
+ discriminator_id_image_binding_ (discriminator_id_image_bind_,
+ id_column_count),
+ discriminator_id_image_native_binding_ (
+ discriminator_id_image_values_,
+ discriminator_id_image_lengths_,
+ discriminator_id_image_formats_,
+ id_column_count)
+ {
+ discriminator_image_.version = 0;
+ discriminator_id_image_.version = 0;
+
+ discriminator_image_version_ = 0;
+ discriminator_id_image_version_ = 0;
+
+ std::memset (discriminator_image_bind_,
+ 0,
+ sizeof (discriminator_image_bind_));
+ std::memset (discriminator_id_image_bind_,
+ 0,
+ sizeof (discriminator_id_image_bind_));
+ std::memset (discriminator_image_truncated_,
+ 0,
+ sizeof (discriminator_image_truncated_));
+
+ for (std::size_t i (0);
+ i < discriminator_column_count + managed_optimistic_column_count;
+ ++i)
+ {
+ discriminator_image_bind_[i].truncated =
+ discriminator_image_truncated_ + i;
+ }
+ }
+
+ //
+ // polymorphic_derived_object_statements
+ //
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ ~polymorphic_derived_object_statements ()
+ {
+ }
+
+ template <typename T>
+ polymorphic_derived_object_statements<T>::
+ polymorphic_derived_object_statements (connection_type& conn)
+ : statements_base (conn),
+ root_statements_ (conn.statement_cache ().find_object<root_type> ()),
+ base_statements_ (conn.statement_cache ().find_object<base_type> ()),
+ insert_image_binding_ (insert_image_bind_, insert_column_count),
+ insert_image_native_binding_ (insert_image_values_,
+ insert_image_lengths_,
+ insert_image_formats_,
+ insert_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count),
+ update_image_native_binding_ (update_image_values_,
+ update_image_lengths_,
+ update_image_formats_,
+ update_column_count + id_column_count)
+ {
+ image_.base = &base_statements_.image ();
+ image_.version = 0;
+
+ for (std::size_t i (0); i < object_traits::depth; ++i)
+ select_image_versions_[i] = 0;
+
+ for (std::size_t i (0);
+ i < (object_traits::abstract ? 1 : object_traits::depth);
+ ++i)
+ {
+ select_image_bindings_[i].bind = select_image_bind_;
+ select_image_bindings_[i].count = object_traits::find_column_counts[i];
+ }
+
+ insert_image_version_ = 0;
+ insert_id_binding_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+
+ template <typename T>
+ void polymorphic_derived_object_statements<T>::
+ delayed_loader (odb::database& db,
+ const id_type& id,
+ root_type& robj,
+ const schema_version_migration* svm)
+ {
+ connection_type& conn (transaction::current ().connection (db));
+ polymorphic_derived_object_statements& sts (
+ conn.statement_cache ().find_object<object_type> ());
+ root_statements_type& rsts (sts.root_statements ());
+
+ object_type& obj (static_cast<object_type&> (robj));
+
+ // The same code as in object_statements::load_delayed_().
+ //
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (sts, &id))
+ throw object_not_persistent ();
+
+ object_traits::callback (db, obj, callback_event::pre_load);
+ tc.init (obj, sts.image (), &db);
+ tc.load_ (sts, obj, false); // Load containers, etc.
+
+ rsts.load_delayed (svm);
+
+ {
+ typename root_statements_type::auto_unlock u (rsts);
+ object_traits::callback (db, obj, callback_event::post_load);
+ }
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/prepared-query.cxx b/libodb-pgsql/odb/pgsql/prepared-query.cxx
new file mode 100644
index 0000000..a6d783c
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/prepared-query.cxx
@@ -0,0 +1,15 @@
+// file : odb/pgsql/prepared-query.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/prepared-query.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ prepared_query_impl::
+ ~prepared_query_impl ()
+ {
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/prepared-query.hxx b/libodb-pgsql/odb/pgsql/prepared-query.hxx
new file mode 100644
index 0000000..3622226
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/prepared-query.hxx
@@ -0,0 +1,34 @@
+// file : odb/pgsql/prepared-query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_PREPARED_QUERY_HXX
+#define ODB_PGSQL_PREPARED_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/prepared-query.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/query.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ struct LIBODB_PGSQL_EXPORT prepared_query_impl: odb::prepared_query_impl
+ {
+ virtual
+ ~prepared_query_impl ();
+
+ prepared_query_impl (odb::connection& c): odb::prepared_query_impl (c) {}
+
+ pgsql::query_base query;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_PREPARED_QUERY_HXX
diff --git a/libodb-pgsql/odb/pgsql/query-const-expr.cxx b/libodb-pgsql/odb/pgsql/query-const-expr.cxx
new file mode 100644
index 0000000..c910620
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query-const-expr.cxx
@@ -0,0 +1,14 @@
+// file : odb/pgsql/query-const-expr.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/query.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // Sun CC cannot handle this in query.cxx.
+ //
+ const query_base query_base::true_expr (true);
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/query-dynamic.cxx b/libodb-pgsql/odb/pgsql/query-dynamic.cxx
new file mode 100644
index 0000000..ebd62de
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query-dynamic.cxx
@@ -0,0 +1,157 @@
+// file : odb/pgsql/query-dynamic.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+
+#include <odb/pgsql/query-dynamic.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ static const char* logic_operators[] = {") AND (", ") OR ("};
+ static const char* comp_operators[] = {"=", "!=", "<", ">", "<=", ">="};
+
+ static void
+ translate (query_base& q, const odb::query_base& s, size_t p)
+ {
+ typedef odb::query_base::clause_part part;
+
+ const part& x (s.clause ()[p]);
+
+ switch (x.kind)
+ {
+ case part::kind_column:
+ {
+ const query_column_base* c (
+ static_cast<const query_column_base*> (
+ x.native_info[id_pgsql].column));
+
+ q.append (c->table (), c->column ());
+ break;
+ }
+ case part::kind_param_val:
+ case part::kind_param_ref:
+ {
+ const query_column_base* c (
+ static_cast<const query_column_base*> (
+ x.native_info[id_pgsql].column));
+
+ query_param_factory f (
+ reinterpret_cast<query_param_factory> (
+ x.native_info[id_pgsql].param_factory));
+
+ const odb::query_param* p (
+ reinterpret_cast<const odb::query_param*> (x.data));
+
+ q.append (f (p->value, x.kind == part::kind_param_ref),
+ c->conversion ());
+ break;
+ }
+ case part::kind_native:
+ {
+ q.append (s.strings ()[x.data]);
+ break;
+ }
+ case part::kind_true:
+ case part::kind_false:
+ {
+ q.append (x.kind == part::kind_true);
+ break;
+ }
+ case part::op_add:
+ {
+ translate (q, s, x.data);
+ translate (q, s, p - 1);
+ break;
+ }
+ case part::op_and:
+ case part::op_or:
+ {
+ q += "(";
+ translate (q, s, x.data);
+ q += logic_operators[x.kind - part::op_and];
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_not:
+ {
+ q += "NOT (";
+ translate (q, s, p - 1);
+ q += ")";
+ break;
+ }
+ case part::op_null:
+ case part::op_not_null:
+ {
+ translate (q, s, p - 1);
+ q += (x.kind == part::op_null ? "IS NULL" : "IS NOT NULL");
+ break;
+ }
+ case part::op_in:
+ {
+ if (x.data != 0)
+ {
+ size_t b (p - x.data);
+
+ translate (q, s, b - 1); // column
+ q += "IN (";
+
+ for (size_t i (b); i != p; ++i)
+ {
+ if (i != b)
+ q += ",";
+
+ translate (q, s, i);
+ }
+
+ q += ")";
+ }
+ else
+ q.append (false);
+
+ break;
+ }
+ case part::op_like:
+ {
+ translate (q, s, p - 2); // column
+ q += "LIKE";
+ translate (q, s, p - 1); // pattern
+ break;
+ }
+ case part::op_like_escape:
+ {
+ translate (q, s, p - 3); // column
+ q += "LIKE";
+ translate (q, s, p - 2); // pattern
+ q += "ESCAPE";
+ translate (q, s, p - 1); // escape
+ break;
+ }
+ case part::op_eq:
+ case part::op_ne:
+ case part::op_lt:
+ case part::op_gt:
+ case part::op_le:
+ case part::op_ge:
+ {
+ translate (q, s, x.data);
+ q += comp_operators[x.kind - part::op_eq];
+ translate (q, s, p - 1);
+ break;
+ }
+ }
+ }
+
+ query_base::
+ query_base (const odb::query_base& q)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ if (!q.empty ())
+ translate (*this, q, q.clause ().size () - 1);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/query-dynamic.hxx b/libodb-pgsql/odb/pgsql/query-dynamic.hxx
new file mode 100644
index 0000000..df87519
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query-dynamic.hxx
@@ -0,0 +1,32 @@
+// file : odb/pgsql/query-dynamic.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_QUERY_DYNAMIC_HXX
+#define ODB_PGSQL_QUERY_DYNAMIC_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/query.hxx>
+#include <odb/query-dynamic.hxx>
+
+#include <odb/pgsql/query.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ typedef details::shared_ptr<query_param> (*query_param_factory) (
+ const void* val, bool by_ref);
+
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void*, bool);
+ }
+}
+
+#include <odb/pgsql/query-dynamic.ixx>
+#include <odb/pgsql/query-dynamic.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_QUERY_DYNAMIC_HXX
diff --git a/libodb-pgsql/odb/pgsql/query-dynamic.ixx b/libodb-pgsql/odb/pgsql/query-dynamic.ixx
new file mode 100644
index 0000000..016d213
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query-dynamic.ixx
@@ -0,0 +1,26 @@
+// file : odb/pgsql/query-dynamic.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ //
+ template <typename T, database_type_id ID>
+ inline query_column<T, ID>::
+ query_column (odb::query_column<T>& qc,
+ const char* table, const char* column, const char* conv)
+ : query_column_base (table, column, conv)
+ {
+ native_column_info& ci (qc.native_info[id_pgsql]);
+ ci.column = static_cast<query_column_base*> (this);
+
+ // For some reason GCC needs this statically-typed pointer in
+ // order to instantiate the functions.
+ //
+ query_param_factory f (&query_param_factory_impl<T, ID>);
+ ci.param_factory = reinterpret_cast<void*> (f);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/query-dynamic.txx b/libodb-pgsql/odb/pgsql/query-dynamic.txx
new file mode 100644
index 0000000..7cfaf63
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query-dynamic.txx
@@ -0,0 +1,20 @@
+// file : odb/pgsql/query-dynamic.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T, database_type_id ID>
+ details::shared_ptr<query_param>
+ query_param_factory_impl (const void* val, bool by_ref)
+ {
+ const T& v (*static_cast<const T*> (val));
+
+ return details::shared_ptr<query_param> (
+ by_ref
+ ? new (details::shared) query_param_impl<T, ID> (ref_bind<T> (v))
+ : new (details::shared) query_param_impl<T, ID> (val_bind<T> (v)));
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/query.cxx b/libodb-pgsql/odb/pgsql/query.cxx
new file mode 100644
index 0000000..b096c97
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query.cxx
@@ -0,0 +1,444 @@
+// file : odb/pgsql/query.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+#include <cassert>
+#include <sstream>
+
+#include <odb/pgsql/query.hxx>
+#include <odb/pgsql/statement.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // query_param
+ //
+ query_param::
+ ~query_param ()
+ {
+ }
+
+ // query_base
+ //
+ query_base::
+ query_base (const query_base& q)
+ : clause_ (q.clause_),
+ parameters_ (q.parameters_),
+ bind_ (q.bind_),
+ binding_ (0, 0),
+ values_ (q.values_),
+ lengths_ (q.lengths_),
+ formats_ (q.formats_),
+ types_ (q.types_),
+ native_binding_ (0, 0, 0, 0)
+ {
+ // Here and below we want to maintain up to date binding info so
+ // that the call to parameters_binding() below is an immutable
+ // operation, provided the query does not have any by-reference
+ // parameters. This way a by-value-only query can be shared
+ // between multiple threads without the need for synchronization.
+ //
+ if (size_t n = bind_.size ())
+ {
+ binding_.bind = &bind_[0];
+ binding_.count = n;
+ binding_.version++;
+
+ native_binding_.values = &values_[0];
+ native_binding_.lengths = &lengths_[0];
+ native_binding_.formats = &formats_[0];
+ native_binding_.count = n;
+
+ assert (values_.size () == n);
+ assert (lengths_.size () == n);
+ assert (formats_.size () == n);
+ assert (types_.size () == n);
+
+ statement::bind_param (native_binding_, binding_);
+ }
+ }
+
+ query_base& query_base::
+ operator= (const query_base& q)
+ {
+ if (this != &q)
+ {
+ clause_ = q.clause_;
+ parameters_ = q.parameters_;
+ bind_ = q.bind_;
+
+ size_t n (bind_.size ());
+
+ binding_.count = n;
+ binding_.version++;
+
+ values_ = q.values_;
+ lengths_ = q.lengths_;
+ formats_ = q.formats_;
+ types_ = q.types_;
+
+ native_binding_.count = n;
+
+ assert (values_.size () == n);
+ assert (lengths_.size () == n);
+ assert (formats_.size () == n);
+ assert (types_.size () == n);
+
+ if (n != 0)
+ {
+ binding_.bind = &bind_[0];
+
+ native_binding_.values = &values_[0];
+ native_binding_.lengths = &lengths_[0];
+ native_binding_.formats = &formats_[0];
+
+ statement::bind_param (native_binding_, binding_);
+ }
+ }
+
+ return *this;
+ }
+
+ query_base& query_base::
+ operator+= (const query_base& q)
+ {
+ clause_.insert (clause_.end (), q.clause_.begin (), q.clause_.end ());
+
+ size_t n (bind_.size ());
+
+ parameters_.insert (
+ parameters_.end (), q.parameters_.begin (), q.parameters_.end ());
+
+ bind_.insert (
+ bind_.end (), q.bind_.begin (), q.bind_.end ());
+
+ values_.insert (
+ values_.end (), q.values_.begin (), q.values_.end ());
+
+ lengths_.insert (
+ lengths_.end (), q.lengths_.begin (), q.lengths_.end ());
+
+ formats_.insert (
+ formats_.end (), q.formats_.begin (), q.formats_.end ());
+
+ types_.insert (
+ types_.end (), q.types_.begin (), q.types_.end ());
+
+ if (n != bind_.size ())
+ {
+ n = bind_.size ();
+
+ binding_.bind = &bind_[0];
+ binding_.count = n;
+ binding_.version++;
+
+ assert (values_.size () == n);
+ assert (lengths_.size () == n);
+ assert (formats_.size () == n);
+ assert (types_.size () == n);
+
+ native_binding_.values = &values_[0];
+ native_binding_.lengths = &lengths_[0];
+ native_binding_.formats = &formats_[0];
+ native_binding_.count = n;
+
+ statement::bind_param (native_binding_, binding_);
+ }
+
+ return *this;
+ }
+
+ void query_base::
+ append (const string& q)
+ {
+ if (!clause_.empty () &&
+ clause_.back ().kind == clause_part::kind_native)
+ {
+ string& s (clause_.back ().part);
+
+ char first (!q.empty () ? q[0] : ' ');
+ char last (!s.empty () ? s[s.size () - 1] : ' ');
+
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ s += ' ';
+
+ s += q;
+ }
+ else
+ clause_.push_back (clause_part (clause_part::kind_native, q));
+ }
+
+ void query_base::
+ append (const char* table, const char* column)
+ {
+ string s (table);
+ s += '.';
+ s += column;
+
+ clause_.push_back (clause_part (clause_part::kind_column, s));
+ }
+
+ void query_base::
+ append (details::shared_ptr<query_param> p, const char* conv)
+ {
+ clause_.push_back (clause_part (clause_part::kind_param));
+
+ if (conv != 0)
+ clause_.back ().part = conv;
+
+ parameters_.push_back (p);
+ bind_.push_back (bind ());
+ binding_.bind = &bind_[0];
+ binding_.count = bind_.size ();
+ binding_.version++;
+
+ bind* b (&bind_.back ());
+ memset (b, 0, sizeof (bind));
+ p->bind (b);
+
+ values_.push_back (0);
+ lengths_.push_back (0);
+ formats_.push_back (0);
+ native_binding_.values = &values_[0];
+ native_binding_.lengths = &lengths_[0];
+ native_binding_.formats = &formats_[0];
+
+ // native_binding_.count should always equal binding_.count.
+ // At this point, we know that we have added one element to
+ // each array, so there is no need to check.
+ //
+ native_binding_.count = binding_.count;
+
+ types_.push_back (p->oid ());
+
+ statement::bind_param (native_binding_, binding_);
+ }
+
+ void query_base::
+ init_parameters () const
+ {
+ bool ref (false), inc_ver (false);
+
+ for (size_t i (0); i < parameters_.size (); ++i)
+ {
+ query_param& p (*parameters_[i]);
+
+ if (p.reference ())
+ {
+ ref = true;
+
+ if (p.init ())
+ {
+ p.bind (&bind_[i]);
+ inc_ver = true;
+ }
+ }
+ }
+
+ if (ref)
+ {
+ statement::bind_param (native_binding_, binding_);
+
+ if (inc_ver)
+ binding_.version++;
+ }
+ }
+
+ static bool
+ check_prefix (const string& s)
+ {
+ string::size_type n;
+
+ // It is easier to compare to upper and lower-case versions
+ // rather than getting involved with the portable case-
+ // insensitive string comparison mess.
+ //
+ if (s.compare (0, (n = 5), "WHERE") == 0 ||
+ s.compare (0, (n = 5), "where") == 0 ||
+ s.compare (0, (n = 6), "SELECT") == 0 ||
+ s.compare (0, (n = 6), "select") == 0 ||
+ s.compare (0, (n = 8), "ORDER BY") == 0 ||
+ s.compare (0, (n = 8), "order by") == 0 ||
+ s.compare (0, (n = 8), "GROUP BY") == 0 ||
+ s.compare (0, (n = 8), "group by") == 0 ||
+ s.compare (0, (n = 6), "HAVING") == 0 ||
+ s.compare (0, (n = 6), "having") == 0 ||
+ s.compare (0, (n = 4), "WITH") == 0 ||
+ s.compare (0, (n = 4), "with") == 0)
+ {
+ // It either has to be an exact match, or there should be
+ // a whitespace following the keyword.
+ //
+ if (s.size () == n || s[n] == ' ' || s[n] == '\n' || s[n] =='\t')
+ return true;
+ }
+
+ return false;
+ }
+
+ void query_base::
+ optimize ()
+ {
+ // Remove a single TRUE literal or one that is followe by one of
+ // the other clauses. This avoids useless WHERE clauses like
+ //
+ // WHERE TRUE GROUP BY foo
+ //
+ clause_type::iterator i (clause_.begin ()), e (clause_.end ());
+
+ if (i != e && i->kind == clause_part::kind_bool && i->bool_part)
+ {
+ clause_type::iterator j (i + 1);
+
+ if (j == e ||
+ (j->kind == clause_part::kind_native && check_prefix (j->part)))
+ clause_.erase (i);
+ }
+ }
+
+ const char* query_base::
+ clause_prefix () const
+ {
+ if (!clause_.empty ())
+ {
+ const clause_part& p (clause_.front ());
+
+ if (p.kind == clause_part::kind_native && check_prefix (p.part))
+ return "";
+
+ return "WHERE ";
+ }
+
+ return "";
+ }
+
+ string query_base::
+ clause () const
+ {
+ string r;
+ size_t param (1);
+
+ for (clause_type::const_iterator i (clause_.begin ()),
+ end (clause_.end ());
+ i != end;
+ ++i)
+ {
+ char last (!r.empty () ? r[r.size () - 1] : ' ');
+
+ switch (i->kind)
+ {
+ case clause_part::kind_column:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ r += i->part;
+ break;
+ }
+ case clause_part::kind_param:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ ostringstream os;
+ os << param++;
+
+ // Add the conversion expression, if any.
+ //
+ string::size_type p (0);
+ if (!i->part.empty ())
+ {
+ p = i->part.find ("(?)");
+ r.append (i->part, 0, p);
+ }
+
+ r += '$';
+ r += os.str ();
+
+ if (!i->part.empty ())
+ r.append (i->part, p + 3, string::npos);
+
+ break;
+ }
+ case clause_part::kind_native:
+ {
+ // We don't want extra spaces after '(' as well as before ','
+ // and ')'.
+ //
+ const string& p (i->part);
+ char first (!p.empty () ? p[0] : ' ');
+
+ if (last != ' ' && last != '\n' && last != '(' &&
+ first != ' ' && first != '\n' && first != ',' && first != ')')
+ r += ' ';
+
+ r += p;
+ break;
+ }
+ case clause_part::kind_bool:
+ {
+ if (last != ' ' && last != '\n' && last != '(')
+ r += ' ';
+
+ r += i->bool_part ? "TRUE" : "FALSE";
+ break;
+ }
+ }
+ }
+
+ return clause_prefix () + r;
+ }
+
+ query_base
+ operator&& (const query_base& x, const query_base& y)
+ {
+ // Optimize cases where one or both sides are constant truth.
+ //
+ bool xt (x.const_true ()), yt (y.const_true ());
+
+ if (xt && yt)
+ return x;
+
+ if (xt)
+ return y;
+
+ if (yt)
+ return x;
+
+ query_base r ("(");
+ r += x;
+ r += ") AND (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator|| (const query_base& x, const query_base& y)
+ {
+ query_base r ("(");
+ r += x;
+ r += ") OR (";
+ r += y;
+ r += ")";
+ return r;
+ }
+
+ query_base
+ operator! (const query_base& x)
+ {
+ query_base r ("NOT (");
+ r += x;
+ r += ")";
+ return r;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/query.hxx b/libodb-pgsql/odb/pgsql/query.hxx
new file mode 100644
index 0000000..42182d6
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query.hxx
@@ -0,0 +1,2181 @@
+// file : odb/pgsql/query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_QUERY_HXX
+#define ODB_PGSQL_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx> // odb::query_column
+#include <odb/query.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/traits.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/pgsql-oid.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+#include <odb/pgsql/details/conversion.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ struct val_bind
+ {
+ typedef const T& type;
+
+ explicit
+ val_bind (type v): val (v) {}
+
+ type val;
+ };
+
+ template <typename T, std::size_t N>
+ struct val_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ val_bind (type v): val (v) {}
+
+ type val;
+ };
+
+ template <typename T>
+ struct ref_bind
+ {
+ typedef const T& type;
+
+ explicit
+ ref_bind (type r): ref (r) {}
+
+ const void*
+ ptr () const {return &ref;}
+
+ type ref;
+ };
+
+ template <typename T, std::size_t N>
+ struct ref_bind<T[N]>
+ {
+ typedef const T* type;
+
+ explicit
+ ref_bind (type r): ref (r) {}
+
+ // Allow implicit conversion from decayed ref_bind's.
+ //
+ ref_bind (ref_bind<T*> r): ref (r.ref) {}
+ ref_bind (ref_bind<const T*> r): ref (r.ref) {}
+
+ const void*
+ ptr () const {return ref;}
+
+ type ref;
+ };
+
+ template <typename T, database_type_id ID>
+ struct val_bind_typed: val_bind<T>
+ {
+ explicit
+ val_bind_typed (typename val_bind<T>::type v): val_bind<T> (v) {}
+ };
+
+ template <typename T, database_type_id ID>
+ struct ref_bind_typed: ref_bind<T>
+ {
+ explicit
+ ref_bind_typed (typename ref_bind<T>::type r): ref_bind<T> (r) {}
+ };
+
+ struct LIBODB_PGSQL_EXPORT query_param: details::shared_base
+ {
+ typedef pgsql::bind bind_type;
+
+ virtual
+ ~query_param ();
+
+ bool
+ reference () const
+ {
+ return value_ != 0;
+ }
+
+ virtual bool
+ init () = 0;
+
+ virtual void
+ bind (bind_type*) = 0;
+
+ virtual unsigned int
+ oid () const = 0;
+
+ protected:
+ query_param (const void* value)
+ : value_ (value)
+ {
+ }
+
+ protected:
+ const void* value_;
+ };
+
+ //
+ //
+ template <typename T, database_type_id ID>
+ struct query_column;
+
+ class LIBODB_PGSQL_EXPORT query_base
+ {
+ public:
+ struct clause_part
+ {
+ enum kind_type
+ {
+ kind_column,
+ kind_param,
+ kind_native,
+ kind_bool
+ };
+
+ clause_part (kind_type k): kind (k), bool_part (false) {}
+ clause_part (kind_type k, const std::string& p)
+ : kind (k), part (p), bool_part (false) {}
+ clause_part (bool p): kind (kind_bool), bool_part (p) {}
+
+ kind_type kind;
+ std::string part; // If kind is param, then part is conversion expr.
+ bool bool_part;
+ };
+
+ query_base ()
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ }
+
+ // True or false literal.
+ //
+ explicit
+ query_base (bool v)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ append (v);
+ }
+
+ explicit
+ query_base (const char* native)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ explicit
+ query_base (const std::string& native)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ clause_.push_back (clause_part (clause_part::kind_native, native));
+ }
+
+ query_base (const char* table, const char* column)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ append (table, column);
+ }
+
+ template <typename T>
+ explicit
+ query_base (val_bind<T> v)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ *this += v;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (val_bind_typed<T, ID> v)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ *this += v;
+ }
+
+ template <typename T>
+ explicit
+ query_base (ref_bind<T> r)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ *this += r;
+ }
+
+ template <typename T, database_type_id ID>
+ explicit
+ query_base (ref_bind_typed<T, ID> r)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ *this += r;
+ }
+
+ template <database_type_id ID>
+ query_base (const query_column<bool, ID>&);
+
+ // Translate common query representation to PostgreSQL native. Defined
+ // in query-dynamic.cxx
+ //
+ query_base (const odb::query_base&);
+
+ // Copy c-tor and assignment.
+ //
+ query_base (const query_base&);
+
+ query_base&
+ operator= (const query_base&);
+
+ public:
+ std::string
+ clause () const;
+
+ const char*
+ clause_prefix () const;
+
+ // Initialize the by-reference parameters from bound variables.
+ //
+ void
+ init_parameters () const;
+
+ native_binding&
+ parameters_binding () const;
+
+ const unsigned int*
+ parameter_types () const
+ {
+ return types_.empty () ? 0 : &types_[0];
+ }
+
+ std::size_t
+ parameter_count () const
+ {
+ return parameters_.size ();
+ }
+
+ public:
+ bool
+ empty () const
+ {
+ return clause_.empty ();
+ }
+
+ static const query_base true_expr;
+
+ bool
+ const_true () const
+ {
+ return clause_.size () == 1 &&
+ clause_.front ().kind == clause_part::kind_bool &&
+ clause_.front ().bool_part;
+ }
+
+ void
+ optimize ();
+
+ public:
+ template <typename T>
+ static val_bind<T>
+ _val (const T& x)
+ {
+ return val_bind<T> (x);
+ }
+
+ template <database_type_id ID, typename T>
+ static val_bind_typed<T, ID>
+ _val (const T& x)
+ {
+ return val_bind_typed<T, ID> (x);
+ }
+
+ template <typename T>
+ static ref_bind<T>
+ _ref (const T& x)
+ {
+ return ref_bind<T> (x);
+ }
+
+ template <database_type_id ID, typename T>
+ static ref_bind_typed<T, ID>
+ _ref (const T& x)
+ {
+ return ref_bind_typed<T, ID> (x);
+ }
+
+ // Some compilers (notably VC++), when deducing const T& from const
+ // array do not strip const from the array type. As a result, in the
+ // above signatures we get, for example, T = const char[4] instead
+ // of T = char[4], which is what we want. So to "fix" such compilers,
+ // we will have to provide the following specializations of the above
+ // functions.
+ //
+ template <typename T, std::size_t N>
+ static val_bind<T[N]>
+ _val (const T (&x) [N])
+ {
+ return val_bind<T[N]> (x);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static val_bind_typed<T[N], ID>
+ _val (const T (&x) [N])
+ {
+ return val_bind_typed<T[N], ID> (x);
+ }
+
+ template <typename T, std::size_t N>
+ static ref_bind<T[N]>
+ _ref (const T (&x) [N])
+ {
+ return ref_bind<T[N]> (x);
+ }
+
+ template <database_type_id ID, typename T, std::size_t N>
+ static ref_bind_typed<T[N], ID>
+ _ref (const T (&x) [N])
+ {
+ return ref_bind_typed<T[N], ID> (x);
+ }
+
+ public:
+ query_base&
+ operator+= (const query_base&);
+
+ query_base&
+ operator+= (const std::string& q)
+ {
+ append (q);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (val_bind<T> v)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ v, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (val_bind_typed<T, ID> v)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (v, 0);
+ return *this;
+ }
+
+ template <typename T>
+ query_base&
+ operator+= (ref_bind<T> r)
+ {
+ append<T, type_traits<T>::db_type_id> (
+ r, details::conversion<T>::to ());
+ return *this;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base&
+ operator+= (ref_bind_typed<T, ID> r)
+ {
+ // We are not using default type_traits so no default conversion
+ // either.
+ //
+ append<T, ID> (r, 0);
+ return *this;
+ }
+
+ // Implementation details.
+ //
+ public:
+ template <typename T, database_type_id ID>
+ void
+ append (val_bind<T>, const char* conv);
+
+ template <typename T, database_type_id ID>
+ void
+ append (ref_bind<T>, const char* conv);
+
+ void
+ append (details::shared_ptr<query_param>, const char* conv);
+
+ void
+ append (bool v)
+ {
+ clause_.push_back (clause_part (v));
+ }
+
+ void
+ append (const std::string& native);
+
+ void
+ append (const char* native) // Clashes with append(bool).
+ {
+ append (std::string (native));
+ }
+
+ void
+ append (const char* table, const char* column);
+
+ private:
+ typedef std::vector<clause_part> clause_type;
+ typedef std::vector<details::shared_ptr<query_param> > parameters_type;
+
+ clause_type clause_;
+ parameters_type parameters_;
+
+ mutable std::vector<bind> bind_;
+ mutable binding binding_;
+
+ std::vector<char*> values_;
+ std::vector<int> lengths_;
+ std::vector<int> formats_;
+ std::vector<unsigned int> types_;
+ mutable native_binding native_binding_;
+ };
+
+ inline query_base
+ operator+ (const query_base& x, const query_base& y)
+ {
+ query_base r (x);
+ r += y;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, val_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, val_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, ref_bind<T> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, ref_bind_typed<T, ID> b)
+ {
+ query_base r (q);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const query_base& q)
+ {
+ query_base r;
+ r += b;
+ r += q;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const query_base& q, const std::string& s)
+ {
+ query_base r (q);
+ r += s;
+ return r;
+ }
+
+ inline query_base
+ operator+ (const std::string& s, const query_base& q)
+ {
+ query_base r (s);
+ r += q;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, val_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (val_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, val_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (val_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, ref_bind<T> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (ref_bind<T> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, ref_bind_typed<T, ID> b)
+ {
+ query_base r (s);
+ r += b;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (ref_bind_typed<T, ID> b, const std::string& s)
+ {
+ query_base r;
+ r += b;
+ r += s;
+ return r;
+ }
+
+ LIBODB_PGSQL_EXPORT query_base
+ operator&& (const query_base&, const query_base&);
+
+ LIBODB_PGSQL_EXPORT query_base
+ operator|| (const query_base&, const query_base&);
+
+ LIBODB_PGSQL_EXPORT query_base
+ operator! (const query_base&);
+
+ // query_column
+ //
+ struct query_column_base
+ {
+ // Note that we keep shallow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column_base (const char* table,
+ const char* column,
+ const char* conv)
+ : table_ (table), column_ (column), conversion_ (conv)
+ {
+ }
+
+ const char*
+ table () const
+ {
+ return table_;
+ }
+
+ const char*
+ column () const
+ {
+ return column_;
+ }
+
+ // Can be NULL.
+ //
+ const char*
+ conversion () const
+ {
+ return conversion_;
+ }
+
+ protected:
+ const char* table_;
+ const char* column_;
+ const char* conversion_;
+ };
+
+ template <typename T, database_type_id ID>
+ struct query_column: query_column_base
+ {
+ typedef typename decay_traits<T>::type decayed_type;
+
+ // Note that we keep shallow copies of the table, column, and conversion
+ // expression. The latter can be NULL.
+ //
+ query_column (const char* table, const char* column, const char* conv)
+ : query_column_base (table, column, conv) {}
+
+ // Implementation is in query-dynamic.ixx.
+ //
+ query_column (odb::query_column<T>&,
+ const char* table, const char* column, const char* conv);
+
+ // is_null, is_not_null
+ //
+ public:
+ query_base
+ is_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NULL";
+ return q;
+ }
+
+ query_base
+ is_not_null () const
+ {
+ query_base q (table_, column_);
+ q += "IS NOT NULL";
+ return q;
+ }
+
+ // in
+ //
+ public:
+ query_base
+ in (decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type) const;
+
+ query_base
+ in (decayed_type, decayed_type, decayed_type, decayed_type,
+ decayed_type) const;
+
+ template <typename I>
+ query_base
+ in_range (I begin, I end) const;
+
+ // like
+ //
+ public:
+ query_base
+ like (decayed_type pattern) const
+ {
+ return like (val_bind<T> (pattern));
+ }
+
+ query_base
+ like (val_bind<T> pattern) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)));
+ }
+
+ query_base
+ like (ref_bind<T> pattern) const;
+
+ query_base
+ like (decayed_type pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (pattern), escape);
+ }
+
+ query_base
+ like (val_bind<T> pattern, decayed_type escape) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern, decayed_type escape) const
+ {
+ return like (val_bind<T> (decayed_type (pattern.val)), escape);
+ }
+
+ query_base
+ like (ref_bind<T> pattern, decayed_type escape) const;
+
+ // =
+ //
+ public:
+ query_base
+ equal (decayed_type v) const
+ {
+ return equal (val_bind<T> (v));
+ }
+
+ query_base
+ equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ equal (val_bind<T2> v) const
+ {
+ return equal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator== (const query_column& c, decayed_type v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (decayed_type v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, val_bind<T> v)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (val_bind<T> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (const query_column& c, val_bind<T2> v)
+ {
+ return c.equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (val_bind<T2> v, const query_column& c)
+ {
+ return c.equal (v);
+ }
+
+ friend query_base
+ operator== (const query_column& c, ref_bind<T> r)
+ {
+ return c.equal (r);
+ }
+
+ friend query_base
+ operator== (ref_bind<T> r, const query_column& c)
+ {
+ return c.equal (r);
+ }
+
+ // !=
+ //
+ public:
+ query_base
+ unequal (decayed_type v) const
+ {
+ return unequal (val_bind<T> (v));
+ }
+
+ query_base
+ unequal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ unequal (val_bind<T2> v) const
+ {
+ return unequal (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ unequal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "!=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator!= (const query_column& c, decayed_type v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (decayed_type v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, val_bind<T> v)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (val_bind<T> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (const query_column& c, val_bind<T2> v)
+ {
+ return c.unequal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (val_bind<T2> v, const query_column& c)
+ {
+ return c.unequal (v);
+ }
+
+ friend query_base
+ operator!= (const query_column& c, ref_bind<T> r)
+ {
+ return c.unequal (r);
+ }
+
+ friend query_base
+ operator!= (ref_bind<T> r, const query_column& c)
+ {
+ return c.unequal (r);
+ }
+
+ // <
+ //
+ public:
+ query_base
+ less (decayed_type v) const
+ {
+ return less (val_bind<T> (v));
+ }
+
+ query_base
+ less (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less (val_bind<T2> v) const
+ {
+ return less (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ less (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "<";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator< (const query_column& c, decayed_type v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (decayed_type v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, val_bind<T> v)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator< (val_bind<T> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (const query_column& c, val_bind<T2> v)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator< (const query_column& c, ref_bind<T> r)
+ {
+ return c.less (r);
+ }
+
+ friend query_base
+ operator< (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater (r);
+ }
+
+ // >
+ //
+ public:
+ query_base
+ greater (decayed_type v) const
+ {
+ return greater (val_bind<T> (v));
+ }
+
+ query_base
+ greater (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater (val_bind<T2> v) const
+ {
+ return greater (val_bind<T> (decayed_type (v.val)));
+ }
+
+ query_base
+ greater (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += ">";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator> (const query_column& c, decayed_type v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (decayed_type v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, val_bind<T> v)
+ {
+ return c.greater (v);
+ }
+
+ friend query_base
+ operator> (val_bind<T> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (val_bind<T2> v, const query_column& c)
+ {
+ return c.less (v);
+ }
+
+ friend query_base
+ operator> (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater (r);
+ }
+
+ friend query_base
+ operator> (ref_bind<T> r, const query_column& c)
+ {
+ return c.less (r);
+ }
+
+ // <=
+ //
+ public:
+ query_base
+ less_equal (decayed_type v) const
+ {
+ return less_equal (val_bind<T> (v));
+ }
+
+ query_base
+ less_equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ less_equal (val_bind<T2> v) const
+ {
+ return less_equal (val_bind<T> (decayed_type (v.val)));;
+ }
+
+ query_base
+ less_equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += "<=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator<= (const query_column& c, decayed_type v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (decayed_type v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, val_bind<T> v)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator<= (val_bind<T> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (const query_column& c, val_bind<T2> v)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator<= (const query_column& c, ref_bind<T> r)
+ {
+ return c.less_equal (r);
+ }
+
+ friend query_base
+ operator<= (ref_bind<T> r, const query_column& c)
+ {
+ return c.greater_equal (r);
+ }
+
+ // >=
+ //
+ public:
+ query_base
+ greater_equal (decayed_type v) const
+ {
+ return greater_equal (val_bind<T> (v));
+ }
+
+ query_base
+ greater_equal (val_bind<T> v) const
+ {
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (v, conversion_);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ greater_equal (val_bind<T2> v) const
+ {
+ return greater_equal (val_bind<T> (decayed_type (v.val)));;
+ }
+
+ query_base
+ greater_equal (ref_bind<T> r) const
+ {
+ query_base q (table_, column_);
+ q += ">=";
+ q.append<T, ID> (r, conversion_);
+ return q;
+ }
+
+ friend query_base
+ operator>= (const query_column& c, decayed_type v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (decayed_type v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, val_bind<T> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ friend query_base
+ operator>= (val_bind<T> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (const query_column& c, val_bind<T2> v)
+ {
+ return c.greater_equal (v);
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (val_bind<T2> v, const query_column& c)
+ {
+ return c.less_equal (v);
+ }
+
+ friend query_base
+ operator>= (const query_column& c, ref_bind<T> r)
+ {
+ return c.greater_equal (r);
+ }
+
+ friend query_base
+ operator>= (ref_bind<T> r, const query_column& c)
+ {
+ return c.less_equal (r);
+ }
+
+ // Column comparison.
+ //
+ public:
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator== (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () ==
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator!= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () !=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "!=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator< (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator> (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator<= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () <=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += "<=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T2, database_type_id ID2>
+ query_base
+ operator>= (const query_column<T2, ID2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (decay_traits<T>::instance () >=
+ decay_traits<T2>::instance ()));
+
+ query_base q (table_, column_);
+ q += ">=";
+ q.append (c.table (), c.column ());
+ return q;
+ }
+ };
+
+ // Provide operator+() for using columns to construct native
+ // query fragments (e.g., ORDER BY).
+ //
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const std::string& s)
+ {
+ query_base q (c.table (), c.column ());
+ q += s;
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const std::string& s, const query_column<T, ID>& c)
+ {
+ query_base q (s);
+ q.append (c.table (), c.column ());
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_column<T, ID>& c, const query_base& q)
+ {
+ query_base r (c.table (), c.column ());
+ r += q;
+ return r;
+ }
+
+ template <typename T, database_type_id ID>
+ inline query_base
+ operator+ (const query_base& q, const query_column<T, ID>& c)
+ {
+ query_base r (q);
+ r.append (c.table (), c.column ());
+ return r;
+ }
+
+ //
+ //
+ template <typename T, database_type_id>
+ struct query_param_impl;
+
+ // BOOLEAN
+ //
+ template <typename T>
+ struct query_param_impl<T, id_boolean>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::boolean_;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return bool_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_boolean>::set_image (image_, is_null, v);
+ }
+
+ private:
+ bool image_;
+ };
+
+ // SMALLINT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_smallint>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::smallint;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return int2_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_smallint>::set_image (image_, is_null, v);
+ }
+
+ private:
+ short image_;
+ };
+
+ // INTEGER
+ //
+ template <typename T>
+ struct query_param_impl<T, id_integer>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::integer;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return int4_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_integer>::set_image (image_, is_null, v);
+ }
+
+ private:
+ int image_;
+ };
+
+ // BIGINT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_bigint>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::bigint;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return int8_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_bigint>::set_image (image_, is_null, v);
+ }
+
+ private:
+ long long image_;
+ };
+
+ // REAL
+ //
+ template <typename T>
+ struct query_param_impl<T, id_real>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::real;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return float4_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_real>::set_image (image_, is_null, v);
+ }
+
+ private:
+ float image_;
+ };
+
+ // DOUBLE
+ //
+ template <typename T>
+ struct query_param_impl<T, id_double>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::double_;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return float8_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_double>::set_image (image_, is_null, v);
+ }
+
+ private:
+ double image_;
+ };
+
+ // NUMERIC
+ //
+ template <typename T>
+ struct query_param_impl<T, id_numeric>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::numeric;
+ b->buffer = buffer_.data_ptr ();
+ b->capacity = buffer_.capacity ();
+ b->size = &size_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return numeric_oid;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_numeric>::set_image (buffer_, size, is_null, v);
+ size_ = size;
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ std::size_t size_;
+ };
+
+ // DATE
+ //
+ template <typename T>
+ struct query_param_impl<T, id_date>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::date;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return date_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_date>::set_image (image_, is_null, v);
+ }
+
+ private:
+ int image_;
+ };
+
+ // TIME
+ //
+ template <typename T>
+ struct query_param_impl<T, id_time>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::time;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return time_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_time>::set_image (image_, is_null, v);
+ }
+
+ private:
+ long long image_;
+ };
+
+ // TIMESTAMP
+ //
+ template <typename T>
+ struct query_param_impl<T, id_timestamp>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::timestamp;
+ b->buffer = &image_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return timestamp_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_timestamp>::set_image (image_, is_null, v);
+ }
+
+ private:
+ long long image_;
+ };
+
+ // STRING
+ //
+ template <typename T>
+ struct query_param_impl<T, id_string>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::text;
+ b->buffer = buffer_.data_ptr ();
+ b->capacity = buffer_.capacity ();
+ b->size = &size_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return text_oid;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_string>::set_image (buffer_, size, is_null, v);
+ size_ = size;
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ std::size_t size_;
+ };
+
+ // BYTEA
+ //
+ template <typename T>
+ struct query_param_impl<T, id_bytea>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::bytea;
+ b->buffer = buffer_.data_ptr ();
+ b->capacity = buffer_.capacity ();
+ b->size = &size_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return bytea_oid;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_bytea>::set_image (buffer_, size, is_null, v);
+ size_ = size;
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::buffer buffer_;
+ std::size_t size_;
+ };
+
+ // BIT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_bit>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::bit;
+ b->buffer = buffer_.data_ptr ();
+ b->capacity = buffer_.capacity ();
+ b->size = &size_;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+
+ // NOTE: Using a fixed size bit type in queries requires
+ // alternative image buffer type support.
+ //
+ value_traits<T, id_bit>::set_image (buffer_, size, is_null, v);
+ size_ = size;
+
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::ubuffer buffer_;
+ std::size_t size_;
+ };
+
+ // VARBIT
+ //
+ template <typename T>
+ struct query_param_impl<T, id_varbit>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ return init (*static_cast<const T*> (value_));
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::varbit;
+ b->buffer = buffer_.data_ptr ();
+ b->capacity = buffer_.capacity ();
+ b->size = &size_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return varbit_oid;
+ }
+
+ private:
+ bool
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ std::size_t size (0), cap (buffer_.capacity ());
+ value_traits<T, id_varbit>::set_image (buffer_, size, is_null, v);
+ size_ = size;
+ return cap != buffer_.capacity ();
+ }
+
+ private:
+ details::ubuffer buffer_;
+ std::size_t size_;
+ };
+
+ // UUID
+ //
+ template <typename T>
+ struct query_param_impl<T, id_uuid>: query_param
+ {
+ query_param_impl (ref_bind<T> r) : query_param (r.ptr ()) {}
+ query_param_impl (val_bind<T> v) : query_param (0) {init (v.val);}
+
+ virtual bool
+ init ()
+ {
+ init (*static_cast<const T*> (value_));
+ return false;
+ }
+
+ virtual void
+ bind (bind_type* b)
+ {
+ b->type = bind::uuid;
+ b->buffer = buffer_;
+ }
+
+ virtual unsigned int
+ oid () const
+ {
+ return uuid_oid;
+ }
+
+ private:
+ void
+ init (typename decay_traits<T>::type v)
+ {
+ bool is_null (false); // Can't be NULL.
+ value_traits<T, id_uuid>::set_image (buffer_, is_null, v);
+ }
+
+ private:
+ unsigned char buffer_[16];
+ };
+ }
+}
+
+// odb::pgsql::query and odb::query specialization for PostgreSQL.
+//
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ class query: public query_base,
+ public query_selector<T, id_pgsql>::columns_type
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : query_base (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : query_base (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : query_base (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (val_bind<T2> v)
+ : query_base (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (ref_bind<T2> r)
+ : query_base (r)
+ {
+ }
+
+ query (const query_base& q)
+ : query_base (q)
+ {
+ }
+
+ template <database_type_id ID>
+ query (const query_column<bool, ID>& qc)
+ : query_base (qc)
+ {
+ }
+
+ query (const odb::query_base& q)
+ : query_base (q)
+ {
+ }
+ };
+
+ namespace core
+ {
+ using pgsql::query;
+ }
+ }
+
+ // Derive odb::query from odb::pgsql::query so that it can be
+ // implicitly converted in pgsql::database::query() calls.
+ //
+ template <typename T>
+ class query<T, pgsql::query_base>: public pgsql::query<T>
+ {
+ public:
+ // We don't define any typedefs here since they may clash with
+ // column names defined by our base type.
+ //
+
+ query ()
+ {
+ }
+
+ explicit
+ query (bool v)
+ : pgsql::query<T> (v)
+ {
+ }
+
+ explicit
+ query (const char* q)
+ : pgsql::query<T> (q)
+ {
+ }
+
+ explicit
+ query (const std::string& q)
+ : pgsql::query<T> (q)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (pgsql::val_bind<T2> v)
+ : pgsql::query<T> (v)
+ {
+ }
+
+ template <typename T2>
+ explicit
+ query (pgsql::ref_bind<T2> r)
+ : pgsql::query<T> (r)
+ {
+ }
+
+ query (const pgsql::query_base& q)
+ : pgsql::query<T> (q)
+ {
+ }
+
+ template <pgsql::database_type_id ID>
+ query (const pgsql::query_column<bool, ID>& qc)
+ : pgsql::query<T> (qc)
+ {
+ }
+ };
+}
+
+#include <odb/pgsql/query.ixx>
+#include <odb/pgsql/query.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_QUERY_HXX
diff --git a/libodb-pgsql/odb/pgsql/query.ixx b/libodb-pgsql/odb/pgsql/query.ixx
new file mode 100644
index 0000000..826b4ab
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query.ixx
@@ -0,0 +1,34 @@
+// file : odb/pgsql/query.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace pgsql
+ {
+ inline native_binding& query_base::
+ parameters_binding () const
+ {
+ return native_binding_;
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (val_bind<T> v, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (v)),
+ conv);
+ }
+
+ template <typename T, database_type_id ID>
+ inline void query_base::
+ append (ref_bind<T> r, const char* conv)
+ {
+ append (
+ details::shared_ptr<query_param> (
+ new (details::shared) query_param_impl<T, ID> (r)),
+ conv);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/query.txx b/libodb-pgsql/odb/pgsql/query.txx
new file mode 100644
index 0000000..44dae30
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/query.txx
@@ -0,0 +1,168 @@
+// file : odb/pgsql/query.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // query_base
+ //
+
+ template <database_type_id ID>
+ query_base::
+ query_base (const query_column<bool, ID>& c)
+ : binding_ (0, 0), native_binding_ (0, 0, 0, 0)
+ {
+ // Cannot use IS TRUE here since database type can be a non-
+ // integral type.
+ //
+ append (c.table (), c.column ());
+ append ("=");
+ append<bool, ID> (val_bind<bool> (true), c.conversion ());
+ }
+
+ //
+ // query_column
+ //
+
+ // in
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3,
+ decayed_type v4) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ in (decayed_type v1, decayed_type v2, decayed_type v3, decayed_type v4,
+ decayed_type v5) const
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+ q.append<T, ID> (val_bind<T> (v1), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v2), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v3), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v4), conversion_);
+ q += ",";
+ q.append<T, ID> (val_bind<T> (v5), conversion_);
+ q += ")";
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ template <typename I>
+ query_base query_column<T, ID>::
+ in_range (I begin, I end) const
+ {
+ if (begin != end)
+ {
+ query_base q (table_, column_);
+ q += "IN (";
+
+ for (I i (begin); i != end; ++i)
+ {
+ if (i != begin)
+ q += ",";
+
+ q.append<T, ID> (val_bind<T> (*i), conversion_);
+ }
+
+ q += ")";
+ return q;
+ }
+ else
+ return query_base (false);
+ }
+
+ // like
+ //
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (val_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+
+ template <typename T, database_type_id ID>
+ query_base query_column<T, ID>::
+ like (ref_bind<T> p, decayed_type e) const
+ {
+ query_base q (table_, column_);
+ q += "LIKE";
+ q.append<T, ID> (p, conversion_);
+ q += "ESCAPE";
+ q.append<T, ID> (val_bind<T> (e), conversion_);
+ return q;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/section-statements.hxx b/libodb-pgsql/odb/pgsql/section-statements.hxx
new file mode 100644
index 0000000..e40b282
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/section-statements.hxx
@@ -0,0 +1,220 @@
+// file : odb/pgsql/section-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_SECTION_STATEMENTS_HXX
+#define ODB_PGSQL_SECTION_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/pgsql-fwd.hxx> // Oid
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class connection;
+
+ // Template argument is the section traits type.
+ //
+ template <typename T, typename ST>
+ class section_statements
+ {
+ public:
+ typedef ST traits;
+
+ typedef typename traits::image_type image_type;
+ typedef typename traits::id_image_type id_image_type;
+
+ typedef pgsql::select_statement select_statement_type;
+ typedef pgsql::update_statement update_statement_type;
+
+ typedef pgsql::connection connection_type;
+
+ section_statements (connection_type&,
+ image_type&,
+ id_image_type&,
+ binding& id,
+ binding& idv,
+ native_binding& idn,
+ const Oid* idt);
+
+ connection_type&
+ connection () {return conn_;}
+
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ image_type&
+ image () {return image_;}
+
+ const binding&
+ id_binding () {return id_binding_;}
+
+ // Id and optimistic concurrency version (if any).
+ //
+ const binding&
+ idv_binding () {return idv_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_binding_version () const { return update_id_binding_version_;}
+
+ void
+ update_id_binding_version (std::size_t v) {update_id_binding_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ //
+ // Statements.
+ //
+
+ select_statement_type&
+ select_statement ()
+ {
+ if (select_ == 0)
+ select_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ traits::select_name,
+ traits::select_statement,
+ traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ id_types_,
+ id_column_count,
+ id_binding_,
+ id_native_binding_,
+ select_image_binding_,
+ false));
+
+ return *select_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ traits::update_name,
+ traits::update_statement,
+ traits::versioned, // Process if versioned.
+ traits::update_types,
+ update_column_count + id_column_count +
+ managed_optimistic_update_column_count,
+ update_image_binding_,
+ update_image_native_binding_,
+ false));
+
+ return *update_;
+ }
+
+ public:
+ static const std::size_t id_column_count = traits::id_column_count;
+ static const std::size_t managed_optimistic_load_column_count =
+ traits::managed_optimistic_load_column_count;
+ static const std::size_t managed_optimistic_update_column_count =
+ traits::managed_optimistic_update_column_count;
+ static const std::size_t select_column_count = traits::load_column_count;
+ static const std::size_t update_column_count =
+ traits::update_column_count;
+
+ private:
+ section_statements (const section_statements&);
+ section_statements& operator= (const section_statements&);
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+
+ // These come from object_statements.
+ //
+ image_type& image_;
+ binding& id_binding_;
+ binding& idv_binding_;
+ native_binding& id_native_binding_;
+ const Oid* id_types_;
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+
+ static const std::size_t select_bind_count =
+ select_column_count != 0 || managed_optimistic_load_column_count != 0
+ ? select_column_count + managed_optimistic_load_column_count
+ : 1;
+
+ binding select_image_binding_;
+ bind select_image_bind_[select_bind_count];
+ bool select_image_truncated_[select_bind_count];
+
+ // Update binding.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_binding_version_;
+
+ static const std::size_t update_bind_count =
+ update_column_count != 0 || managed_optimistic_update_column_count != 0
+ ? update_column_count + id_column_count +
+ managed_optimistic_update_column_count
+ : 1;
+
+ binding update_image_binding_;
+ bind update_image_bind_[update_bind_count];
+
+ native_binding update_image_native_binding_;
+ char* update_image_values_[update_bind_count];
+ int update_image_lengths_[update_bind_count];
+ int update_image_formats_[update_bind_count];
+
+ details::shared_ptr<select_statement_type> select_;
+ details::shared_ptr<update_statement_type> update_;
+ };
+ }
+}
+
+#include <odb/pgsql/section-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_SECTION_STATEMENTS_HXX
diff --git a/libodb-pgsql/odb/pgsql/section-statements.txx b/libodb-pgsql/odb/pgsql/section-statements.txx
new file mode 100644
index 0000000..55f4093
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/section-statements.txx
@@ -0,0 +1,51 @@
+// file : odb/pgsql/section-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T, typename ST>
+ section_statements<T, ST>::
+ section_statements (connection_type& conn,
+ image_type& im,
+ id_image_type&,
+ binding& id,
+ binding& idv,
+ native_binding& idn,
+ const Oid* idt)
+ : conn_ (conn),
+ svm_ (0),
+ image_ (im),
+ id_binding_ (id),
+ idv_binding_ (idv),
+ id_native_binding_ (idn),
+ id_types_ (idt),
+ select_image_binding_ (select_image_bind_,
+ select_column_count +
+ managed_optimistic_load_column_count),
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_update_column_count),
+ update_image_native_binding_ (update_image_values_,
+ update_image_lengths_,
+ update_image_formats_,
+ update_column_count + id_column_count +
+ managed_optimistic_update_column_count)
+ {
+ select_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_binding_version_ = 0;
+
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+
+ for (std::size_t i (0); i < select_bind_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/simple-object-result.hxx b/libodb-pgsql/odb/pgsql/simple-object-result.hxx
new file mode 100644
index 0000000..7472cbe
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/simple-object-result.hxx
@@ -0,0 +1,85 @@
+// file : odb/pgsql/simple-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_SIMPLE_OBJECT_RESULT_HXX
+#define ODB_PGSQL_SIMPLE_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/simple-object-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx> // query_base
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ class object_result_impl: public odb::object_result_impl<T>
+ {
+ public:
+ typedef odb::object_result_impl<T> base_type;
+
+ typedef typename base_type::id_type id_type;
+ typedef typename base_type::object_type object_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, id_pgsql> object_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef typename object_traits::statements_type statements_type;
+
+ virtual
+ ~object_result_impl ();
+
+ object_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (object_type&, bool fetch);
+
+ virtual id_type
+ load_id ();
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ void
+ load_image ();
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ object_traits_calls<object_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/pgsql/simple-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_SIMPLE_OBJECT_RESULT_HXX
diff --git a/libodb-pgsql/odb/pgsql/simple-object-result.txx b/libodb-pgsql/odb/pgsql/simple-object-result.txx
new file mode 100644
index 0000000..c14d64b
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/simple-object-result.txx
@@ -0,0 +1,158 @@
+// file : odb/pgsql/simple-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/callback.hxx>
+
+#include <odb/pgsql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ object_result_impl<T>::
+ ~object_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ object_result_impl<T>::
+ object_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ load (object_type& obj, bool fetch)
+ {
+ if (fetch)
+ load_image ();
+
+ // This is a top-level call so the statements cannot be locked.
+ //
+ assert (!statements_.locked ());
+ typename statements_type::auto_lock l (statements_);
+
+ object_traits::callback (this->db_, obj, callback_event::pre_load);
+
+ typename object_traits::image_type& i (statements_.image ());
+ tc_.init (obj, i, &this->db_);
+
+ // Initialize the id image and binding and load the rest of the object
+ // (containers, etc).
+ //
+ typename object_traits::id_image_type& idi (statements_.id_image ());
+ object_traits::init (idi, object_traits::id (i));
+
+ binding& idb (statements_.id_image_binding ());
+ if (idi.version != statements_.id_image_version () || idb.version == 0)
+ {
+ object_traits::bind (idb.bind, idi);
+ statements_.id_image_version (idi.version);
+ idb.version++;
+ }
+
+ tc_.load_ (statements_, obj, false);
+ statements_.load_delayed (tc_.version ());
+ l.unlock ();
+ object_traits::callback (this->db_, obj, callback_event::post_load);
+ }
+
+ template <typename T>
+ typename object_result_impl<T>::id_type
+ object_result_impl<T>::
+ load_id ()
+ {
+ load_image ();
+ return object_traits::id (statements_.image ());
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (statement_->next ())
+ count_++;
+ else
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ load_image ()
+ {
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ typename object_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ }
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ if (tc_.grow (im, statements_.select_image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.select_image_version ())
+ {
+ binding& b (statements_.select_image_binding ());
+ tc_.bind (b.bind, im, statement_select);
+ statements_.select_image_version (im.version);
+ b.version++;
+ statement_->reload ();
+ }
+ }
+ }
+
+ template <typename T>
+ void object_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t object_result_impl<T>::
+ size ()
+ {
+ return this->end_ ? count_ : statement_->result_size ();
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/simple-object-statements.cxx b/libodb-pgsql/odb/pgsql/simple-object-statements.cxx
new file mode 100644
index 0000000..432f990
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/simple-object-statements.cxx
@@ -0,0 +1,15 @@
+// file : odb/pgsql/simple-object-statements.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/simple-object-statements.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ object_statements_base::
+ ~object_statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/simple-object-statements.hxx b/libodb-pgsql/odb/pgsql/simple-object-statements.hxx
new file mode 100644
index 0000000..086ef5f
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/simple-object-statements.hxx
@@ -0,0 +1,657 @@
+// file : odb/pgsql/simple-object-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_SIMPLE_OBJECT_STATEMENTS_HXX
+#define ODB_PGSQL_SIMPLE_OBJECT_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cassert>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/pgsql-types.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/statements-base.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ // The extra_statement_cache class is only defined (and used) in
+ // the generated source file. However, object_statements may be
+ // referenced from another source file in the case of a polymorphic
+ // hierarchy (though in this case the extra statement cache is
+ // not used). As a result, we cannot have a by-value member and
+ // instead will store a pointer and lazily allocate the cache if
+ // and when needed. We will also need to store a pointer to the
+ // deleter function which will be initialized during allocation
+ // (at that point we know that the cache class is defined).
+ //
+ template <typename T, typename I, typename ID>
+ struct extra_statement_cache_ptr
+ {
+ typedef I image_type;
+ typedef ID id_image_type;
+ typedef pgsql::connection connection_type;
+
+ extra_statement_cache_ptr (): p_ (0) {}
+ ~extra_statement_cache_ptr ()
+ {
+ if (p_ != 0)
+ (this->*deleter_) (0, 0, 0, 0, 0, 0, 0);
+ }
+
+ T&
+ get (connection_type& c,
+ image_type& im,
+ id_image_type& idim,
+ binding& id,
+ binding* idv,
+ native_binding& idn,
+ const Oid* idt)
+ {
+ if (p_ == 0)
+ allocate (&c, &im, &idim, &id, (idv != 0 ? idv : &id), &idn, idt);
+
+ return *p_;
+ }
+
+ private:
+ void
+ allocate (connection_type*,
+ image_type*, id_image_type*,
+ binding*, binding*, native_binding*, const Oid*);
+
+ private:
+ T* p_;
+ void (extra_statement_cache_ptr::*deleter_) (
+ connection_type*,
+ image_type*, id_image_type*,
+ binding*, binding*, native_binding*, const Oid*);
+ };
+
+ template <typename T, typename I, typename ID>
+ void extra_statement_cache_ptr<T, I, ID>::
+ allocate (connection_type* c,
+ image_type* im, id_image_type* idim,
+ binding* id, binding* idv, native_binding* idn, const Oid* idt)
+ {
+ // To reduce object code size, this function acts as both allocator
+ // and deleter.
+ //
+ if (p_ == 0)
+ {
+ p_ = new T (*c, *im, *idim, *id, *idv, *idn, idt);
+ deleter_ = &extra_statement_cache_ptr<T, I, ID>::allocate;
+ }
+ else
+ delete p_;
+ }
+
+ //
+ // Implementation for objects with object id.
+ //
+
+ class LIBODB_PGSQL_EXPORT object_statements_base: public statements_base
+ {
+ public:
+ // Locking.
+ //
+ void
+ lock ()
+ {
+ assert (!locked_);
+ locked_ = true;
+ }
+
+ void
+ unlock ()
+ {
+ assert (locked_);
+ locked_ = false;
+ }
+
+ bool
+ locked () const
+ {
+ return locked_;
+ }
+
+ struct auto_unlock
+ {
+ // Unlocks the statement on construction and re-locks it on
+ // destruction.
+ //
+ auto_unlock (object_statements_base&);
+ ~auto_unlock ();
+
+ private:
+ auto_unlock (const auto_unlock&);
+ auto_unlock& operator= (const auto_unlock&);
+
+ private:
+ object_statements_base& s_;
+ };
+
+ public:
+ virtual
+ ~object_statements_base ();
+
+ protected:
+ object_statements_base (connection_type& conn)
+ : statements_base (conn), locked_ (false)
+ {
+ }
+
+ protected:
+ bool locked_;
+ };
+
+ template <typename T, bool optimistic>
+ struct optimistic_data;
+
+ template <typename T>
+ struct optimistic_data<T, true>
+ {
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_pgsql> object_traits;
+
+ optimistic_data (bind*, char** nv, int* nl, int* nf,
+ std::size_t skip, unsigned long long* status);
+
+ binding*
+ id_image_binding () {return &id_image_binding_;}
+
+ native_binding*
+ id_image_native_binding () {return &id_image_native_binding_;}
+
+ const Oid*
+ id_image_types ()
+ {return object_traits::optimistic_erase_statement_types;}
+
+ // The id + optimistic column binding.
+ //
+ binding id_image_binding_;
+ native_binding id_image_native_binding_;
+
+ details::shared_ptr<delete_statement> erase_;
+ };
+
+ template <typename T>
+ struct optimistic_data<T, false>
+ {
+ optimistic_data (bind*, char**, int*, int*,
+ std::size_t, unsigned long long*) {}
+
+ binding*
+ id_image_binding () {return 0;}
+
+ native_binding*
+ id_image_native_binding () {return 0;}
+
+ const Oid*
+ id_image_types () {return 0;}
+ };
+
+ template <typename T>
+ class object_statements: public object_statements_base
+ {
+ public:
+ typedef T object_type;
+ typedef object_traits_impl<object_type, id_pgsql> object_traits;
+ typedef typename object_traits::id_type id_type;
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef typename object_traits::image_type image_type;
+ typedef typename object_traits::id_image_type id_image_type;
+
+ typedef
+ typename object_traits::pointer_cache_traits
+ pointer_cache_traits;
+
+ typedef
+ typename object_traits::extra_statement_cache_type
+ extra_statement_cache_type;
+
+ typedef pgsql::insert_statement insert_statement_type;
+ typedef pgsql::select_statement select_statement_type;
+ typedef pgsql::update_statement update_statement_type;
+ typedef pgsql::delete_statement delete_statement_type;
+
+ // Automatic lock.
+ //
+ struct auto_lock
+ {
+ // Lock the statements unless they are already locked in which
+ // case subsequent calls to locked() will return false.
+ //
+ auto_lock (object_statements&);
+
+ // Unlock the statemens if we are holding the lock and clear
+ // the delayed loads. This should only happen in case an
+ // exception is thrown. In normal circumstances, the user
+ // should call unlock() explicitly.
+ //
+ ~auto_lock ();
+
+ // Return true if this auto_lock instance holds the lock.
+ //
+ bool
+ locked () const;
+
+ // Unlock the statemens.
+ //
+ void
+ unlock ();
+
+ private:
+ auto_lock (const auto_lock&);
+ auto_lock& operator= (const auto_lock&);
+
+ private:
+ object_statements& s_;
+ bool locked_;
+ };
+
+ public:
+ object_statements (connection_type&);
+
+ virtual
+ ~object_statements ();
+
+ // Delayed loading.
+ //
+ typedef void (*loader_function) (odb::database&,
+ const id_type&,
+ object_type&,
+ const schema_version_migration*);
+ void
+ delay_load (const id_type& id,
+ object_type& obj,
+ const typename pointer_cache_traits::position_type& p,
+ loader_function l = 0)
+ {
+ delayed_.push_back (delayed_load (id, obj, p, l));
+ }
+
+ void
+ load_delayed (const schema_version_migration* svm)
+ {
+ assert (locked ());
+
+ if (!delayed_.empty ())
+ load_delayed_<object_statements> (svm);
+ }
+
+ void
+ clear_delayed ()
+ {
+ if (!delayed_.empty ())
+ clear_delayed_ ();
+ }
+
+ // Object image.
+ //
+ image_type&
+ image (std::size_t i = 0) {return images_[i].obj;}
+
+ // Insert binding.
+ //
+ std::size_t
+ insert_image_version () const { return insert_image_version_;}
+
+ void
+ insert_image_version (std::size_t v) {insert_image_version_ = v;}
+
+ binding&
+ insert_image_binding () {return insert_image_binding_;}
+
+ // Update binding.
+ //
+ std::size_t
+ update_image_version () const { return update_image_version_;}
+
+ void
+ update_image_version (std::size_t v) {update_image_version_ = v;}
+
+ std::size_t
+ update_id_image_version () const { return update_id_image_version_;}
+
+ void
+ update_id_image_version (std::size_t v) {update_id_image_version_ = v;}
+
+ binding&
+ update_image_binding () {return update_image_binding_;}
+
+ // Select binding.
+ //
+ std::size_t
+ select_image_version () const { return select_image_version_;}
+
+ void
+ select_image_version (std::size_t v) {select_image_version_ = v;}
+
+ binding&
+ select_image_binding () {return select_image_binding_;}
+
+ bool*
+ select_image_truncated () {return select_image_truncated_;}
+
+ // Object id image and binding.
+ //
+ id_image_type&
+ id_image (std::size_t i = 0) {return images_[i].id;}
+
+ std::size_t
+ id_image_version () const {return id_image_version_;}
+
+ void
+ id_image_version (std::size_t v) {id_image_version_ = v;}
+
+ binding&
+ id_image_binding () {return id_image_binding_;}
+
+ // Optimistic id + managed column image binding. It points to
+ // the same suffix as id binding and they are always updated
+ // at the same time.
+ //
+ binding&
+ optimistic_id_image_binding () {return od_.id_image_binding_;}
+
+ // Statements.
+ //
+ insert_statement_type&
+ persist_statement ()
+ {
+ if (persist_ == 0)
+ persist_.reset (
+ new (details::shared) insert_statement_type (
+ conn_,
+ object_traits::persist_statement_name,
+ object_traits::persist_statement,
+ object_traits::versioned, // Process if versioned.
+ object_traits::persist_statement_types,
+ insert_column_count,
+ insert_image_binding_,
+ insert_image_native_binding_,
+ (object_traits::auto_id ? &id_image_binding_ : 0),
+ false));
+
+ return *persist_;
+ }
+
+ select_statement_type&
+ find_statement ()
+ {
+ if (find_ == 0)
+ find_.reset (
+ new (details::shared) select_statement_type (
+ conn_,
+ object_traits::find_statement_name,
+ object_traits::find_statement,
+ object_traits::versioned, // Process if versioned.
+ false, // Don't optimize.
+ object_traits::find_statement_types,
+ id_column_count,
+ id_image_binding_,
+ id_image_native_binding_,
+ select_image_binding_,
+ false));
+
+ return *find_;
+ }
+
+ update_statement_type&
+ update_statement ()
+ {
+ if (update_ == 0)
+ update_.reset (
+ new (details::shared) update_statement_type (
+ conn_,
+ object_traits::update_statement_name,
+ object_traits::update_statement,
+ object_traits::versioned, // Process if versioned.
+ object_traits::update_statement_types,
+ update_column_count + id_column_count,
+ update_image_binding_,
+ update_image_native_binding_,
+ false));
+
+ return *update_;
+ }
+
+ delete_statement_type&
+ erase_statement ()
+ {
+ if (erase_ == 0)
+ erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::erase_statement_name,
+ object_traits::erase_statement,
+ object_traits::find_statement_types, // The same as find (id).
+ id_column_count,
+ id_image_binding_,
+ id_image_native_binding_,
+ false));
+
+ return *erase_;
+ }
+
+ delete_statement_type&
+ optimistic_erase_statement ()
+ {
+ if (od_.erase_ == 0)
+ od_.erase_.reset (
+ new (details::shared) delete_statement_type (
+ conn_,
+ object_traits::optimistic_erase_statement_name,
+ object_traits::optimistic_erase_statement,
+ object_traits::optimistic_erase_statement_types,
+ id_column_count + managed_optimistic_column_count,
+ od_.id_image_binding_,
+ od_.id_image_native_binding_,
+ false));
+
+ return *od_.erase_;
+ }
+
+ // Extra (container, section) statement cache.
+ //
+ extra_statement_cache_type&
+ extra_statement_cache ()
+ {
+ return extra_statement_cache_.get (
+ conn_,
+ images_[0].obj,
+ images_[0].id,
+ id_image_binding_,
+ od_.id_image_binding (),
+ id_image_native_binding_,
+ object_traits::find_statement_types);
+ }
+
+ public:
+ // select = total - separate_load
+ // insert = total - inverse - managed_optimistic - auto_id
+ // update = total - inverse - managed_optimistic - id - readonly -
+ // separate_update
+ //
+ static const std::size_t select_column_count =
+ object_traits::column_count -
+ object_traits::separate_load_column_count;
+
+ static const std::size_t id_column_count =
+ object_traits::id_column_count;
+
+ static const std::size_t insert_column_count =
+ object_traits::column_count -
+ object_traits::inverse_column_count -
+ object_traits::managed_optimistic_column_count -
+ (object_traits::auto_id ? id_column_count : 0);
+
+ static const std::size_t update_column_count =
+ insert_column_count -
+ (object_traits::auto_id ? 0 : id_column_count) -
+ object_traits::readonly_column_count -
+ object_traits::separate_update_column_count;
+
+ static const std::size_t managed_optimistic_column_count =
+ object_traits::managed_optimistic_column_count;
+
+ private:
+ object_statements (const object_statements&);
+ object_statements& operator= (const object_statements&);
+
+ protected:
+ template <typename STS>
+ void
+ load_delayed_ (const schema_version_migration*);
+
+ void
+ clear_delayed_ ();
+
+ protected:
+ template <typename T1>
+ friend class polymorphic_derived_object_statements;
+
+ extra_statement_cache_ptr<extra_statement_cache_type,
+ image_type,
+ id_image_type> extra_statement_cache_;
+
+ // The UPDATE statement uses both the object and id image. Keep them
+ // next to each other so that the same skip distance can be used in
+ // batch binding.
+ //
+ struct images
+ {
+ image_type obj;
+ id_image_type id;
+ };
+
+ images images_[object_traits::batch];
+ unsigned long long status_[object_traits::batch];
+
+ // Select binding.
+ //
+ std::size_t select_image_version_;
+ binding select_image_binding_;
+ bind select_image_bind_[select_column_count];
+ bool select_image_truncated_[select_column_count];
+
+ // Insert binding.
+ //
+ std::size_t insert_image_version_;
+ binding insert_image_binding_;
+ bind insert_image_bind_[
+ insert_column_count != 0 ? insert_column_count : 1];
+ native_binding insert_image_native_binding_;
+ char* insert_image_values_[
+ insert_column_count != 0 ? insert_column_count : 1];
+ int insert_image_lengths_[
+ insert_column_count != 0 ? insert_column_count : 1];
+ int insert_image_formats_[
+ insert_column_count != 0 ? insert_column_count : 1];
+
+ // Update binding. Note that the id suffix is bound to id_image_
+ // below instead of image_ which makes this binding effectively
+ // bound to two images. As a result, we have to track versions
+ // for both of them. If this object uses optimistic concurrency,
+ // then the binding for the managed column (version, timestamp,
+ // etc) comes after the id and the image for such a column is
+ // stored as part of the id image.
+ //
+ std::size_t update_image_version_;
+ std::size_t update_id_image_version_;
+ binding update_image_binding_;
+ bind update_image_bind_[update_column_count + id_column_count +
+ managed_optimistic_column_count];
+ native_binding update_image_native_binding_;
+ char* update_image_values_[update_column_count + id_column_count +
+ managed_optimistic_column_count];
+ int update_image_lengths_[update_column_count + id_column_count +
+ managed_optimistic_column_count];
+ int update_image_formats_[update_column_count + id_column_count +
+ managed_optimistic_column_count];
+
+ // Id image binding (only used as a parameter). Uses the suffix in
+ // the update bind.
+ //
+ std::size_t id_image_version_;
+ binding id_image_binding_;
+ native_binding id_image_native_binding_;
+
+ // Extra data for objects with optimistic concurrency support.
+ //
+ optimistic_data<T, managed_optimistic_column_count != 0> od_;
+
+ details::shared_ptr<insert_statement_type> persist_;
+ details::shared_ptr<select_statement_type> find_;
+ details::shared_ptr<update_statement_type> update_;
+ details::shared_ptr<delete_statement_type> erase_;
+
+ // Delayed loading.
+ //
+ struct delayed_load
+ {
+ typedef typename pointer_cache_traits::position_type position_type;
+
+ delayed_load () {}
+ delayed_load (const id_type& i,
+ object_type& o,
+ const position_type& p,
+ loader_function l)
+ : id (i), obj (&o), pos (p), loader (l)
+ {
+ }
+
+ id_type id;
+ object_type* obj;
+ position_type pos;
+ loader_function loader;
+ };
+
+ typedef std::vector<delayed_load> delayed_loads;
+ delayed_loads delayed_;
+
+ // Delayed vectors swap guard. See the load_delayed_() function for
+ // details.
+ //
+ struct swap_guard
+ {
+ swap_guard (object_statements& os, delayed_loads& dl)
+ : os_ (os), dl_ (dl)
+ {
+ dl_.swap (os_.delayed_);
+ }
+
+ ~swap_guard ()
+ {
+ os_.clear_delayed ();
+ dl_.swap (os_.delayed_);
+ }
+
+ private:
+ object_statements& os_;
+ delayed_loads& dl_;
+ };
+ };
+ }
+}
+
+#include <odb/pgsql/simple-object-statements.ixx>
+#include <odb/pgsql/simple-object-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_SIMPLE_OBJECT_STATEMENTS_HXX
diff --git a/libodb-pgsql/odb/pgsql/simple-object-statements.ixx b/libodb-pgsql/odb/pgsql/simple-object-statements.ixx
new file mode 100644
index 0000000..fbb2775
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/simple-object-statements.ixx
@@ -0,0 +1,68 @@
+// file : odb/pgsql/simple-object-statements.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // auto_unlock
+ //
+ inline object_statements_base::auto_unlock::
+ auto_unlock (object_statements_base& s)
+ : s_ (s)
+ {
+ s_.unlock ();
+ }
+
+ inline object_statements_base::auto_unlock::
+ ~auto_unlock ()
+ {
+ s_.lock ();
+ }
+
+ //
+ // auto_lock
+ //
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ auto_lock (object_statements& s)
+ : s_ (s)
+ {
+ if (!s_.locked ())
+ {
+ s_.lock ();
+ locked_ = true;
+ }
+ else
+ locked_ = false;
+ }
+
+ template <typename T>
+ inline object_statements<T>::auto_lock::
+ ~auto_lock ()
+ {
+ if (locked_)
+ {
+ s_.unlock ();
+ s_.clear_delayed ();
+ }
+ }
+
+ template <typename T>
+ inline bool object_statements<T>::auto_lock::
+ locked () const
+ {
+ return locked_;
+ }
+
+ template <typename T>
+ inline void object_statements<T>::auto_lock::
+ unlock ()
+ {
+ assert (locked_);
+ s_.unlock ();
+ locked_ = false;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/simple-object-statements.txx b/libodb-pgsql/odb/pgsql/simple-object-statements.txx
new file mode 100644
index 0000000..bb47b43
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/simple-object-statements.txx
@@ -0,0 +1,190 @@
+// file : odb/pgsql/simple-object-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memset
+
+#include <odb/callback.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // optimistic_data
+ //
+
+ template <typename T>
+ optimistic_data<T, true>::
+ optimistic_data (bind* b, char** nv, int* nl, int* nf,
+ std::size_t skip, unsigned long long* status)
+ : id_image_binding_ (
+ b,
+ object_traits::id_column_count +
+ object_traits::managed_optimistic_column_count,
+ object_traits::batch,
+ skip,
+ status),
+ id_image_native_binding_ (
+ nv, nl, nf,
+ object_traits::id_column_count +
+ object_traits::managed_optimistic_column_count)
+ {
+ }
+
+ //
+ // object_statements
+ //
+
+ template <typename T>
+ object_statements<T>::
+ ~object_statements ()
+ {
+ }
+
+ template <typename T>
+ object_statements<T>::
+ object_statements (connection_type& conn)
+ : object_statements_base (conn),
+ // select
+ select_image_binding_ (select_image_bind_, select_column_count),
+ // insert
+ insert_image_binding_ (insert_image_bind_,
+ insert_column_count,
+ object_traits::batch,
+ sizeof (images),
+ status_),
+ insert_image_native_binding_ (insert_image_values_,
+ insert_image_lengths_,
+ insert_image_formats_,
+ insert_column_count),
+ // update
+ update_image_binding_ (update_image_bind_,
+ update_column_count + id_column_count +
+ managed_optimistic_column_count,
+ object_traits::batch,
+ sizeof (images),
+ status_),
+ update_image_native_binding_ (update_image_values_,
+ update_image_lengths_,
+ update_image_formats_,
+ update_column_count + id_column_count +
+ managed_optimistic_column_count),
+ // id
+ id_image_binding_ (update_image_bind_ + update_column_count,
+ id_column_count,
+ object_traits::batch,
+ sizeof (images),
+ status_),
+ id_image_native_binding_ (
+ update_image_values_ + update_column_count,
+ update_image_lengths_ + update_column_count,
+ update_image_formats_ + update_column_count,
+ id_column_count),
+ // optimistic data
+ od_ (update_image_bind_ + update_column_count,
+ update_image_values_ + update_column_count,
+ update_image_lengths_ + update_column_count,
+ update_image_formats_ + update_column_count,
+ sizeof (images),
+ status_)
+ {
+ // Only versions in the first element used.
+ //
+ images_[0].obj.version = 0;
+ images_[0].id.version = 0;
+
+ select_image_version_ = 0;
+ insert_image_version_ = 0;
+ update_image_version_ = 0;
+ update_id_image_version_ = 0;
+ id_image_version_ = 0;
+
+ std::memset (insert_image_bind_, 0, sizeof (insert_image_bind_));
+ std::memset (update_image_bind_, 0, sizeof (update_image_bind_));
+ std::memset (select_image_bind_, 0, sizeof (select_image_bind_));
+ std::memset (
+ select_image_truncated_, 0, sizeof (select_image_truncated_));
+
+ for (std::size_t i (0); i < select_column_count; ++i)
+ select_image_bind_[i].truncated = select_image_truncated_ + i;
+ }
+
+ template <typename T>
+ template <typename STS>
+ void object_statements<T>::
+ load_delayed_ (const schema_version_migration* svm)
+ {
+ database& db (connection ().database ());
+
+ delayed_loads dls;
+ swap_guard sg (*this, dls);
+
+ while (!dls.empty ())
+ {
+ delayed_load l (dls.back ());
+ typename pointer_cache_traits::insert_guard ig (l.pos);
+ dls.pop_back ();
+
+ if (l.loader == 0)
+ {
+ object_traits_calls<T> tc (svm);
+
+ if (!tc.find_ (static_cast<STS&> (*this), &l.id))
+ throw object_not_persistent ();
+
+ object_traits::callback (db, *l.obj, callback_event::pre_load);
+
+ // Our calls to init/load below can result in additional delayed
+ // loads being added to the delayed_ vector. We need to process
+ // those before we call the post callback.
+ //
+ tc.init (*l.obj, image (), &db);
+
+ // Load containers, etc.
+ //
+ tc.load_ (static_cast<STS&> (*this), *l.obj, false);
+
+ if (!delayed_.empty ())
+ load_delayed_<STS> (svm);
+
+ // Temporarily unlock the statement for the post_load call so that
+ // it can load objects of this type recursively. This is safe to do
+ // because we have completely loaded the current object. Also the
+ // delayed_ list is clear before the unlock and should be clear on
+ // re-lock (since a callback can only call public API functions
+ // which will make sure all the delayed loads are processed before
+ // returning).
+ //
+ {
+ auto_unlock u (*this);
+ object_traits::callback (db, *l.obj, callback_event::post_load);
+ }
+ }
+ else
+ l.loader (db, l.id, *l.obj, svm);
+
+ pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+ }
+
+ template <typename T>
+ void object_statements<T>::
+ clear_delayed_ ()
+ {
+ // Remove the objects from the session cache.
+ //
+ for (typename delayed_loads::iterator i (delayed_.begin ()),
+ e (delayed_.end ()); i != e; ++i)
+ {
+ pointer_cache_traits::erase (i->pos);
+ }
+
+ delayed_.clear ();
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/statement-cache.hxx b/libodb-pgsql/odb/pgsql/statement-cache.hxx
new file mode 100644
index 0000000..9417949
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/statement-cache.hxx
@@ -0,0 +1,57 @@
+// file : odb/pgsql/statement-cache.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_STATEMENT_CACHE_HXX
+#define ODB_PGSQL_STATEMENT_CACHE_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <typeinfo>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/type-info.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/statements-base.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class statement_cache
+ {
+ public:
+ statement_cache (connection& conn)
+ : conn_ (conn),
+ version_seq_ (conn_.database ().schema_version_sequence ()) {}
+
+ template <typename T>
+ typename object_traits_impl<T, id_pgsql>::statements_type&
+ find_object ();
+
+ template <typename T>
+ view_statements<T>&
+ find_view ();
+
+ private:
+ typedef std::map<const std::type_info*,
+ details::shared_ptr<statements_base>,
+ details::type_info_comparator> map;
+
+ connection& conn_;
+ unsigned int version_seq_;
+ map map_;
+ };
+ }
+}
+
+#include <odb/pgsql/statement-cache.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_STATEMENT_CACHE_HXX
diff --git a/libodb-pgsql/odb/pgsql/statement-cache.txx b/libodb-pgsql/odb/pgsql/statement-cache.txx
new file mode 100644
index 0000000..488ba2c
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/statement-cache.txx
@@ -0,0 +1,60 @@
+// file : odb/pgsql/statement-cache.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/database.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ typename object_traits_impl<T, id_pgsql>::statements_type&
+ statement_cache::
+ find_object ()
+ {
+ typedef
+ typename object_traits_impl<T, id_pgsql>::statements_type
+ statements_type;
+
+ // Clear the cache if the database version has changed. This
+ // makes sure we don't re-use statements that correspond to
+ // the old schema.
+ //
+ if (version_seq_ != conn_.database ().schema_version_sequence ())
+ {
+ map_.clear ();
+ version_seq_ = conn_.database ().schema_version_sequence ();
+ }
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<statements_type&> (*i->second);
+
+ details::shared_ptr<statements_type> p (
+ new (details::shared) statements_type (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+
+ template <typename T>
+ view_statements<T>& statement_cache::
+ find_view ()
+ {
+ // We don't cache any statements for views so no need to clear
+ // the cache.
+
+ map::iterator i (map_.find (&typeid (T)));
+
+ if (i != map_.end ())
+ return static_cast<view_statements<T>&> (*i->second);
+
+ details::shared_ptr<view_statements<T> > p (
+ new (details::shared) view_statements<T> (conn_));
+
+ map_.insert (map::value_type (&typeid (T), p));
+ return *p;
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/statement.cxx b/libodb-pgsql/odb/pgsql/statement.cxx
new file mode 100644
index 0000000..8574c37
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/statement.cxx
@@ -0,0 +1,1545 @@
+// file : odb/pgsql/statement.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <libpq-fe.h>
+
+#ifdef LIBPQ_HAS_PIPELINING
+# ifndef _WIN32
+# include <errno.h>
+# include <sys/select.h>
+# endif
+#endif
+
+#include <cstring> // strcmp
+#include <utility> // pair
+#include <cassert>
+
+#ifdef ODB_CXX11
+# include <cstdlib> // strtoull
+#else
+# include <sstream> // istringstream
+#endif
+
+#include <odb/tracer.hxx>
+
+#include <odb/pgsql/pgsql-oid.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/exceptions.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/transaction.hxx>
+#include <odb/pgsql/auto-handle.hxx>
+#include <odb/pgsql/error.hxx>
+
+#include <odb/pgsql/details/endian-traits.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ using namespace details;
+
+ static unsigned long long
+ affected_row_count (PGresult* h)
+ {
+ const char* s (PQcmdTuples (h));
+ unsigned long long count;
+
+ if (s[0] != '\0' && s[1] == '\0')
+ count = static_cast<unsigned long long> (s[0] - '0');
+ else
+ {
+#ifdef ODB_CXX11
+ count = strtoull (s, 0, 10);
+#else
+ istringstream ss (s);
+ ss >> count;
+#endif
+ }
+
+ return count;
+ }
+
+ //
+ // statement
+ //
+
+ statement::
+ ~statement ()
+ {
+ try
+ {
+ deallocate ();
+ }
+ catch (...)
+ {
+ }
+ }
+
+ void statement::
+ deallocate ()
+ {
+ if (!deallocated_)
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->deallocate (conn_, *this);
+ }
+
+ string s ("deallocate \"");
+ s += name_;
+ s += "\"";
+
+ deallocated_ = true;
+ auto_handle<PGresult> h (PQexec (conn_.handle (), s.c_str ()));
+
+ if (!is_good_result (h))
+ {
+ // When we try to execute an invalid statement, PG "poisons" the
+ // transaction (those "current transaction is aborted, commands
+ // ignored until end of transaction block" messages in the log).
+ // This includes prepared statement deallocations (quite a stupid
+ // decision, if you ask me).
+ //
+ // So what can happen in this situation is the deallocation fails
+ // but we ignore it because we are already unwinding the stack
+ // (i.e., the prepared statement execution has failed). Next the
+ // user fixes things (e.g., passes valid parameters) and tries to
+ // re-execute the same query. But since we have failed to deallocate
+ // the statement, we now cannot re-prepare it; the name is already
+ // in use.
+ //
+ // What can we do to fix this? One way would be to postpone the
+ // deallocation until after the transaction is rolled back. This,
+ // however, would require quite an elaborate machinery: connection
+ // will have to store a list of such statements, etc. A much simpler
+ // solution is to mark the connection as failed. While it maybe a
+ // bit less efficient, we assume this is an "exceptional" situation
+ // that doesn't occur often. The only potentially problematic case
+ // is if the user holds the pointer to the connection and runs
+ // multiple transactions on it. But in this case the user should
+ // check if the connection is still good after each failure anyway.
+ //
+ conn_.mark_failed ();
+
+ translate_error (conn_, h);
+ }
+ }
+ }
+
+ statement::
+ statement (connection_type& conn,
+ const string& name,
+ const string& text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize,
+ const Oid* types,
+ size_t types_count)
+ : conn_ (conn),
+ name_copy_ (name), name_ (name_copy_.c_str ()),
+ deallocated_ (false)
+ {
+ if (process == 0)
+ {
+ text_copy_ = text;
+ text_ = text_copy_.c_str ();
+ }
+ else
+ text_ = text.c_str (); // Temporary, see init().
+
+ init (sk, process, optimize, types, types_count);
+
+ //
+ // If any code after this line throws, the statement will be leaked
+ // (on the server) since deallocate() won't be called for it.
+ //
+ }
+
+ statement::
+ statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ statement_kind sk,
+ const binding* process,
+ bool optimize,
+ bool copy,
+ const Oid* types,
+ size_t types_count)
+ : conn_ (conn), deallocated_ (false)
+ {
+ if (copy)
+ {
+ name_copy_ = name;
+ name_ = name_copy_.c_str ();
+ }
+ else
+ name_ = name;
+
+ if (process == 0 && copy)
+ {
+ text_copy_ = text;
+ text_ = text_copy_.c_str ();
+ }
+ else
+ text_ = text; // Potentially temporary, see init().
+
+ init (sk, process, optimize, types, types_count);
+
+ //
+ // If any code after this line throws, the statement will be leaked
+ // (on the server) since deallocate() won't be called for it.
+ //
+ }
+
+ void statement::
+ init (statement_kind sk,
+ const binding* proc,
+ bool optimize,
+ const Oid* types,
+ size_t types_count)
+ {
+ if (proc != 0)
+ {
+ switch (sk)
+ {
+ case statement_select:
+ process_select (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '"', '"',
+ optimize);
+ break;
+ case statement_insert:
+ process_insert (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '$');
+ break;
+ case statement_update:
+ process_update (text_copy_,
+ text_,
+ &proc->bind->buffer, proc->count, sizeof (bind),
+ '$');
+ break;
+ case statement_delete:
+ assert (false);
+ }
+
+ text_ = text_copy_.c_str ();
+ }
+
+ // Empty statement.
+ //
+ if (*text_ == '\0')
+ {
+ deallocated_ = true;
+ return;
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->prepare (conn_, *this);
+ }
+
+ auto_handle<PGresult> h (
+ PQprepare (conn_.handle (),
+ name_,
+ text_,
+ static_cast<int> (types_count),
+ types));
+
+ if (!is_good_result (h))
+ translate_error (conn_, h);
+
+ //
+ // If any code after this line throws, the statement will be leaked
+ // (on the server) since deallocate() won't be called for it.
+ //
+ }
+
+ const char* statement::
+ text () const
+ {
+ return text_;
+ }
+
+ template <typename T>
+ static inline T*
+ offset (T* base, size_t count, size_t size)
+ {
+ return reinterpret_cast<T*> (
+ reinterpret_cast<char*> (base) + count * size);
+ }
+
+ void statement::
+ bind_param (native_binding& ns, const binding& bs, size_t pos)
+ {
+ assert (ns.count == bs.count);
+
+ for (size_t i (0); i < ns.count; ++i)
+ {
+ const bind& b (bs.bind[i]);
+
+ ns.formats[i] = 1;
+
+ bool* n (b.is_null != 0 ? offset (b.is_null, pos, bs.skip) : 0);
+
+ if ((n != 0 && *n) || b.buffer == 0) // Handle NULL entries.
+ {
+ ns.values[i] = 0;
+ ns.lengths[i] = 0;
+ continue;
+ }
+
+ ns.values[i] = static_cast<char*> (offset (b.buffer, pos, bs.skip));
+
+ size_t l (0);
+
+ switch (b.type)
+ {
+ case bind::boolean_:
+ {
+ l = sizeof (bool);
+ break;
+ }
+ case bind::smallint:
+ {
+ l = sizeof (short);
+ break;
+ }
+ case bind::integer:
+ {
+ l = sizeof (int);
+ break;
+ }
+ case bind::bigint:
+ {
+ l = sizeof (long long);
+ break;
+ }
+ case bind::real:
+ {
+ l = sizeof (float);
+ break;
+ }
+ case bind::double_:
+ {
+ l = sizeof (double);
+ break;
+ }
+ case bind::date:
+ {
+ l = sizeof (int);
+ break;
+ }
+ case bind::time:
+ case bind::timestamp:
+ {
+ l = sizeof (long long);
+ break;
+ }
+ case bind::numeric:
+ case bind::text:
+ case bind::bytea:
+ case bind::varbit:
+ {
+ // In this case b.buffer is a pointer to pointer to buffer so we
+ // need to chase one level.
+ //
+ ns.values[i] = static_cast<char*> (
+ *reinterpret_cast<void**> (ns.values[i]));
+ }
+ // Fall through.
+ case bind::bit:
+ {
+ l = *offset (b.size, pos, bs.skip);
+ break;
+ }
+ case bind::uuid:
+ {
+ // UUID is a 16-byte sequence.
+ //
+ l = 16;
+ break;
+ }
+ }
+
+ ns.lengths[i] = static_cast<int> (l);
+ }
+ }
+
+ bool statement::
+ bind_result (const binding& bs,
+ PGresult* result,
+ size_t row,
+ bool truncated,
+ size_t pos)
+ {
+ bool r (true);
+ int col_count (PQnfields (result));
+
+ int col (0);
+ for (size_t i (0); i != bs.count && col != col_count; ++i)
+ {
+ const bind& b (bs.bind[i]);
+
+ if (b.buffer == 0) // Skip NULL entries.
+ continue;
+
+ int c (col++);
+
+ {
+ bool* t (b.truncated != 0 ? offset (b.truncated, pos, bs.skip) : 0);
+
+ if (truncated && (t == 0 || !*t))
+ continue;
+
+ if (t != 0)
+ *t = false;
+ }
+
+ // Check for NULL unless we are reloading a truncated result.
+ //
+ if (!truncated)
+ {
+ bool* n (offset (b.is_null, pos, bs.skip));
+
+ *n = PQgetisnull (result, static_cast<int> (row), c) == 1;
+
+ if (*n)
+ continue;
+ }
+
+ void* buf (offset (b.buffer, pos, bs.skip));
+
+ const char* v (PQgetvalue (result, static_cast<int> (row), c));
+
+ switch (b.type)
+ {
+ case bind::boolean_:
+ {
+ *static_cast<bool*> (buf) = *reinterpret_cast<const bool*> (v);
+ break;
+ }
+ case bind::smallint:
+ case bind::integer:
+ case bind::bigint:
+ {
+ // If we are dealing with a custom schema, we may have a
+ // difference in the integer widths. Note also that we have
+ // to go to our endianness and back in order for casts to
+ // work properly.
+ //
+ long long i (0);
+
+ switch (PQftype (result, c))
+ {
+ case int2_oid:
+ {
+ i = endian_traits::ntoh (*reinterpret_cast<const short*> (v));
+ break;
+ }
+ case int4_oid:
+ {
+ i = endian_traits::ntoh (*reinterpret_cast<const int*> (v));
+ break;
+ }
+ case int8_oid:
+ {
+ i = endian_traits::ntoh (
+ *reinterpret_cast<const long long*> (v));
+ break;
+ }
+ default:
+ {
+ assert (false); // Column in the database is not an integer.
+ break;
+ }
+ }
+
+ switch (b.type)
+ {
+ case bind::smallint:
+ {
+ *static_cast<short*> (buf) =
+ endian_traits::hton (static_cast<short> (i));
+ break;
+ }
+ case bind::integer:
+ {
+ *static_cast<int*> (buf) =
+ endian_traits::hton (static_cast<int> (i));
+ break;
+ }
+ case bind::bigint:
+ {
+ *static_cast<long long*> (buf) = endian_traits::hton (i);
+ break;
+ }
+ default:
+ break;
+ }
+
+ break;
+ }
+ case bind::real:
+ {
+ *static_cast<float*> (buf) = *reinterpret_cast<const float*> (v);
+ break;
+ }
+ case bind::double_:
+ {
+ *static_cast<double*> (buf) = *reinterpret_cast<const double*> (v);
+ break;
+ }
+ case bind::date:
+ {
+ *static_cast<int*> (buf) = *reinterpret_cast<const int*> (v);
+ break;
+ }
+ case bind::time:
+ case bind::timestamp:
+ {
+ *static_cast<long long*> (buf) =
+ *reinterpret_cast<const long long*> (v);
+ break;
+ }
+ case bind::numeric:
+ case bind::text:
+ case bind::bytea:
+ case bind::bit:
+ case bind::varbit:
+ {
+ // Currently this is neither supported (due to capacity) nor used
+ // in batches.
+ //
+#ifdef LIBPGSQL_EXTRA_CHECKS
+ assert (pos == 0);
+#endif
+
+ *b.size = static_cast<size_t> (
+ PQgetlength (result, static_cast<int> (row), c));
+
+ if (b.capacity < *b.size)
+ {
+ if (b.truncated)
+ *b.truncated = true;
+
+ r = false;
+ continue;
+ }
+
+ // In these cases b.buffer is a pointer to pointer to buffer so we
+ // need to chase one level.
+ //
+ if (b.type != bind::bit)
+ buf = *static_cast<void**> (buf);
+
+ memcpy (buf, v, *b.size);
+ break;
+ }
+ case bind::uuid:
+ {
+ memcpy (buf, v, 16);
+ break;
+ }
+ }
+ }
+
+ // Make sure that the number of columns in the result returned by
+ // the database matches the number that we expect. A common cause
+ // of this assertion is a native view with a number of data members
+ // not matching the number of columns in the SELECT-list.
+ //
+ assert (col == col_count);
+
+ return r;
+ }
+
+#if defined(LIBPQ_HAS_PIPELINING) && !defined(_WIN32)
+
+ // Note that this function always marks the connection as failed.
+ //
+ static void
+ translate_connection_error (connection& conn)
+ {
+ const char* m (PQerrorMessage (conn.handle ()));
+
+ if (PQstatus (conn.handle ()) == CONNECTION_BAD)
+ {
+ conn.mark_failed ();
+ throw connection_lost ();
+ }
+ else
+ {
+ conn.mark_failed ();
+ throw database_exception (m != 0 ? m : "bad connection state");
+ }
+ }
+
+ // A RAII object for PGconn's non-blocking pipeline mode.
+ //
+ struct pipeline
+ {
+ connection& conn;
+ int sock;
+
+ explicit
+ pipeline (connection& c)
+ : conn (c)
+ {
+ PGconn* ch (conn.handle ());
+
+ if ((sock = PQsocket (ch)) == -1 ||
+ PQsetnonblocking (ch, 1) == -1 ||
+ PQenterPipelineMode (ch) == 0)
+ {
+ translate_connection_error (conn);
+ }
+ }
+
+ void
+ close (bool throw_ = true)
+ {
+ if (!conn.failed ())
+ {
+ PGconn* ch (conn.handle ());
+
+ if (PQexitPipelineMode (ch) == 0 ||
+ PQsetnonblocking (ch, 0) == -1)
+ {
+ if (throw_)
+ translate_connection_error (conn);
+ else
+ conn.mark_failed ();
+ }
+ }
+ }
+
+ ~pipeline ()
+ {
+ close (false);
+ }
+
+ pair<bool /* read */, bool /* write */>
+ wait (bool write, bool throw_ = true)
+ {
+ fd_set wds;
+ fd_set rds;
+
+ for (;;)
+ {
+ if (write)
+ {
+ FD_ZERO (&wds);
+ FD_SET (sock, &wds);
+ }
+
+ FD_ZERO (&rds);
+ FD_SET (sock, &rds);
+
+ if (select (sock + 1, &rds, write ? &wds : 0, 0, 0) != -1)
+ break;
+
+ if (errno != EINTR)
+ {
+ if (throw_)
+ translate_connection_error (conn);
+ else
+ {
+ conn.mark_failed ();
+ return pair<bool, bool> (false, false);
+ }
+ }
+ }
+
+ return pair<bool, bool> (FD_ISSET (sock, &rds),
+ write && FD_ISSET (sock, &wds));
+ }
+ };
+
+ // A RAII object for recovering from an error in a pipeline.
+ //
+ // Specifically, it reads and discards results until reaching
+ // PGRES_PIPELINE_SYNC.
+ //
+ struct pipeline_recovery
+ {
+ pipeline_recovery (pipeline& pl, bool wdone, bool sync)
+ : pl_ (&pl), wdone_ (wdone), sync_ (sync)
+ {
+ }
+
+ ~pipeline_recovery ()
+ {
+ if (pl_ != 0 && !pl_->conn.failed ())
+ {
+ PGconn* ch (pl_->conn.handle ());
+
+ // This code runs as part of stack unwinding caused by an exception
+ // so if we encounter an error, we "upgrade" the existing exception
+ // by marking the connection as failed.
+ //
+ // The rest is essentially a special version of execute() below.
+ //
+ // Note that on the first iteration we may still have results from
+ // the previous call to PQconsumeInput() (and these results may
+ // be the entire outstanding sequence, in which case calling wait()
+ // will block indefinitely).
+ //
+ for (bool first (true);; first = false)
+ {
+ if (sync_)
+ {
+ assert (!wdone_);
+
+ if (PQpipelineSync (ch) == 0)
+ break;
+
+ sync_ = false;
+ }
+
+ pair<bool, bool> r (false, false);
+
+ if (!first)
+ {
+ r = pl_->wait (!wdone_);
+ if (!r.first && !r.second)
+ break;
+ }
+
+ if (r.first /* read */ || first)
+ {
+ if (r.first && PQconsumeInput (ch) == 0)
+ break;
+
+ while (PQisBusy (ch) == 0)
+ {
+ auto_handle<PGresult> res (PQgetResult (ch));
+
+ // We should only get NULLs as well as PGRES_PIPELINE_ABORTED
+ // finished with PGRES_PIPELINE_SYNC.
+ //
+ if (res != 0)
+ {
+ ExecStatusType stat (PQresultStatus (res));
+
+ if (stat == PGRES_PIPELINE_SYNC)
+ return;
+
+ assert (stat == PGRES_PIPELINE_ABORTED);
+ }
+ }
+ }
+
+ if (r.second /* write */)
+ {
+ int r (PQflush (ch));
+ if (r == -1)
+ break;
+
+ if (r == 0)
+ wdone_ = true;
+ }
+ }
+
+ pl_->conn.mark_failed ();
+ }
+ }
+
+ void
+ cancel ()
+ {
+ pl_ = 0;
+ }
+
+ private:
+ pipeline* pl_;
+ bool wdone_;
+ bool sync_;
+ };
+
+ size_t statement::
+ execute (const binding& param,
+ native_binding& native_param,
+ size_t n,
+ multiple_exceptions& mex,
+ bool (*process) (size_t, PGresult*, bool, void*),
+ void* data)
+ {
+ size_t i (0); // Parameter set being attempted.
+ mex.current (i);
+
+ PGconn* ch (conn_.handle ());
+
+ pipeline pl (conn_);
+
+ // True if we've written and read everything, respectively.
+ //
+ bool wdone (false), rdone (false);
+
+ for (size_t wn (0), rn (0); !rdone; )
+ {
+ // Note that there is a special version of this code above in
+ // ~pipeline_recovery().
+ //
+ pair<bool, bool> r (pl.wait (!wdone));
+
+ // Note that once we start the pipeline, any call that may throw
+ // without marking the connection as failed should be guarded by
+ // pipeline_recovery.
+
+ // Try to minimize the chance of blocking the server by first
+ // processing the result and then sending more queries.
+ //
+ if (r.first /* read */)
+ {
+ if (PQconsumeInput (ch) == 0)
+ translate_connection_error (conn_);
+
+ // Note that PQisBusy() will return 0 (and subsequent PQgetResult()
+ // -- NULL) if we have consumed all the results for the queries that
+ // we have sent so far. Thus the (wn > rn) condition. Note that for
+ // this to work correctly we have to count the PQpipelineSync() call
+ // below as one of the queries (since it has a separate result).
+ //
+ while (wn > rn && PQisBusy (ch) == 0)
+ {
+ auto_handle<PGresult> res (PQgetResult (ch));
+
+ ExecStatusType stat (PGRES_FATAL_ERROR);
+ bool gr (is_good_result (res, &stat));
+
+ if (stat == PGRES_PIPELINE_SYNC)
+ {
+ assert (wdone && rn == n);
+ rdone = true;
+ break;
+ }
+
+ assert (rn != n);
+ ++rn;
+
+ if (stat != PGRES_PIPELINE_ABORTED)
+ {
+ // translate_error() may throw an exception (e.g., deadlock)
+ // without marking the connection as failed.
+ //
+ {
+ pipeline_recovery plr (pl, wdone, wn < n);
+
+ if (!process (i, res, gr, data))
+ translate_error (conn_, res, i, &mex);
+
+ plr.cancel ();
+ }
+
+ mex.attempted (++i);
+ mex.current (i);
+ }
+ else
+ {
+ // Should we treat PGRES_PIPELINE_ABORTED entries as attempted
+ // or not? While we did issue PQsendQueryPrepared() for them,
+ // the server tells us that it did not attemp to execute them.
+ // So it feels like they should not be treated as attempted.
+ //
+ // Note that for this to fit into out multiple_exceptions model,
+ // such an incomplete batch should be fatal (otherwise we could
+ // end up with unattempted "holes"). This is currently the case
+ // for errors handled by translate_error() but not necessarily
+ // the case for those handled by the process function (e.g.,
+ // duplicate id handled by process_insert_result() below). So in
+ // a somewhat hackish way we assume the error (e.g., duplicate
+ // id) will always be translated to an exception and pre-mark
+ // multiple_exceptions as fatal.
+ //
+ mex.fatal (true);
+ }
+
+ // We get a NULL result after each query result.
+ //
+ {
+ PGresult* end (PQgetResult (ch));
+ assert (end == 0);
+ }
+ }
+ }
+
+ if (r.second /* write */)
+ {
+ // Send queries until we get blocked (write-biased). This feels like
+ // a better overall strategy to keep the server busy compared to
+ // sending one query at a time and then re-checking if there is
+ // anything to read because the results of INSERT/UPDATE/DELETE are
+ // presumably small and quite a few of them can get buffered before
+ // the server gets blocked.
+ //
+ for (;;)
+ {
+ if (wn < n)
+ {
+ bind_param (native_param, param, wn);
+
+ if (PQsendQueryPrepared (ch,
+ name_,
+ static_cast<int> (native_param.count),
+ native_param.values,
+ native_param.lengths,
+ native_param.formats,
+ 1) == 0)
+ translate_connection_error (conn_);
+
+ if (++wn == n)
+ {
+ if (PQpipelineSync (ch) == 0)
+ translate_connection_error (conn_);
+
+ // Count as one of the queries since it has a separate result.
+ //
+ ++wn;
+ }
+ }
+
+ // PQflush() result:
+ //
+ // 0 -- success (queue is now empty)
+ // 1 -- blocked
+ // -1 -- error
+ //
+ int r (PQflush (ch));
+ if (r == -1)
+ translate_connection_error (conn_);
+
+ if (r == 0)
+ {
+ if (wn < n)
+ {
+ // If we continue here, then we are write-biased. And if we
+ // break, then we are read-biased.
+ //
+#ifdef LIBPGSQL_READ_BIASED
+ break;
+#else
+ continue;
+#endif
+ }
+
+ wdone = true;
+ }
+
+ break; // Blocked or done.
+ }
+ }
+ }
+
+ pl.close ();
+ return i;
+ }
+#endif
+
+ //
+ // select_statement
+ //
+
+ select_statement::
+ ~select_statement ()
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process,
+ bool optimize,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding& result)
+ : statement (conn,
+ name, text, statement_select,
+ (process ? &result : 0), optimize,
+ types, types_count),
+ param_ (&param),
+ native_param_ (&native_param),
+ result_ (result),
+ row_count_ (0),
+ current_row_ (0)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ bool process,
+ bool optimize,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding& result,
+ bool copy)
+ : statement (conn,
+ name, text, statement_select,
+ (process ? &result : 0), optimize, copy,
+ types, types_count),
+ param_ (&param),
+ native_param_ (&native_param),
+ result_ (result),
+ row_count_ (0),
+ current_row_ (0)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process,
+ bool optimize,
+ binding& result)
+ : statement (conn,
+ name, text, statement_select,
+ (process ? &result : 0), optimize,
+ 0, 0),
+ param_ (0),
+ native_param_ (0),
+ result_ (result),
+ row_count_ (0),
+ current_row_ (0)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ bool process,
+ bool optimize,
+ binding& result,
+ bool copy)
+ : statement (conn,
+ name, text, statement_select,
+ (process ? &result : 0), optimize, copy,
+ 0, 0),
+ param_ (0),
+ native_param_ (0),
+ result_ (result),
+ row_count_ (0),
+ current_row_ (0)
+ {
+ }
+
+ select_statement::
+ select_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process,
+ bool optimize,
+ const Oid* types,
+ std::size_t types_count,
+ native_binding& native_param,
+ binding& result)
+ : statement (conn,
+ name, text, statement_select,
+ (process ? &result : 0), optimize,
+ types, types_count),
+ param_ (0),
+ native_param_ (&native_param),
+ result_ (result),
+ row_count_ (0),
+ current_row_ (0)
+ {
+ }
+
+ void select_statement::
+ execute ()
+ {
+ handle_.reset ();
+
+ if (param_ != 0)
+ bind_param (*native_param_, *param_);
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ bool in (native_param_ != 0);
+
+ handle_.reset (
+ PQexecPrepared (conn_.handle (),
+ name_,
+ in ? static_cast<int> (native_param_->count) : 0,
+ in ? native_param_->values : 0,
+ in ? native_param_->lengths : 0,
+ in ? native_param_->formats : 0,
+ 1));
+
+ if (!is_good_result (handle_))
+ translate_error (conn_, handle_);
+
+ row_count_ = static_cast<size_t> (PQntuples (handle_));
+ current_row_ = 0;
+ }
+
+ void select_statement::
+ free_result ()
+ {
+ handle_.reset ();
+ row_count_ = 0;
+ current_row_ = 0;
+ }
+
+ bool select_statement::
+ next ()
+ {
+ if (current_row_ <= row_count_)
+ ++current_row_;
+
+ return current_row_ <= row_count_;
+ }
+
+ select_statement::result select_statement::
+ load ()
+ {
+ if (current_row_ > row_count_)
+ return no_data;
+
+ assert (current_row_ > 0);
+ return bind_result (result_, handle_, current_row_ - 1)
+ ? success
+ : truncated;
+ }
+
+ void select_statement::
+ reload ()
+ {
+ assert (current_row_ > 0);
+ assert (current_row_ <= row_count_);
+
+ if (!bind_result (result_, handle_, current_row_ - 1, true))
+ assert (false);
+ }
+
+ //
+ // insert_statement
+ //
+
+ insert_statement::
+ ~insert_statement ()
+ {
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const string& name,
+ const string& text,
+ bool process,
+ const Oid* types,
+ size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding* returning)
+ : statement (conn,
+ name, text, statement_insert,
+ (process ? &param : 0), false,
+ types, types_count),
+ param_ (param),
+ native_param_ (native_param),
+ returning_ (returning)
+ {
+ if (returning_ != 0)
+ assert (returning_->count == 1);
+ }
+
+ insert_statement::
+ insert_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ bool process,
+ const Oid* types,
+ size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding* returning,
+ bool copy)
+ : statement (conn,
+ name, text, statement_insert,
+ (process ? &param : 0), false, copy,
+ types, types_count),
+ param_ (param),
+ native_param_ (native_param),
+ returning_ (returning)
+ {
+ if (returning_ != 0)
+ assert (returning_->count == 1);
+ }
+
+ bool insert_statement::
+ execute ()
+ {
+ bind_param (native_param_, param_);
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ auto_handle<PGresult> h (
+ PQexecPrepared (conn_.handle (),
+ name_,
+ static_cast<int> (native_param_.count),
+ native_param_.values,
+ native_param_.lengths,
+ native_param_.formats,
+ 1));
+
+ ExecStatusType stat (PGRES_FATAL_ERROR);
+
+ if (!is_good_result (h, &stat))
+ {
+ // An auto-assigned object id should never cause a duplicate
+ // primary key.
+ //
+ if (returning_ == 0 && stat == PGRES_FATAL_ERROR)
+ {
+ const char* ss (PQresultErrorField (h, PG_DIAG_SQLSTATE));
+
+ if (ss != 0 && strcmp (ss, "23505") == 0)
+ return false;
+ }
+
+ translate_error (conn_, h);
+ }
+
+ if (returning_ != 0)
+ bind_result (*returning_, h, 0);
+
+ return true;
+ }
+
+#if defined(LIBPQ_HAS_PIPELINING) && !defined(_WIN32)
+
+ struct insert_data
+ {
+ binding& param;
+ binding* returning;
+ };
+
+ static bool
+ process_insert_result (size_t i, PGresult* r, bool gr, void* data)
+ {
+ insert_data& d (*static_cast<insert_data*> (data));
+
+ unsigned long long& s (d.param.status[i]);
+ s = 1;
+
+ if (gr)
+ {
+ // Note that the result can never be truncated.
+ //
+ if (d.returning != 0)
+ statement::bind_result (*d.returning, r, 0, false, i);
+ }
+ else
+ {
+ // An auto-assigned object id should never cause a duplicate
+ // primary key.
+ //
+ if (d.returning == 0 &&
+ r != 0 && PQresultStatus (r) == PGRES_FATAL_ERROR)
+ {
+ // Note that statement::execute() assumes that this will eventually
+ // be translated to an entry in multiple_exceptions.
+ //
+ const char* ss (PQresultErrorField (r, PG_DIAG_SQLSTATE));
+
+ if (ss != 0 && strcmp (ss, "23505") == 0)
+ s = 0;
+ }
+
+ if (s == 1)
+ return false;
+ }
+
+ return true;
+ }
+
+ size_t insert_statement::
+ execute (size_t n, multiple_exceptions& mex)
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ insert_data d {param_, returning_};
+
+ return statement::execute (
+ param_, native_param_, n, mex, &process_insert_result, &d);
+ }
+#endif
+
+ //
+ // update_statement
+ //
+
+ update_statement::
+ ~update_statement ()
+ {
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const string& name,
+ const string& text,
+ bool process,
+ const Oid* types,
+ size_t types_count,
+ binding& param,
+ native_binding& native_param)
+ : statement (conn,
+ name, text, statement_update,
+ (process ? &param : 0), false,
+ types, types_count),
+ param_ (param),
+ native_param_ (native_param)
+ {
+ }
+
+ update_statement::
+ update_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ bool process,
+ const Oid* types,
+ size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ bool copy)
+ : statement (conn,
+ name, text, statement_update,
+ (process ? &param : 0), false, copy,
+ types, types_count),
+ param_ (param),
+ native_param_ (native_param)
+ {
+ }
+
+ unsigned long long update_statement::
+ execute ()
+ {
+ bind_param (native_param_, param_);
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ auto_handle<PGresult> h (
+ PQexecPrepared (conn_.handle (),
+ name_,
+ static_cast<int> (native_param_.count),
+ native_param_.values,
+ native_param_.lengths,
+ native_param_.formats,
+ 1));
+
+ if (!is_good_result (h))
+ translate_error (conn_, h);
+
+ return affected_row_count (h);
+ }
+
+#if defined(LIBPQ_HAS_PIPELINING) && !defined(_WIN32)
+
+ static bool
+ process_update_result (size_t i, PGresult* r, bool gr, void* data)
+ {
+ binding& param (*static_cast<binding*> (data));
+
+ unsigned long long& s (param.status[i]);
+
+ if (gr)
+ {
+ s = affected_row_count (r);
+ return true;
+ }
+ else
+ {
+ s = update_statement::result_unknown;
+ return false;
+ }
+ }
+
+ size_t update_statement::
+ execute (size_t n, multiple_exceptions& mex)
+ {
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ return statement::execute (
+ param_, native_param_, n, mex, &process_update_result, &param_);
+ }
+#endif
+
+ //
+ // delete_statement
+ //
+
+ delete_statement::
+ ~delete_statement ()
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const string& name,
+ const string& text,
+ const Oid* types,
+ size_t types_count,
+ binding& param,
+ native_binding& native_param)
+ : statement (conn,
+ name, text, statement_delete,
+ 0, false,
+ types, types_count),
+ param_ (&param),
+ native_param_ (native_param)
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ const Oid* types,
+ size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ bool copy)
+ : statement (conn,
+ name, text, statement_delete,
+ 0, false, copy,
+ types, types_count),
+ param_ (&param),
+ native_param_ (native_param)
+ {
+ }
+
+ delete_statement::
+ delete_statement (connection_type& conn,
+ const string& name,
+ const string& text,
+ const Oid* types,
+ size_t types_count,
+ native_binding& native_param)
+ : statement (conn,
+ name, text, statement_delete,
+ 0, false,
+ types, types_count),
+ param_ (0),
+ native_param_ (native_param)
+ {
+ }
+
+ unsigned long long delete_statement::
+ execute ()
+ {
+ if (param_ != 0)
+ bind_param (native_param_, *param_);
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ auto_handle<PGresult> h (
+ PQexecPrepared (conn_.handle (),
+ name_,
+ static_cast<int> (native_param_.count),
+ native_param_.values,
+ native_param_.lengths,
+ native_param_.formats,
+ 1));
+
+ if (!is_good_result (h))
+ translate_error (conn_, h);
+
+ return affected_row_count (h);
+ }
+
+#if defined(LIBPQ_HAS_PIPELINING) && !defined(_WIN32)
+
+ static bool
+ process_delete_result (size_t i, PGresult* r, bool gr, void* data)
+ {
+ binding& param (*static_cast<binding*> (data));
+
+ unsigned long long& s (param.status[i]);
+
+ if (gr)
+ {
+ s = affected_row_count (r);
+ return true;
+ }
+ else
+ {
+ s = delete_statement::result_unknown;
+ return false;
+ }
+ }
+
+ size_t delete_statement::
+ execute (size_t n, multiple_exceptions& mex)
+ {
+ assert (param_ != 0);
+
+ {
+ odb::tracer* t;
+ if ((t = conn_.transaction_tracer ()) ||
+ (t = conn_.tracer ()) ||
+ (t = conn_.database ().tracer ()))
+ t->execute (conn_, *this);
+ }
+
+ return statement::execute (
+ *param_, native_param_, n, mex, &process_delete_result, param_);
+ }
+#endif
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/statement.hxx b/libodb-pgsql/odb/pgsql/statement.hxx
new file mode 100644
index 0000000..139d2d6
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/statement.hxx
@@ -0,0 +1,450 @@
+// file : odb/pgsql/statement.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_STATEMENT_HXX
+#define ODB_PGSQL_STATEMENT_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+#include <odb/statement.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/pgsql-fwd.hxx> // PGresult
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/auto-handle.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class connection;
+
+ class LIBODB_PGSQL_EXPORT statement: public odb::statement
+ {
+ public:
+ typedef pgsql::connection connection_type;
+
+ virtual
+ ~statement () = 0;
+
+ const char*
+ name () const
+ {
+ return name_;
+ }
+
+ virtual const char*
+ text () const;
+
+ virtual connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // A statement can be empty. This is used to handle situations
+ // where a SELECT or UPDATE statement ends up not having any
+ // columns after processing. An empty statement cannot be
+ // executed.
+ //
+ bool
+ empty () const
+ {
+ return *text_ == '\0';
+ }
+
+ void
+ deallocate ();
+
+ // Adapt an ODB binding to a native PostgreSQL parameter binding. If pos
+ // is not 0, then bind the parameter set at this position in a batch.
+ //
+ static void
+ bind_param (native_binding&, const binding&, std::size_t pos = 0);
+
+ // Populate an ODB binding given a PostgreSQL result. If the truncated
+ // argument is true, then only truncated columns are extracted. Return
+ // true if all the data was extracted successfully and false if one or
+ // more columns were truncated. If pos is not 0, then populate the
+ // parameter set at this position in a batch.
+ //
+ static bool
+ bind_result (const binding&,
+ PGresult*,
+ std::size_t row,
+ bool truncated = false,
+ std::size_t pos = 0);
+
+ protected:
+ // We keep two versions to take advantage of std::string COW.
+ //
+ statement (connection_type&,
+ const std::string& name,
+ const std::string& text,
+ statement_kind,
+ const binding* process,
+ bool optimize,
+ const Oid* types,
+ std::size_t types_count);
+
+ statement (connection_type&,
+ const char* name,
+ const char* text,
+ statement_kind,
+ const binding* process,
+ bool optimize,
+ bool copy_name_text,
+ const Oid* types,
+ std::size_t types_count);
+
+ // Bulk execute implementation.
+ //
+ std::size_t
+ execute (const binding& param,
+ native_binding& native_param,
+ std::size_t n,
+ multiple_exceptions&,
+ bool (*process) (size_t i, PGresult*, bool good, void* data),
+ void* data);
+
+ private:
+ void
+ init (statement_kind,
+ const binding* process,
+ bool optimize,
+ const Oid* types,
+ std::size_t types_count);
+
+ protected:
+ connection_type& conn_;
+
+ std::string name_copy_;
+ const char* name_;
+
+ std::string text_copy_;
+ const char* text_;
+
+ private:
+ bool deallocated_;
+ };
+
+ class LIBODB_PGSQL_EXPORT select_statement: public statement
+ {
+ public:
+ virtual
+ ~select_statement ();
+
+ select_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* name,
+ const char* stmt,
+ bool process_text,
+ bool optimize_text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding& result,
+ bool copy_name_text = true);
+
+ select_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ binding& result);
+
+ select_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ bool process_text,
+ bool optimize_text,
+ binding& result,
+ bool copy_name_text = true);
+
+ select_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process_text,
+ bool optimize_text,
+ const Oid* types,
+ std::size_t types_count,
+ native_binding& native_param,
+ binding& result);
+
+ // Common select interface expected by the generated code.
+ //
+ public:
+ enum result
+ {
+ success,
+ no_data,
+ truncated
+ };
+
+ void
+ execute ();
+
+ void
+ cache () const
+ {
+ }
+
+ std::size_t
+ result_size () const
+ {
+ return row_count_;
+ }
+
+ // Load next row columns into bound buffers.
+ //
+ result
+ fetch ()
+ {
+ return next () ? load () : no_data;
+ }
+
+ // Reload truncated columns into bound buffers.
+ //
+ void
+ refetch ()
+ {
+ reload ();
+ }
+
+ // Free the result set.
+ //
+ void
+ free_result ();
+
+ // Finer grained control of PostgreSQL-specific interface that
+ // splits fetch() into next() and load().
+ //
+ public:
+ bool
+ next ();
+
+ result
+ load ();
+
+ void
+ reload ();
+
+ private:
+ select_statement (const select_statement&);
+ select_statement& operator= (const select_statement&);
+
+ private:
+ binding* param_;
+ native_binding* native_param_;
+
+ binding& result_;
+
+ auto_handle<PGresult> handle_;
+ std::size_t row_count_;
+ std::size_t current_row_;
+ };
+
+ struct LIBODB_PGSQL_EXPORT auto_result
+ {
+ explicit auto_result (select_statement& s): s_ (s) {}
+ ~auto_result () {s_.free_result ();}
+
+ private:
+ auto_result (const auto_result&);
+ auto_result& operator= (const auto_result&);
+
+ private:
+ select_statement& s_;
+ };
+
+ class LIBODB_PGSQL_EXPORT insert_statement: public statement
+ {
+ public:
+ virtual
+ ~insert_statement ();
+
+ insert_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process_text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding* returning);
+
+ insert_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ bool process_text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ binding* returning,
+ bool copy_name_text = true);
+
+ // Return true if successful and false if the row is a duplicate.
+ // All other errors are reported by throwing exceptions.
+ //
+ bool
+ execute ();
+
+ // Return the number of parameter sets (out of n) that were attempted.
+ //
+ std::size_t
+ execute (std::size_t n, multiple_exceptions&);
+
+ // Return true if successful and false if this row is a duplicate.
+ // All other errors are reported via exceptions.
+ //
+ bool
+ result (std::size_t i)
+ {
+ return param_.status[i] != 0;
+ }
+
+ private:
+ insert_statement (const insert_statement&);
+ insert_statement& operator= (const insert_statement&);
+
+ private:
+ binding& param_;
+ native_binding& native_param_;
+ binding* returning_;
+ };
+
+ class LIBODB_PGSQL_EXPORT update_statement: public statement
+ {
+ public:
+ virtual
+ ~update_statement ();
+
+ update_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ bool process_text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param);
+
+ update_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ bool process_text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ bool copy_name_text = true);
+
+ unsigned long long
+ execute ();
+
+ // Return the number of parameter sets (out of n) that were attempted.
+ //
+ std::size_t
+ execute (std::size_t n, multiple_exceptions&);
+
+ // Return the number of rows affected (updated) by the parameter
+ // set. All errors are reported by throwing exceptions.
+ //
+ static const unsigned long long result_unknown = ~0ULL;
+
+ unsigned long long
+ result (std::size_t i)
+ {
+ return param_.status[i];
+ }
+
+ private:
+ update_statement (const update_statement&);
+ update_statement& operator= (const update_statement&);
+
+ private:
+ binding& param_;
+ native_binding& native_param_;
+ };
+
+ class LIBODB_PGSQL_EXPORT delete_statement: public statement
+ {
+ public:
+ virtual
+ ~delete_statement ();
+
+ delete_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param);
+
+ delete_statement (connection_type& conn,
+ const char* name,
+ const char* text,
+ const Oid* types,
+ std::size_t types_count,
+ binding& param,
+ native_binding& native_param,
+ bool copy_name_text = true);
+
+ delete_statement (connection_type& conn,
+ const std::string& name,
+ const std::string& text,
+ const Oid* types,
+ std::size_t types_count,
+ native_binding& native_param);
+
+ unsigned long long
+ execute ();
+
+ // Return the number of parameter sets (out of n) that were attempted.
+ //
+ std::size_t
+ execute (std::size_t n, multiple_exceptions&);
+
+ // Return the number of rows affected (deleted) by the parameter
+ // set. All errors are reported by throwing exceptions.
+ //
+ static const unsigned long long result_unknown = ~0ULL;
+
+ unsigned long long
+ result (std::size_t i)
+ {
+ return param_->status[i];
+ }
+
+ private:
+ delete_statement (const delete_statement&);
+ delete_statement& operator= (const delete_statement&);
+
+ private:
+ binding* param_;
+ native_binding& native_param_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_STATEMENT_HXX
diff --git a/libodb-pgsql/odb/pgsql/statements-base.cxx b/libodb-pgsql/odb/pgsql/statements-base.cxx
new file mode 100644
index 0000000..0e72555
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/statements-base.cxx
@@ -0,0 +1,15 @@
+// file : odb/pgsql/statements-base.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/statements-base.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ statements_base::
+ ~statements_base ()
+ {
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/statements-base.hxx b/libodb-pgsql/odb/pgsql/statements-base.hxx
new file mode 100644
index 0000000..8b45bdc
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/statements-base.hxx
@@ -0,0 +1,63 @@
+// file : odb/pgsql/statements-base.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_STATEMENTS_BASE_HXX
+#define ODB_PGSQL_STATEMENTS_BASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/schema-version.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/database.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class LIBODB_PGSQL_EXPORT statements_base: public details::shared_base
+ {
+ public:
+ typedef pgsql::connection connection_type;
+
+ connection_type&
+ connection ()
+ {
+ return conn_;
+ }
+
+ // Schema version. database::schema_version_migration() is thread-
+ // safe which means it is also slow. Cache the result in statements
+ // so we can avoid the mutex lock. This is thread-safe since if the
+ // version is updated, then the statements cache will be expired.
+ //
+ const schema_version_migration&
+ version_migration (const char* name = "") const
+ {
+ if (svm_ == 0)
+ svm_ = &conn_.database ().schema_version_migration (name);
+
+ return *svm_;
+ }
+
+ public:
+ virtual
+ ~statements_base ();
+
+ protected:
+ statements_base (connection_type& conn): conn_ (conn), svm_ (0) {}
+
+ protected:
+ connection_type& conn_;
+ mutable const schema_version_migration* svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_STATEMENTS_BASE_HXX
diff --git a/libodb-pgsql/odb/pgsql/tracer.cxx b/libodb-pgsql/odb/pgsql/tracer.cxx
new file mode 100644
index 0000000..48e0cf8
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/tracer.cxx
@@ -0,0 +1,60 @@
+// file : odb/pgsql/tracer.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/tracer.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/statement.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ tracer::
+ ~tracer ()
+ {
+ }
+
+ void tracer::
+ prepare (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ execute (connection& c, const statement& s)
+ {
+ execute (c, s.text ());
+ }
+
+ void tracer::
+ deallocate (connection&, const statement&)
+ {
+ }
+
+ void tracer::
+ prepare (odb::connection& c, const odb::statement& s)
+ {
+ prepare (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const odb::statement& s)
+ {
+ execute (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+
+ void tracer::
+ execute (odb::connection& c, const char* s)
+ {
+ execute (static_cast<connection&> (c), s);
+ }
+
+ void tracer::
+ deallocate (odb::connection& c, const odb::statement& s)
+ {
+ deallocate (static_cast<connection&> (c),
+ static_cast<const statement&> (s));
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/tracer.hxx b/libodb-pgsql/odb/pgsql/tracer.hxx
new file mode 100644
index 0000000..89cda1c
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/tracer.hxx
@@ -0,0 +1,61 @@
+// file : odb/pgsql/tracer.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_TRACER_HXX
+#define ODB_PGSQL_TRACER_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/tracer.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class LIBODB_PGSQL_EXPORT tracer: private odb::tracer
+ {
+ public:
+ virtual
+ ~tracer ();
+
+ virtual void
+ prepare (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const char* statement) = 0;
+
+ virtual void
+ deallocate (connection&, const statement&);
+
+ private:
+ // Allow these classes to convert pgsql::tracer to odb::tracer.
+ //
+ friend class database;
+ friend class connection;
+ friend class transaction;
+
+ virtual void
+ prepare (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const odb::statement&);
+
+ virtual void
+ execute (odb::connection&, const char* statement);
+
+ virtual void
+ deallocate (odb::connection&, const odb::statement&);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_TRACER_HXX
diff --git a/libodb-pgsql/odb/pgsql/traits-calls.hxx b/libodb-pgsql/odb/pgsql/traits-calls.hxx
new file mode 100644
index 0000000..419c7b2
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/traits-calls.hxx
@@ -0,0 +1,214 @@
+// file : odb/pgsql/traits-calls.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_TRAITS_CALLS_HXX
+#define ODB_PGSQL_TRAITS_CALLS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/pgsql-types.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ //
+ // object_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = object_traits_impl<T, id_pgsql>::versioned>
+ struct object_traits_calls;
+
+ template <typename T>
+ struct object_traits_calls<T, false>
+ {
+ typedef object_traits_impl<T, id_pgsql> traits;
+ typedef typename traits::image_type image_type;
+ typedef pgsql::bind bind_type;
+
+ object_traits_calls (const schema_version_migration*) {}
+
+ const schema_version_migration*
+ version () const {return 0;}
+
+ static bool
+ grow (image_type& i, bool* t)
+ {
+ return traits::grow (i, t);
+ }
+
+ static void
+ bind (bind_type* b, image_type& i, statement_kind sk)
+ {
+ traits::bind (b, i, sk);
+ }
+
+ // Poly-derived version.
+ //
+ static void
+ bind (bind_type* b,
+ const bind_type* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk)
+ {
+ traits::bind (b, id, id_size, i, sk);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+
+ static bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id)
+ {
+ return traits::find_ (sts, id);
+ }
+
+ static void
+ load_ (typename traits::statements_type& sts, T& o, bool reload)
+ {
+ return traits::load_ (sts, o, reload);
+ }
+ };
+
+ template <typename T>
+ struct object_traits_calls<T, true>
+ {
+ typedef object_traits_impl<T, id_pgsql> traits;
+ typedef typename traits::image_type image_type;
+ typedef pgsql::bind bind_type;
+
+ object_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ const schema_version_migration*
+ version () const {return &svm_;}
+
+ bool
+ grow (image_type& i, bool* t) const
+ {
+ return traits::grow (i, t, svm_);
+ }
+
+ void
+ bind (bind_type* b, image_type& i, statement_kind sk) const
+ {
+ traits::bind (b, i, sk, svm_);
+ }
+
+ // Poly-derived version.
+ //
+ void
+ bind (bind_type* b,
+ const bind_type* id, std::size_t id_size,
+ image_type& i,
+ statement_kind sk) const
+ {
+ traits::bind (b, id, id_size, i, sk, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ bool
+ find_ (typename traits::statements_type& sts,
+ const typename traits::id_type* id) const
+ {
+ return traits::find_ (sts, id, svm_);
+ }
+
+ void
+ load_ (typename traits::statements_type& sts, T& o, bool reload) const
+ {
+ return traits::load_ (sts, o, reload, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+
+ //
+ // view_traits_calls
+ //
+
+ template <typename T,
+ bool versioned = view_traits_impl<T, id_pgsql>::versioned>
+ struct view_traits_calls;
+
+ template <typename T>
+ struct view_traits_calls<T, false>
+ {
+ typedef view_traits_impl<T, id_pgsql> traits;
+ typedef typename traits::image_type image_type;
+ typedef pgsql::bind bind_type;
+
+ view_traits_calls (const schema_version_migration*) {}
+
+ static bool
+ grow (image_type& i, bool* t)
+ {
+ return traits::grow (i, t);
+ }
+
+ static void
+ bind (bind_type* b, image_type& i)
+ {
+ traits::bind (b, i);
+ }
+
+ static void
+ init (T& o, const image_type& i, odb::database* db)
+ {
+ traits::init (o, i, db);
+ }
+ };
+
+ template <typename T>
+ struct view_traits_calls<T, true>
+ {
+ typedef view_traits_impl<T, id_pgsql> traits;
+ typedef typename traits::image_type image_type;
+ typedef pgsql::bind bind_type;
+
+ view_traits_calls (const schema_version_migration* svm): svm_ (*svm) {}
+
+ bool
+ grow (image_type& i, bool* t) const
+ {
+ return traits::grow (i, t, svm_);
+ }
+
+ void
+ bind (bind_type* b, image_type& i) const
+ {
+ traits::bind (b, i, svm_);
+ }
+
+ void
+ init (T& o, const image_type& i, odb::database* db) const
+ {
+ traits::init (o, i, db, svm_);
+ }
+
+ private:
+ const schema_version_migration& svm_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_TRAITS_CALLS_HXX
diff --git a/libodb-pgsql/odb/pgsql/traits.cxx b/libodb-pgsql/odb/pgsql/traits.cxx
new file mode 100644
index 0000000..11a3a67
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/traits.cxx
@@ -0,0 +1,143 @@
+// file : odb/pgsql/traits.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/traits.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ using details::buffer;
+
+ //
+ // default_value_traits<std::string, id_string>
+ //
+ void default_value_traits<std::string, id_string>::
+ set_image (buffer& b,
+ size_t& n,
+ bool& is_null,
+ const string& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v.c_str (), n);
+ }
+
+ //
+ // c_string_value_traits
+ //
+ void c_string_value_traits::
+ set_image (buffer& b,
+ size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ n = strlen (v);
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+
+ //
+ // c_array_value_traits_base
+ //
+ void c_array_value_traits_base::
+ set_value (char* const& v,
+ const details::buffer& b,
+ size_t n,
+ bool is_null,
+ size_t N)
+ {
+ if (!is_null)
+ {
+ n = n < N ? n : N;
+
+ if (n != 0)
+ memcpy (v, b.data (), n);
+ }
+ else
+ n = 0;
+
+ if (n != N) // Append '\0' if there is space.
+ v[n] = '\0';
+ }
+
+ void c_array_value_traits_base::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const char* v,
+ size_t N)
+ {
+ is_null = false;
+
+ // Figure out the length. We cannot use strlen since it may
+ // not be 0-terminated (strnlen is not standard).
+ //
+ for (n = 0; n != N && v[n] != '\0'; ++n) ;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ if (n != 0)
+ memcpy (b.data (), v, n);
+ }
+
+ //
+ // default_value_traits<vector<char>, id_bytea>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<char>, id_bytea>::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ memcpy (b.data (), &v.front (), n);
+ }
+
+ //
+ // default_value_traits<vector<unsigned char>, id_bytea>
+ //
+ // std::vector has to be qualified for Sun CC.
+ //
+ void default_value_traits<std::vector<unsigned char>, id_bytea>::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = v.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ // std::vector::data() may not be available in older compilers.
+ //
+ if (n != 0)
+ memcpy (b.data (), &v.front (), n);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/traits.hxx b/libodb-pgsql/odb/pgsql/traits.hxx
new file mode 100644
index 0000000..3d87033
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/traits.hxx
@@ -0,0 +1,944 @@
+// file : odb/pgsql/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_TRAITS_HXX
+#define ODB_PGSQL_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cstring> // std::memcpy, std::memset, std::strlen
+
+#ifdef ODB_CXX11
+# include <array>
+#endif
+
+#include <odb/traits.hxx>
+#include <odb/wrapper-traits.hxx>
+
+#include <odb/details/buffer.hxx>
+#include <odb/details/wrapper-p.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/pgsql-types.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+#include <odb/pgsql/details/endian-traits.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ enum database_type_id
+ {
+ id_boolean,
+ id_smallint,
+ id_integer,
+ id_bigint,
+
+ id_numeric, // Internal PostgreSQL binary representation.
+
+ id_real,
+ id_double,
+
+ id_date,
+ id_time,
+ id_timestamp,
+
+ id_string,
+ id_bytea,
+ id_bit,
+ id_varbit,
+
+ id_uuid
+ };
+
+ //
+ // image_traits
+ //
+
+ template <database_type_id>
+ struct image_traits;
+
+ template <>
+ struct image_traits<id_boolean> {typedef bool image_type;};
+
+ template <>
+ struct image_traits<id_smallint> {typedef short image_type;};
+
+ template <>
+ struct image_traits<id_integer> {typedef int image_type;};
+
+ template <>
+ struct image_traits<id_bigint> {typedef long long image_type;};
+
+ template <>
+ struct image_traits<id_numeric> {typedef details::buffer image_type;};
+
+ template <>
+ struct image_traits<id_real> {typedef float image_type;};
+
+ template <>
+ struct image_traits<id_double> {typedef double image_type;};
+
+ template <>
+ struct image_traits<id_date> {typedef int image_type;};
+
+ template <>
+ struct image_traits<id_time> {typedef long long image_type;};
+
+ template <>
+ struct image_traits<id_timestamp> {typedef long long image_type;};
+
+ template <>
+ struct image_traits<id_string> {typedef details::buffer image_type;};
+
+ template <>
+ struct image_traits<id_bytea> {typedef details::buffer image_type;};
+
+ template <>
+ struct image_traits<id_bit> {typedef unsigned char* image_type;};
+
+ template <>
+ struct image_traits<id_varbit> {typedef details::ubuffer image_type;};
+
+ // UUID image is a 16-byte sequence.
+ //
+ template <>
+ struct image_traits<id_uuid> {typedef unsigned char* image_type;};
+
+ //
+ // value_traits
+ //
+
+ template <typename W, database_type_id, bool null_handler>
+ struct wrapped_value_traits;
+
+ template <typename T, database_type_id>
+ struct default_value_traits;
+
+ template <typename T, database_type_id, bool w = details::wrapper_p<T>::r>
+ struct select_traits;
+
+ template <typename T, database_type_id ID>
+ struct select_traits<T, ID, false>
+ {
+ typedef default_value_traits<T, ID> type;
+ };
+
+ template <typename W, database_type_id ID>
+ struct select_traits<W, ID, true>
+ {
+ typedef
+ wrapped_value_traits<W, ID, wrapper_traits<W>::null_handler>
+ type;
+ };
+
+ template <typename T, database_type_id ID>
+ class value_traits: public select_traits<T, ID>::type
+ {
+ };
+
+ // The wrapped_value_traits specializations should be able to handle
+ // any value type which means we have to have every possible signature
+ // of the set_value() and set_image() functions.
+ //
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, false>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // String, BYTEA, and NUMERIC.
+ //
+ static void
+ set_value (W& v, const details::buffer& b, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::buffer& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // BIT.
+ //
+ static void
+ set_value (W& v, const unsigned char* i, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (unsigned char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+
+ // VARBIT.
+ //
+ static void
+ set_value (W& v, const details::ubuffer& b, std::size_t n, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::ubuffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // UUID.
+ //
+ static void
+ set_value (W& v, const unsigned char* i, bool is_null)
+ {
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (unsigned char* i, bool& is_null, const W& v)
+ {
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename W, database_type_id ID>
+ struct wrapped_value_traits<W, ID, true>
+ {
+ typedef wrapper_traits<W> wtraits;
+ typedef typename wtraits::unrestricted_wrapped_type wrapped_type;
+
+ typedef W value_type;
+ typedef wrapped_type query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ typedef value_traits<wrapped_type, ID> vtraits;
+
+ static void
+ set_value (W& v, const image_type& i, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+
+ // String, BYTEA, and NUMERIC.
+ //
+ static void
+ set_value (W& v, const details::buffer& b, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::buffer& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // BIT.
+ //
+ static void
+ set_value (W& v, const unsigned char* i, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, n, is_null);
+ }
+
+ static void
+ set_image (unsigned char* i,
+ std::size_t c,
+ std::size_t& n,
+ bool& is_null,
+ const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, c, n, is_null, wtraits::get_ref (v));
+ }
+
+ // VARBIT.
+ //
+ static void
+ set_value (W& v, const details::ubuffer& b, std::size_t n, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), b, n, is_null);
+ }
+
+ static void
+ set_image (details::ubuffer& b, std::size_t& n, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (b, n, is_null, wtraits::get_ref (v));
+ }
+
+ // UUID.
+ //
+ static void
+ set_value (W& v, const unsigned char* i, bool is_null)
+ {
+ if (is_null)
+ wtraits::set_null (v);
+ else
+ vtraits::set_value (wtraits::set_ref (v), i, is_null);
+ }
+
+ static void
+ set_image (unsigned char* i, bool& is_null, const W& v)
+ {
+ is_null = wtraits::get_null (v);
+
+ if (!is_null)
+ vtraits::set_image (i, is_null, wtraits::get_ref (v));
+ }
+ };
+
+ template <typename T, database_type_id ID>
+ struct default_value_traits
+ {
+ typedef T value_type;
+ typedef T query_type;
+ typedef typename image_traits<ID>::image_type image_type;
+
+ static void
+ set_value (T& v, const image_type& i, bool is_null)
+ {
+ if (!is_null)
+ v = T (details::endian_traits::ntoh (i));
+ else
+ v = T ();
+ }
+
+ static void
+ set_image (image_type& i, bool& is_null, T v)
+ {
+ is_null = false;
+ i = details::endian_traits::hton (image_type (v));
+ }
+ };
+
+ // std::string specialization.
+ //
+ template <>
+ struct LIBODB_PGSQL_EXPORT default_value_traits<std::string, id_string>
+ {
+ typedef std::string value_type;
+ typedef std::string query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (std::string& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ v.assign (b.data (), n);
+ else
+ v.erase ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const std::string&);
+ };
+
+ // char*/const char* specialization
+ //
+ // Specialization for const char* which only supports initialization
+ // of an image from the value but not the other way around. This way
+ // we can pass such values to the queries.
+ //
+ class LIBODB_PGSQL_EXPORT c_string_value_traits
+ {
+ public:
+ typedef const char* value_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const char*);
+ };
+
+ template <>
+ struct default_value_traits<char*, id_string>: c_string_value_traits {};
+
+ template <>
+ struct default_value_traits<const char*, id_string>:
+ c_string_value_traits {};
+
+ // char[N] specializations.
+ //
+ struct LIBODB_PGSQL_EXPORT c_array_value_traits_base
+ {
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null,
+ std::size_t N);
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v,
+ std::size_t N);
+ };
+
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_string>
+ {
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (v, b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, v, N);
+ }
+ };
+
+ // std::array<char, N> (string) specialization.
+ //
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_string>
+ {
+ typedef std::array<char, N> value_type;
+ typedef std::array<char, N> query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (v.data (), b, n, is_null, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, v.data (), N);
+ }
+ };
+#endif
+
+ // char specialization.
+ //
+ template <>
+ struct default_value_traits<char, id_string>
+ {
+ typedef char value_type;
+ typedef char query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ c_array_value_traits_base::set_value (&v, b, n, is_null, 1);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ char v)
+ {
+ c_array_value_traits_base::set_image (b, n, is_null, &v, 1);
+ }
+ };
+
+ // std::vector<char> (buffer) specialization.
+ //
+ template <>
+ struct LIBODB_PGSQL_EXPORT default_value_traits<
+ std::vector<char>, id_bytea>
+ {
+ public:
+ typedef std::vector<char> value_type;
+ typedef std::vector<char> query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ v.assign (b.data (), b.data () + n);
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const value_type&);
+ };
+
+ // std::vector<unsigned char> (buffer) specialization.
+ //
+ template <>
+ struct LIBODB_PGSQL_EXPORT default_value_traits<
+ std::vector<unsigned char>, id_bytea>
+ {
+ public:
+ typedef std::vector<unsigned char> value_type;
+ typedef std::vector<unsigned char> query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ {
+ const unsigned char* d (
+ reinterpret_cast<const unsigned char*> (b.data ()));
+ v.assign (d, d + n);
+ }
+ else
+ v.clear ();
+ }
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const value_type&);
+ };
+
+ // char[N] (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<char[N], id_bytea>
+ {
+ public:
+ typedef char* value_type;
+ typedef char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b.data (), (n < N ? n : N));
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const char* v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v, n);
+ }
+ };
+
+ // unsigned char[N] (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<unsigned char[N], id_bytea>
+ {
+ public:
+ typedef unsigned char* value_type;
+ typedef unsigned char query_type[N];
+ typedef details::buffer image_type;
+
+ static void
+ set_value (unsigned char* const& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, b.data (), (n < N ? n : N));
+ else
+ std::memset (v, 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const unsigned char* v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v, n);
+ }
+ };
+
+#ifdef ODB_CXX11
+ // std::array<char, N> (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<char, N>, id_bytea>
+ {
+ public:
+ typedef std::array<char, N> value_type;
+ typedef value_type query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b.data (), (n < N ? n : N));
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data (), n);
+ }
+ };
+
+ // std::array<unsigned char, N> (buffer) specialization.
+ //
+ template <std::size_t N>
+ struct default_value_traits<std::array<unsigned char, N>, id_bytea>
+ {
+ public:
+ typedef std::array<unsigned char, N> value_type;
+ typedef value_type query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type& v,
+ const details::buffer& b,
+ std::size_t n,
+ bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v.data (), b.data (), (n < N ? n : N));
+ else
+ std::memset (v.data (), 0, N);
+ }
+
+ static void
+ set_image (details::buffer& b,
+ std::size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = false;
+ n = N;
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ std::memcpy (b.data (), v.data (), n);
+ }
+ };
+#endif
+
+ // char[16] specialization for uuid.
+ //
+ template <>
+ struct default_value_traits<char[16], id_uuid>
+ {
+ typedef char* value_type;
+ typedef char query_type[16];
+ typedef unsigned char* image_type;
+
+ static void
+ set_value (char* const& v, const unsigned char* i, bool is_null)
+ {
+ if (!is_null)
+ std::memcpy (v, i, 16);
+ else
+ std::memset (v, 0, 16);
+ }
+
+ static void
+ set_image (unsigned char* i, bool& is_null, const char* v)
+ {
+ is_null = false;
+ std::memcpy (i, v, 16);
+ }
+ };
+
+ //
+ // type_traits
+ //
+
+ template <typename T>
+ struct default_type_traits;
+
+ template <typename T>
+ class type_traits: public default_type_traits<T>
+ {
+ };
+
+ // Integral types.
+ //
+ template <>
+ struct default_type_traits<bool>
+ {
+ static const database_type_id db_type_id = id_boolean;
+ };
+
+ template <>
+ struct default_type_traits<signed char>
+ {
+ static const database_type_id db_type_id = id_smallint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned char>
+ {
+ static const database_type_id db_type_id = id_smallint;
+ };
+
+ template <>
+ struct default_type_traits<short>
+ {
+ static const database_type_id db_type_id = id_smallint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned short>
+ {
+ static const database_type_id db_type_id = id_smallint;
+ };
+
+ template <>
+ struct default_type_traits<int>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<unsigned int>
+ {
+ static const database_type_id db_type_id = id_integer;
+ };
+
+ template <>
+ struct default_type_traits<long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ template <>
+ struct default_type_traits<long long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ template <>
+ struct default_type_traits<unsigned long long>
+ {
+ static const database_type_id db_type_id = id_bigint;
+ };
+
+ // Float types.
+ //
+ template <>
+ struct default_type_traits<float>
+ {
+ static const database_type_id db_type_id = id_real;
+ };
+
+ template <>
+ struct default_type_traits<double>
+ {
+ static const database_type_id db_type_id = id_double;
+ };
+
+ // String types.
+ //
+ template <>
+ struct default_type_traits<std::string>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ template <>
+ struct default_type_traits<char*>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ template <>
+ struct default_type_traits<const char*>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ template <std::size_t N>
+ struct default_type_traits<char[N]>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<char, N> >
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+#endif
+
+ template <>
+ struct default_type_traits<char>
+ {
+ static const database_type_id db_type_id = id_string;
+ };
+
+ // Binary types.
+ //
+ template <std::size_t N>
+ struct default_type_traits<unsigned char[N]>
+ {
+ static const database_type_id db_type_id = id_bytea;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<char> >
+ {
+ static const database_type_id db_type_id = id_bytea;
+ };
+
+ template <>
+ struct default_type_traits<std::vector<unsigned char> >
+ {
+ static const database_type_id db_type_id = id_bytea;
+ };
+
+#ifdef ODB_CXX11
+ template <std::size_t N>
+ struct default_type_traits<std::array<unsigned char, N> >
+ {
+ static const database_type_id db_type_id = id_bytea;
+ };
+#endif
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_TRAITS_HXX
diff --git a/libodb-pgsql/odb/pgsql/transaction-impl.cxx b/libodb-pgsql/odb/pgsql/transaction-impl.cxx
new file mode 100644
index 0000000..012fe18
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/transaction-impl.cxx
@@ -0,0 +1,107 @@
+// file : odb/pgsql/transaction-impl.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <libpq-fe.h>
+
+#include <odb/tracer.hxx>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/connection.hxx>
+#include <odb/pgsql/error.hxx>
+#include <odb/pgsql/exceptions.hxx>
+#include <odb/pgsql/transaction-impl.hxx>
+#include <odb/pgsql/auto-handle.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ transaction_impl::
+ transaction_impl (database_type& db)
+ : odb::transaction_impl (db)
+ {
+ }
+
+ transaction_impl::
+ transaction_impl (connection_ptr c)
+ : odb::transaction_impl (c->database (), *c), connection_ (c)
+ {
+ }
+
+ transaction_impl::
+ ~transaction_impl ()
+ {
+ }
+
+ void transaction_impl::
+ start ()
+ {
+ // Grab a connection if we don't already have one.
+ //
+ if (connection_ == 0)
+ {
+ connection_ = static_cast<database_type&> (database_).connection ();
+ odb::transaction_impl::connection_ = connection_.get ();
+ }
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "BEGIN");
+ }
+
+ auto_handle<PGresult> h (PQexec (connection_->handle (), "begin"));
+
+ if (!h || PGRES_COMMAND_OK != PQresultStatus (h))
+ translate_error (*connection_, h);
+ }
+
+ void transaction_impl::
+ commit ()
+ {
+ // Invalidate query results.
+ //
+ connection_->invalidate_results ();
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "COMMIT");
+ }
+
+ auto_handle<PGresult> h (PQexec (connection_->handle (), "commit"));
+
+ if (!h || PGRES_COMMAND_OK != PQresultStatus (h))
+ translate_error (*connection_, h);
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+
+ void transaction_impl::
+ rollback ()
+ {
+ // Invalidate query results.
+ //
+ connection_->invalidate_results ();
+
+ {
+ odb::tracer* t;
+ if ((t = connection_->tracer ()) || (t = database_.tracer ()))
+ t->execute (*connection_, "ROLLBACK");
+ }
+
+ auto_handle<PGresult> h (PQexec (connection_->handle (), "rollback"));
+
+ if (!h || PGRES_COMMAND_OK != PQresultStatus (h))
+ translate_error (*connection_, h);
+
+ // Release the connection.
+ //
+ connection_.reset ();
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/transaction-impl.hxx b/libodb-pgsql/odb/pgsql/transaction-impl.hxx
new file mode 100644
index 0000000..5c93b0e
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/transaction-impl.hxx
@@ -0,0 +1,49 @@
+// file : odb/pgsql/transaction-impl.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_TRANSACTION_IMPL_HXX
+#define ODB_PGSQL_TRANSACTION_IMPL_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class LIBODB_PGSQL_EXPORT transaction_impl: public odb::transaction_impl
+ {
+ public:
+ typedef pgsql::database database_type;
+ typedef pgsql::connection connection_type;
+
+ transaction_impl (database_type&);
+ transaction_impl (connection_ptr);
+
+ virtual
+ ~transaction_impl ();
+
+ virtual void
+ start ();
+
+ virtual void
+ commit ();
+
+ virtual void
+ rollback ();
+
+ private:
+ connection_ptr connection_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_TRANSACTION_IMPL_HXX
diff --git a/libodb-pgsql/odb/pgsql/transaction.cxx b/libodb-pgsql/odb/pgsql/transaction.cxx
new file mode 100644
index 0000000..3b32d80
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/transaction.cxx
@@ -0,0 +1,26 @@
+// file : odb/pgsql/transaction.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/pgsql/transaction.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ transaction& transaction::
+ current ()
+ {
+ // While the impl type can be of the concrete type, the transaction
+ // object can be created as either odb:: or odb::pgsql:: type. To
+ // work around that we are going to hard-cast one to the other
+ // relying on the fact that they have the same representation and
+ // no virtual functions. The former is checked in the tests.
+ //
+ odb::transaction& b (odb::transaction::current ());
+ assert (dynamic_cast<transaction_impl*> (&b.implementation ()) != 0);
+ return reinterpret_cast<transaction&> (b);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/transaction.hxx b/libodb-pgsql/odb/pgsql/transaction.hxx
new file mode 100644
index 0000000..e83c754
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/transaction.hxx
@@ -0,0 +1,88 @@
+// file : odb/pgsql/transaction.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_TRANSACTION_HXX
+#define ODB_PGSQL_TRANSACTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx>
+#include <odb/pgsql/tracer.hxx>
+
+#include <odb/pgsql/details/export.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ class transaction_impl;
+
+ class LIBODB_PGSQL_EXPORT transaction: public odb::transaction
+ {
+ public:
+ typedef pgsql::database database_type;
+ typedef pgsql::connection connection_type;
+
+ explicit
+ transaction (transaction_impl*, bool make_current = true);
+
+ transaction ();
+
+ // Return the database this transaction is on.
+ //
+ database_type&
+ database ();
+
+ // Return the underlying database connection for this transaction.
+ //
+ connection_type&
+ connection ();
+
+ connection_type&
+ connection (odb::database&);
+
+ // Return current transaction or throw if there is no transaction
+ // in effect.
+ //
+ static transaction&
+ current ();
+
+ // Set the current thread's transaction.
+ //
+ static void
+ current (transaction&);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef pgsql::tracer tracer_type;
+
+ void
+ tracer (tracer_type& t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ void
+ tracer (tracer_type* t)
+ {
+ odb::transaction::tracer (t);
+ }
+
+ using odb::transaction::tracer;
+
+ public:
+ transaction_impl&
+ implementation ();
+ };
+ }
+}
+
+#include <odb/pgsql/transaction.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_TRANSACTION_HXX
diff --git a/libodb-pgsql/odb/pgsql/transaction.ixx b/libodb-pgsql/odb/pgsql/transaction.ixx
new file mode 100644
index 0000000..31aa603
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/transaction.ixx
@@ -0,0 +1,57 @@
+// file : odb/pgsql/transaction.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/transaction-impl.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ inline transaction::
+ transaction (transaction_impl* impl, bool make_current)
+ : odb::transaction (impl, make_current)
+ {
+ }
+
+ inline transaction::
+ transaction ()
+ : odb::transaction ()
+ {
+ }
+
+ inline transaction_impl& transaction::
+ implementation ()
+ {
+ // We can use static_cast here since we have an instance of
+ // pgsql::transaction.
+ //
+ return static_cast<transaction_impl&> (
+ odb::transaction::implementation ());
+ }
+
+ inline transaction::database_type& transaction::
+ database ()
+ {
+ return static_cast<database_type&> (odb::transaction::database ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection ()
+ {
+ return static_cast<connection_type&> (odb::transaction::connection ());
+ }
+
+ inline transaction::connection_type& transaction::
+ connection (odb::database& db)
+ {
+ return static_cast<connection_type&> (odb::transaction::connection (db));
+ }
+
+ inline void transaction::
+ current (transaction& t)
+ {
+ odb::transaction::current (t);
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/version-build2-stub.hxx b/libodb-pgsql/odb/pgsql/version-build2-stub.hxx
new file mode 100644
index 0000000..a99b093
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/version-build2-stub.hxx
@@ -0,0 +1,4 @@
+// file : odb/pgsql/version-build2-stub.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/pgsql/version.hxx>
diff --git a/libodb-pgsql/odb/pgsql/version-build2.hxx b/libodb-pgsql/odb/pgsql/version-build2.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/version-build2.hxx
diff --git a/libodb-pgsql/odb/pgsql/version-build2.hxx.in b/libodb-pgsql/odb/pgsql/version-build2.hxx.in
new file mode 100644
index 0000000..ed4396b
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/version-build2.hxx.in
@@ -0,0 +1,42 @@
+// file : odb/pgsql/version-build2.hxx.in
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef LIBODB_PGSQL_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_PGSQL_VERSION $libodb_pgsql.version.project_number$ULL
+#define LIBODB_PGSQL_VERSION_STR "$libodb_pgsql.version.project$"
+#define LIBODB_PGSQL_VERSION_ID "$libodb_pgsql.version.project_id$"
+
+#define LIBODB_PGSQL_VERSION_MAJOR $libodb_pgsql.version.major$
+#define LIBODB_PGSQL_VERSION_MINOR $libodb_pgsql.version.minor$
+#define LIBODB_PGSQL_VERSION_PATCH $libodb_pgsql.version.patch$
+
+#define LIBODB_PGSQL_PRE_RELEASE $libodb_pgsql.version.pre_release$
+
+#define LIBODB_PGSQL_SNAPSHOT $libodb_pgsql.version.snapshot_sn$ULL
+#define LIBODB_PGSQL_SNAPSHOT_ID "$libodb_pgsql.version.snapshot_id$"
+
+#include <odb/version.hxx>
+
+$libodb.check(LIBODB_VERSION, LIBODB_SNAPSHOT)$
+
+#endif // LIBODB_PGSQL_VERSION
diff --git a/libodb-pgsql/odb/pgsql/version.hxx b/libodb-pgsql/odb/pgsql/version.hxx
new file mode 100644
index 0000000..e050de4
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/version.hxx
@@ -0,0 +1,48 @@
+// file : odb/pgsql/version.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifdef LIBODB_PGSQL_BUILD2
+# include <odb/pgsql/version-build2.hxx>
+#else
+
+#ifndef ODB_PGSQL_VERSION_HXX
+#define ODB_PGSQL_VERSION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/pgsql/details/config.hxx>
+#include <odb/version.hxx>
+
+// Version format is AABBCCDD where
+//
+// AA - major version number
+// BB - minor version number
+// CC - bugfix version number
+// DD - alpha / beta (DD + 50) version number
+//
+// When DD is not 00, 1 is subtracted from AABBCC. For example:
+//
+// Version AABBCCDD
+// 2.0.0 02000000
+// 2.1.0 02010000
+// 2.1.1 02010100
+// 2.2.0.a1 02019901
+// 3.0.0.b2 02999952
+//
+
+// Check that we have compatible ODB version.
+//
+#if ODB_VERSION != 20476
+# error incompatible odb interface version detected
+#endif
+
+// libodb-pgsql version: odb interface version plus the bugfix
+// version.
+//
+#define LIBODB_PGSQL_VERSION 2049976
+#define LIBODB_PGSQL_VERSION_STR "2.5.0-b.26"
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_VERSION_HXX
+#endif // LIBODB_PGSQL_BUILD2
diff --git a/libodb-pgsql/odb/pgsql/view-result.hxx b/libodb-pgsql/odb/pgsql/view-result.hxx
new file mode 100644
index 0000000..f9acace
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/view-result.hxx
@@ -0,0 +1,77 @@
+// file : odb/pgsql/view-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_VIEW_RESULT_HXX
+#define ODB_PGSQL_VIEW_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/schema-version.hxx>
+#include <odb/view-result.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/forward.hxx> // query_base, view_statements
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/traits-calls.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ class view_result_impl: public odb::view_result_impl<T>
+ {
+ public:
+ typedef odb::view_result_impl<T> base_type;
+
+ typedef typename base_type::view_type view_type;
+ typedef typename base_type::pointer_type pointer_type;
+
+ typedef view_traits_impl<view_type, id_pgsql> view_traits;
+ typedef typename base_type::pointer_traits pointer_traits;
+
+ typedef view_statements<view_type> statements_type;
+
+ virtual
+ ~view_result_impl ();
+
+ view_result_impl (const query_base&,
+ details::shared_ptr<select_statement>,
+ statements_type&,
+ const schema_version_migration*);
+
+ virtual void
+ load (view_type&);
+
+ virtual void
+ next ();
+
+ virtual void
+ cache ();
+
+ virtual std::size_t
+ size ();
+
+ virtual void
+ invalidate ();
+
+ using base_type::current;
+
+ private:
+ details::shared_ptr<select_statement> statement_;
+ statements_type& statements_;
+ view_traits_calls<view_type> tc_;
+ std::size_t count_;
+ };
+ }
+}
+
+#include <odb/pgsql/view-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_VIEW_RESULT_HXX
diff --git a/libodb-pgsql/odb/pgsql/view-result.txx b/libodb-pgsql/odb/pgsql/view-result.txx
new file mode 100644
index 0000000..980811a
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/view-result.txx
@@ -0,0 +1,114 @@
+// file : odb/pgsql/view-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/callback.hxx>
+
+#include <odb/pgsql/view-statements.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ view_result_impl<T>::
+ ~view_result_impl ()
+ {
+ if (!this->end_)
+ statement_->free_result ();
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ invalidate ()
+ {
+ if (!this->end_)
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+
+ statement_.reset ();
+ }
+
+ template <typename T>
+ view_result_impl<T>::
+ view_result_impl (const query_base&,
+ details::shared_ptr<select_statement> statement,
+ statements_type& statements,
+ const schema_version_migration* svm)
+ : base_type (statements.connection ()),
+ statement_ (statement),
+ statements_ (statements),
+ tc_ (svm),
+ count_ (0)
+ {
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ load (view_type& view)
+ {
+ // The image can grow between calls to load() as a result of other
+ // statements execution.
+ //
+ typename view_traits::image_type& im (statements_.image ());
+
+ if (im.version != statements_.image_version ())
+ {
+ binding& b (statements_.image_binding ());
+ tc_.bind (b.bind, im);
+ statements_.image_version (im.version);
+ b.version++;
+ }
+
+ select_statement::result r (statement_->load ());
+
+ if (r == select_statement::truncated)
+ {
+ if (tc_.grow (im, statements_.image_truncated ()))
+ im.version++;
+
+ if (im.version != statements_.image_version ())
+ {
+ binding& b (statements_.image_binding ());
+ tc_.bind (b.bind, im);
+ statements_.image_version (im.version);
+ b.version++;
+ statement_->reload ();
+ }
+ }
+
+ view_traits::callback (this->db_, view, callback_event::pre_load);
+ tc_.init (view, im, &this->db_);
+ view_traits::callback (this->db_, view, callback_event::post_load);
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ next ()
+ {
+ this->current (pointer_type ());
+
+ if (statement_->next ())
+ count_++;
+ else
+ {
+ statement_->free_result ();
+ this->end_ = true;
+ }
+ }
+
+ template <typename T>
+ void view_result_impl<T>::
+ cache ()
+ {
+ }
+
+ template <typename T>
+ std::size_t view_result_impl<T>::
+ size ()
+ {
+ return this->end_ ? count_ : statement_->result_size ();
+ }
+ }
+}
diff --git a/libodb-pgsql/odb/pgsql/view-statements.hxx b/libodb-pgsql/odb/pgsql/view-statements.hxx
new file mode 100644
index 0000000..970e610
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/view-statements.hxx
@@ -0,0 +1,88 @@
+// file : odb/pgsql/view-statements.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PGSQL_VIEW_STATEMENTS_HXX
+#define ODB_PGSQL_VIEW_STATEMENTS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+#include <odb/pgsql/version.hxx>
+#include <odb/pgsql/binding.hxx>
+#include <odb/pgsql/statement.hxx>
+#include <odb/pgsql/statements-base.hxx>
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ class view_statements: public statements_base
+ {
+ public:
+ typedef T view_type;
+ typedef view_traits_impl<view_type, id_pgsql> view_traits;
+ typedef typename view_traits::pointer_type pointer_type;
+ typedef typename view_traits::image_type image_type;
+
+ public:
+ view_statements (connection_type&);
+
+ virtual
+ ~view_statements ();
+
+ // View image.
+ //
+ image_type&
+ image ()
+ {
+ return image_;
+ }
+
+ std::size_t
+ image_version () const
+ {
+ return image_version_;
+ }
+
+ void
+ image_version (std::size_t v)
+ {
+ image_version_ = v;
+ }
+
+ binding&
+ image_binding ()
+ {
+ return image_binding_;
+ }
+
+ bool*
+ image_truncated ()
+ {
+ return image_truncated_;
+ }
+
+ private:
+ view_statements (const view_statements&);
+ view_statements& operator= (const view_statements&);
+
+ private:
+ image_type image_;
+ std::size_t image_version_;
+ binding image_binding_;
+ bind image_bind_[view_traits::column_count];
+ bool image_truncated_[view_traits::column_count];
+ };
+ }
+}
+
+#include <odb/pgsql/view-statements.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_PGSQL_VIEW_STATEMENTS_HXX
diff --git a/libodb-pgsql/odb/pgsql/view-statements.txx b/libodb-pgsql/odb/pgsql/view-statements.txx
new file mode 100644
index 0000000..afa2a49
--- /dev/null
+++ b/libodb-pgsql/odb/pgsql/view-statements.txx
@@ -0,0 +1,33 @@
+// file : odb/pgsql/view-statements.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cstring> // std::memset
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <typename T>
+ view_statements<T>::
+ ~view_statements ()
+ {
+ }
+
+ template <typename T>
+ view_statements<T>::
+ view_statements (connection_type& conn)
+ : statements_base (conn),
+ image_binding_ (image_bind_, view_traits::column_count)
+ {
+ image_.version = 0;
+ image_version_ = 0;
+
+ std::memset (image_bind_, 0, sizeof (image_bind_));
+ std::memset (image_truncated_, 0, sizeof (image_truncated_));
+
+ for (std::size_t i (0); i < view_traits::column_count; ++i)
+ image_bind_[i].truncated = image_truncated_ + i;
+ }
+ }
+}
diff --git a/libodb-pgsql/tests/.gitignore b/libodb-pgsql/tests/.gitignore
new file mode 100644
index 0000000..e54525b
--- /dev/null
+++ b/libodb-pgsql/tests/.gitignore
@@ -0,0 +1 @@
+driver
diff --git a/libodb-pgsql/tests/basics/buildfile b/libodb-pgsql/tests/basics/buildfile
new file mode 100644
index 0000000..fccb243
--- /dev/null
+++ b/libodb-pgsql/tests/basics/buildfile
@@ -0,0 +1,6 @@
+# file : tests/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libs = libodb-pgsql%lib{odb-pgsql}
+
+exe{driver}: {hxx cxx}{*} $libs
diff --git a/libodb-pgsql/tests/basics/driver.cxx b/libodb-pgsql/tests/basics/driver.cxx
new file mode 100644
index 0000000..efd1985
--- /dev/null
+++ b/libodb-pgsql/tests/basics/driver.cxx
@@ -0,0 +1,37 @@
+// file : tests/basics/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Basic test to make sure the library is usable. Functionality testing
+// is done in the odb-tests package.
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/exceptions.hxx>
+#include <odb/pgsql/transaction.hxx>
+
+using namespace odb::pgsql;
+
+int
+main ()
+{
+ {
+ std::ostringstream os;
+ database::print_usage (os);
+ assert (!os.str ().empty ());
+ }
+
+ // We can't really do much here since that would require a database. We can
+ // create a fake database object as long as we don't expect to get a valid
+ // connection.
+ //
+ database db ("john", "secret", "dummy whammy");
+
+ try
+ {
+ transaction t (db.begin ());
+ assert (false);
+ }
+ catch (const database_exception&) {}
+}
diff --git a/libodb-pgsql/tests/build/.gitignore b/libodb-pgsql/tests/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb-pgsql/tests/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb-pgsql/tests/build/bootstrap.build b/libodb-pgsql/tests/build/bootstrap.build
new file mode 100644
index 0000000..6ee38db
--- /dev/null
+++ b/libodb-pgsql/tests/build/bootstrap.build
@@ -0,0 +1,8 @@
+# file : tests/build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = # Unnamed subproject.
+
+using config
+using dist
+using test
diff --git a/libodb-pgsql/tests/build/root.build b/libodb-pgsql/tests/build/root.build
new file mode 100644
index 0000000..6c5a90b
--- /dev/null
+++ b/libodb-pgsql/tests/build/root.build
@@ -0,0 +1,23 @@
+# file : tests/build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+cxx{*}: extension = cxx
+
+if ($cxx.target.system == 'win32-msvc')
+ cxx.poptions += -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS
+
+if ($cxx.class == 'msvc')
+ cxx.coptions += /wd4251 /wd4275 /wd4800
+
+# Every exe{} in this subproject is by default a test.
+#
+exe{*}: test = true
+
+# Specify the test target for cross-testing.
+#
+test.target = $cxx.target
diff --git a/libodb-pgsql/tests/buildfile b/libodb-pgsql/tests/buildfile
new file mode 100644
index 0000000..57588a4
--- /dev/null
+++ b/libodb-pgsql/tests/buildfile
@@ -0,0 +1,4 @@
+# file : tests/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/}