summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2024-01-24 17:03:38 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2024-01-24 17:03:38 +0300
commit0f1f6841ce5a50d5b315c24d796a2d8e2627d136 (patch)
treee16bbf9e23ca75a88b8af032c4e3ed299ca8db66
parent823026b58211a4166de06ac243d978dcb9930271 (diff)
parent26e36b3a9d7b49d46ecfa69b447482251acba8ac (diff)
Merge branch 'libodb' into multi-package
-rw-r--r--libodb/.gitignore25
-rw-r--r--libodb/GPLv2340
-rw-r--r--libodb/INSTALL6
-rw-r--r--libodb/LICENSE20
-rw-r--r--libodb/README20
-rw-r--r--libodb/build/.gitignore3
-rw-r--r--libodb/build/bootstrap.build10
-rw-r--r--libodb/build/export.build9
-rw-r--r--libodb/build/root.build17
-rw-r--r--libodb/buildfile9
-rw-r--r--libodb/manifest19
-rw-r--r--libodb/odb/buildfile81
-rw-r--r--libodb/odb/c-array-traits.hxx103
-rw-r--r--libodb/odb/cache-traits.hxx182
-rw-r--r--libodb/odb/callback.hxx42
-rw-r--r--libodb/odb/connection.cxx125
-rw-r--r--libodb/odb/connection.hxx228
-rw-r--r--libodb/odb/connection.ixx131
-rw-r--r--libodb/odb/connection.txx44
-rw-r--r--libodb/odb/container-traits.hxx219
-rw-r--r--libodb/odb/core.hxx20
-rw-r--r--libodb/odb/database.cxx83
-rw-r--r--libodb/odb/database.hxx657
-rw-r--r--libodb/odb/database.ixx879
-rw-r--r--libodb/odb/database.txx491
-rw-r--r--libodb/odb/details/buffer.cxx35
-rw-r--r--libodb/odb/details/buffer.hxx92
-rw-r--r--libodb/odb/details/build2/config-stub.h5
-rw-r--r--libodb/odb/details/build2/config-vc-stub.h5
-rw-r--r--libodb/odb/details/build2/config-vc.h23
-rw-r--r--libodb/odb/details/build2/config.h19
-rw-r--r--libodb/odb/details/c-string.hxx28
-rw-r--r--libodb/odb/details/condition.cxx13
-rw-r--r--libodb/odb/details/condition.hxx68
-rw-r--r--libodb/odb/details/config-vc.h13
-rw-r--r--libodb/odb/details/config.h.in18
-rw-r--r--libodb/odb/details/config.hxx80
-rw-r--r--libodb/odb/details/exception.hxx21
-rw-r--r--libodb/odb/details/export.hxx78
-rw-r--r--libodb/odb/details/function-wrapper.hxx90
-rw-r--r--libodb/odb/details/function-wrapper.ixx49
-rw-r--r--libodb/odb/details/function-wrapper.txx89
-rw-r--r--libodb/odb/details/lock.cxx13
-rw-r--r--libodb/odb/details/lock.hxx59
-rw-r--r--libodb/odb/details/meta/answer.hxx30
-rw-r--r--libodb/odb/details/meta/class-p.hxx34
-rw-r--r--libodb/odb/details/meta/polymorphic-p.hxx57
-rw-r--r--libodb/odb/details/meta/remove-const-volatile.hxx31
-rw-r--r--libodb/odb/details/meta/remove-const.hxx32
-rw-r--r--libodb/odb/details/meta/remove-pointer.hxx32
-rw-r--r--libodb/odb/details/meta/remove-volatile.hxx32
-rw-r--r--libodb/odb/details/meta/static-assert.hxx32
-rw-r--r--libodb/odb/details/mutex.cxx13
-rw-r--r--libodb/odb/details/mutex.hxx53
-rw-r--r--libodb/odb/details/posix/condition.hxx47
-rw-r--r--libodb/odb/details/posix/condition.ixx38
-rw-r--r--libodb/odb/details/posix/exceptions.cxx22
-rw-r--r--libodb/odb/details/posix/exceptions.hxx38
-rw-r--r--libodb/odb/details/posix/mutex.hxx44
-rw-r--r--libodb/odb/details/posix/mutex.ixx37
-rw-r--r--libodb/odb/details/posix/thread.cxx44
-rw-r--r--libodb/odb/details/posix/thread.hxx41
-rw-r--r--libodb/odb/details/posix/thread.ixx29
-rw-r--r--libodb/odb/details/posix/tls.hxx106
-rw-r--r--libodb/odb/details/posix/tls.ixx20
-rw-r--r--libodb/odb/details/posix/tls.txx121
-rw-r--r--libodb/odb/details/shared-ptr-fwd.hxx24
-rw-r--r--libodb/odb/details/shared-ptr.hxx167
-rw-r--r--libodb/odb/details/shared-ptr/base.cxx83
-rw-r--r--libodb/odb/details/shared-ptr/base.hxx131
-rw-r--r--libodb/odb/details/shared-ptr/base.ixx119
-rw-r--r--libodb/odb/details/shared-ptr/base.txx198
-rw-r--r--libodb/odb/details/shared-ptr/counter-type.hxx23
-rw-r--r--libodb/odb/details/shared-ptr/exception.hxx31
-rw-r--r--libodb/odb/details/thread.cxx22
-rw-r--r--libodb/odb/details/thread.hxx65
-rw-r--r--libodb/odb/details/tls.hxx168
-rw-r--r--libodb/odb/details/transfer-ptr.hxx73
-rw-r--r--libodb/odb/details/type-info.hxx36
-rw-r--r--libodb/odb/details/unique-ptr.hxx95
-rw-r--r--libodb/odb/details/unused.hxx21
-rw-r--r--libodb/odb/details/win32/condition.cxx54
-rw-r--r--libodb/odb/details/win32/condition.hxx52
-rw-r--r--libodb/odb/details/win32/condition.ixx30
-rw-r--r--libodb/odb/details/win32/dll.cxx51
-rw-r--r--libodb/odb/details/win32/exceptions.cxx22
-rw-r--r--libodb/odb/details/win32/exceptions.hxx40
-rw-r--r--libodb/odb/details/win32/init.cxx41
-rw-r--r--libodb/odb/details/win32/init.hxx36
-rw-r--r--libodb/odb/details/win32/lock.hxx49
-rw-r--r--libodb/odb/details/win32/mutex.hxx43
-rw-r--r--libodb/odb/details/win32/mutex.ixx32
-rw-r--r--libodb/odb/details/win32/once-init.hxx23
-rw-r--r--libodb/odb/details/win32/once.cxx26
-rw-r--r--libodb/odb/details/win32/once.hxx50
-rw-r--r--libodb/odb/details/win32/once.ixx42
-rw-r--r--libodb/odb/details/win32/thread.cxx88
-rw-r--r--libodb/odb/details/win32/thread.hxx59
-rw-r--r--libodb/odb/details/win32/tls-init.hxx26
-rw-r--r--libodb/odb/details/win32/tls.cxx245
-rw-r--r--libodb/odb/details/win32/tls.hxx120
-rw-r--r--libodb/odb/details/win32/tls.ixx20
-rw-r--r--libodb/odb/details/win32/tls.txx94
-rw-r--r--libodb/odb/details/win32/windows.hxx33
-rw-r--r--libodb/odb/details/wrapper-p.hxx38
-rw-r--r--libodb/odb/exception.hxx36
-rw-r--r--libodb/odb/exceptions.cxx430
-rw-r--r--libodb/odb/exceptions.hxx523
-rw-r--r--libodb/odb/forward.hxx178
-rw-r--r--libodb/odb/function-table.hxx50
-rw-r--r--libodb/odb/lazy-pointer-traits.hxx141
-rw-r--r--libodb/odb/lazy-ptr-impl.hxx188
-rw-r--r--libodb/odb/lazy-ptr-impl.ixx397
-rw-r--r--libodb/odb/lazy-ptr-impl.txx60
-rw-r--r--libodb/odb/lazy-ptr.hxx681
-rw-r--r--libodb/odb/lazy-ptr.ixx1681
-rw-r--r--libodb/odb/lazy-ptr.txx114
-rw-r--r--libodb/odb/nested-container.hxx218
-rw-r--r--libodb/odb/no-id-object-result.hxx182
-rw-r--r--libodb/odb/no-id-object-result.txx21
-rw-r--r--libodb/odb/no-op-cache-traits.hxx236
-rw-r--r--libodb/odb/nullable.hxx229
-rw-r--r--libodb/odb/object-result.hxx164
-rw-r--r--libodb/odb/pointer-traits.hxx405
-rw-r--r--libodb/odb/polymorphic-info.hxx188
-rw-r--r--libodb/odb/polymorphic-map.hxx276
-rw-r--r--libodb/odb/polymorphic-map.ixx19
-rw-r--r--libodb/odb/polymorphic-map.txx75
-rw-r--r--libodb/odb/polymorphic-object-result.hxx224
-rw-r--r--libodb/odb/polymorphic-object-result.txx70
-rw-r--r--libodb/odb/post.hxx6
-rw-r--r--libodb/odb/pre.hxx23
-rw-r--r--libodb/odb/prepared-query.cxx47
-rw-r--r--libodb/odb/prepared-query.hxx201
-rw-r--r--libodb/odb/query-dynamic.cxx200
-rw-r--r--libodb/odb/query-dynamic.hxx1067
-rw-r--r--libodb/odb/query-dynamic.ixx18
-rw-r--r--libodb/odb/query-dynamic.txx139
-rw-r--r--libodb/odb/query.hxx119
-rw-r--r--libodb/odb/result.cxx40
-rw-r--r--libodb/odb/result.hxx247
-rw-r--r--libodb/odb/result.txx49
-rw-r--r--libodb/odb/schema-catalog-impl.hxx54
-rw-r--r--libodb/odb/schema-catalog.cxx387
-rw-r--r--libodb/odb/schema-catalog.hxx392
-rw-r--r--libodb/odb/schema-version.hxx71
-rw-r--r--libodb/odb/section.cxx27
-rw-r--r--libodb/odb/section.hxx122
-rw-r--r--libodb/odb/session.cxx66
-rw-r--r--libodb/odb/session.hxx217
-rw-r--r--libodb/odb/session.ixx60
-rw-r--r--libodb/odb/session.txx90
-rw-r--r--libodb/odb/simple-object-result.hxx201
-rw-r--r--libodb/odb/simple-object-result.txx58
-rw-r--r--libodb/odb/statement-processing-common.hxx214
-rw-r--r--libodb/odb/statement-processing.cxx685
-rw-r--r--libodb/odb/statement.cxx12
-rw-r--r--libodb/odb/statement.hxx108
-rw-r--r--libodb/odb/std-array-traits.hxx72
-rw-r--r--libodb/odb/std-deque-traits.hxx69
-rw-r--r--libodb/odb/std-forward-list-traits.hxx73
-rw-r--r--libodb/odb/std-list-traits.hxx73
-rw-r--r--libodb/odb/std-map-traits.hxx142
-rw-r--r--libodb/odb/std-set-traits.hxx134
-rw-r--r--libodb/odb/std-unordered-map-traits.hxx133
-rw-r--r--libodb/odb/std-unordered-set-traits.hxx125
-rw-r--r--libodb/odb/std-vector-traits.hxx123
-rw-r--r--libodb/odb/tr1/lazy-pointer-traits.hxx61
-rw-r--r--libodb/odb/tr1/lazy-ptr.hxx267
-rw-r--r--libodb/odb/tr1/lazy-ptr.ixx638
-rw-r--r--libodb/odb/tr1/lazy-ptr.txx43
-rw-r--r--libodb/odb/tr1/memory.hxx37
-rw-r--r--libodb/odb/tr1/pointer-traits.hxx121
-rw-r--r--libodb/odb/tr1/wrapper-traits.hxx76
-rw-r--r--libodb/odb/tracer.cxx95
-rw-r--r--libodb/odb/tracer.hxx36
-rw-r--r--libodb/odb/traits.hxx317
-rw-r--r--libodb/odb/transaction.cxx356
-rw-r--r--libodb/odb/transaction.hxx278
-rw-r--r--libodb/odb/transaction.ixx68
-rw-r--r--libodb/odb/vector-impl.cxx208
-rw-r--r--libodb/odb/vector-impl.hxx221
-rw-r--r--libodb/odb/vector-impl.ixx210
-rw-r--r--libodb/odb/vector-traits.hxx106
-rw-r--r--libodb/odb/vector-traits.txx100
-rw-r--r--libodb/odb/vector.hxx635
-rw-r--r--libodb/odb/vector.ixx359
-rw-r--r--libodb/odb/version-build2-stub.hxx4
-rw-r--r--libodb/odb/version-build2.hxx0
-rw-r--r--libodb/odb/version-build2.hxx.in42
-rw-r--r--libodb/odb/version.hxx43
-rw-r--r--libodb/odb/view-image.hxx36
-rw-r--r--libodb/odb/view-result.hxx231
-rw-r--r--libodb/odb/view-result.txx39
-rw-r--r--libodb/odb/wrapper-traits.hxx276
-rw-r--r--libodb/tests/.gitignore1
-rw-r--r--libodb/tests/basics/buildfile6
-rw-r--r--libodb/tests/basics/driver.cxx29
-rw-r--r--libodb/tests/build/.gitignore3
-rw-r--r--libodb/tests/build/bootstrap.build8
-rw-r--r--libodb/tests/build/root.build23
-rw-r--r--libodb/tests/buildfile4
202 files changed, 25691 insertions, 0 deletions
diff --git a/libodb/.gitignore b/libodb/.gitignore
new file mode 100644
index 0000000..1c363a0
--- /dev/null
+++ b/libodb/.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/GPLv2 b/libodb/GPLv2
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/libodb/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/INSTALL b/libodb/INSTALL
new file mode 100644
index 0000000..46b79ad
--- /dev/null
+++ b/libodb/INSTALL
@@ -0,0 +1,6 @@
+The easiest way to build this package is with the bpkg package manager:
+
+$ bpkg build libodb
+
+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/LICENSE b/libodb/LICENSE
new file mode 100644
index 0000000..9d92da1
--- /dev/null
+++ b/libodb/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/README b/libodb/README
new file mode 100644
index 0000000..902e20b
--- /dev/null
+++ b/libodb/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 common ODB runtime library. Every application
+that includes code generated by the ODB compiler 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/build/.gitignore b/libodb/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb/build/bootstrap.build b/libodb/build/bootstrap.build
new file mode 100644
index 0000000..9c8d1a9
--- /dev/null
+++ b/libodb/build/bootstrap.build
@@ -0,0 +1,10 @@
+# file : build/bootstrap.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+project = libodb
+
+using version
+using config
+using dist
+using test
+using install
diff --git a/libodb/build/export.build b/libodb/build/export.build
new file mode 100644
index 0000000..56312c8
--- /dev/null
+++ b/libodb/build/export.build
@@ -0,0 +1,9 @@
+# file : build/export.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+$out_root/
+{
+ include odb/
+}
+
+export $out_root/odb/lib{odb}
diff --git a/libodb/build/root.build b/libodb/build/root.build
new file mode 100644
index 0000000..882047d
--- /dev/null
+++ b/libodb/build/root.build
@@ -0,0 +1,17 @@
+# file : build/root.build
+# license : GNU GPL v2; see accompanying LICENSE file
+
+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/buildfile b/libodb/buildfile
new file mode 100644
index 0000000..a04e206
--- /dev/null
+++ b/libodb/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/manifest b/libodb/manifest
new file mode 100644
index 0000000..d728912
--- /dev/null
+++ b/libodb/manifest
@@ -0,0 +1,19 @@
+: 1
+name: libodb
+version: 2.5.0-b.26.z
+project: odb
+summary: Common ODB runtime library
+license: GPL-2.0-only
+license: other: proprietary ; Not free/open source.
+topics: C++, ORM, SQL, object persistence, relational database
+description-file: README
+changes-file: NEWS
+url: https://www.codesynthesis.com/products/odb/
+doc-url: https://www.codesynthesis.com/products/odb/doc/manual.xhtml
+src-url: https://git.codesynthesis.com/cgit/odb/libodb/
+email: odb-users@codesynthesis.com
+build-warning-email: odb-builds@codesynthesis.com
+builds: all
+requires: c++11
+depends: * build2 >= 0.16.0-
+depends: * bpkg >= 0.16.0-
diff --git a/libodb/odb/buildfile b/libodb/odb/buildfile
new file mode 100644
index 0000000..ec95e94
--- /dev/null
+++ b/libodb/odb/buildfile
@@ -0,0 +1,81 @@
+# file : odb/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+lib{odb}: {hxx ixx txx cxx}{* -version-build2} \
+ {hxx}{version-build2} \
+ details/{hxx ixx txx}{*} \
+ details/{cxx}{* -condition -lock -mutex} \
+ details/build2/{h}{*} \
+ details/meta/{hxx}{*} \
+details/shared-ptr/{hxx ixx txx cxx}{*} \
+ details/win32/{hxx}{windows}
+
+# 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_BUILD2
+
+obja{*}: cxx.poptions += -DLIBODB_STATIC_BUILD
+objs{*}: cxx.poptions += -DLIBODB_SHARED_BUILD
+
+# Export options.
+#
+lib{odb}: cxx.export.poptions = "-I$out_root" "-I$src_root" -DLIBODB_BUILD2
+
+liba{odb}: cxx.export.poptions += -DLIBODB_STATIC
+libs{odb}: cxx.export.poptions += -DLIBODB_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}: bin.lib.version = @"-$version.project_id"
+else
+ lib{odb}: bin.lib.version = @"-$version.major.$version.minor"
+
+# Install into the odb/ subdirectory of, say, /usr/include/ recreating
+# subdirectories.
+#
+install_include = [dir_path] include/odb/
+
+{hxx ixx txx}{*}:
+{
+ install = $install_include
+ install.subdirs = true
+}
+
+if ($cxx.target.class != "windows")
+ details/win32/*: install = false
+
+# We want these to be picked up whether LIBODB_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/odb/c-array-traits.hxx b/libodb/odb/c-array-traits.hxx
new file mode 100644
index 0000000..fff7880
--- /dev/null
+++ b/libodb/odb/c-array-traits.hxx
@@ -0,0 +1,103 @@
+// file : odb/c-array-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_C_ARRAY_TRAITS_HXX
+#define ODB_C_ARRAY_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ // Optional mapping of C arrays as containers. Note that this mapping is not
+ // enable by default. To enable, pass the following options to the ODB
+ // compiler:
+ //
+ // --odb-epilogue '#include <odb/c-array-traits.hxx>'
+ // --hxx-prologue '#include <odb/c-array-traits.hxx>'
+ //
+ // Note also that the array types have to be named, for example:
+ //
+ // class object
+ // {
+ // // composite_type values[5]; // Won't work.
+ //
+ // typedef composite_type composite_array[5];
+ // composite_array values;
+ // };
+ //
+ // Finally, this mapping is disabled for the char[N] and wchar_t[N] types
+ // (they are mapped as strings by default).
+ //
+ template <typename V, std::size_t N>
+ class access::container_traits<V[N]>
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef V container_type[N];
+
+ typedef V value_type;
+ typedef std::size_t index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (index_type i (0); i < N; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ index_type i (0);
+
+ for (; more && i < N; ++i)
+ {
+ index_type dummy;
+ more = f.select (dummy, c[i]);
+ }
+
+ assert (!more && i == N);
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (index_type i (0); i < N; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // Disable for char[N] and wchar_t[N].
+ //
+#ifdef ODB_COMPILER
+ template <std::size_t N>
+ class access::container_traits<char[N]>;
+
+#ifdef _WIN32
+ template <std::size_t N>
+ class access::container_traits<wchar_t[N]>;
+#endif
+#endif
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_C_ARRAY_TRAITS_HXX
diff --git a/libodb/odb/cache-traits.hxx b/libodb/odb/cache-traits.hxx
new file mode 100644
index 0000000..a8cf750
--- /dev/null
+++ b/libodb/odb/cache-traits.hxx
@@ -0,0 +1,182 @@
+// file : odb/cache-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_CACHE_TRAITS_HXX
+#define ODB_CACHE_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/traits.hxx>
+#include <odb/forward.hxx>
+#include <odb/session.hxx>
+#include <odb/pointer-traits.hxx>
+#include <odb/no-op-cache-traits.hxx>
+
+namespace odb
+{
+ // pointer_cache_traits
+ //
+ // Caching traits for objects passed by pointer. P should be the canonical
+ // pointer (non-const).
+ //
+ template <typename P, typename S, pointer_kind pk>
+ struct pointer_cache_traits_impl
+ {
+ typedef P pointer_type;
+ typedef S session_type;
+ typedef odb::pointer_traits<pointer_type> pointer_traits;
+ typedef typename pointer_traits::element_type object_type;
+ typedef typename object_traits<object_type>::id_type id_type;
+ typedef typename session_type::template cache_position<object_type>
+ position_type;
+
+ struct insert_guard
+ {
+ insert_guard () {}
+ insert_guard (const position_type& pos): pos_ (pos) {}
+ ~insert_guard () {session_type::_cache_erase (pos_);}
+
+ const position_type&
+ position () const {return pos_;}
+
+ void
+ release () {pos_ = position_type ();}
+
+ // Note: doesn't call erase() on the old position (assumes empty).
+ //
+ void
+ reset (const position_type& pos) {pos_ = pos;}
+
+ private:
+ position_type pos_;
+ };
+
+ // Cache management.
+ //
+ // We need the insert() overload with explicit id to handle self-
+ // references. In such cases the object is not yet loaded and the
+ // id member does not contain the correct id.
+ //
+ // Qualify the database type to resolve a phony ambiguity in VC 10.
+ //
+ static position_type
+ insert (odb::database& db, const id_type& id, const pointer_type& p)
+ {
+ return session_type::template _cache_insert<object_type> (db, id, p);
+ }
+
+ static position_type
+ insert (odb::database& db, const pointer_type& p)
+ {
+ const id_type& id (
+ object_traits<object_type>::id (
+ pointer_traits::get_ref (p)));
+
+ return session_type::template _cache_insert<object_type> (db, id, p);
+ }
+
+ static pointer_type
+ find (odb::database& db, const id_type& id)
+ {
+ return session_type::template _cache_find<object_type> (db, id);
+ }
+
+ static void
+ erase (const position_type& p)
+ {
+ session_type::template _cache_erase<object_type> (p);
+ }
+
+ // Notifications.
+ //
+ static void
+ persist (const position_type& p)
+ {
+ session_type::template _cache_persist<object_type> (p);
+ }
+
+ static void
+ load (const position_type& p)
+ {
+ session_type::template _cache_load<object_type> (p);
+ }
+
+ static void
+ update (odb::database& db, const object_type& obj)
+ {
+ session_type::template _cache_update<object_type> (db, obj);
+ }
+
+ static void
+ erase (odb::database& db, const id_type& id)
+ {
+ session_type::template _cache_erase<object_type> (db, id);
+ }
+ };
+
+ // Unique pointers don't work with the object cache.
+ //
+ template <typename P, typename S>
+ struct pointer_cache_traits_impl<P, S, pk_unique>:
+ no_op_pointer_cache_traits<P> {};
+
+ template <typename P, typename S>
+ struct pointer_cache_traits:
+ pointer_cache_traits_impl<P, S, pointer_traits<P>::kind> {};
+
+ // reference_cache_traits
+ //
+ // Caching traits for objects passed by reference. T should be the
+ // canonical object type (non-const). Only if the object pointer
+ // kind is raw do we add the object to the session.
+ //
+ template <typename T, typename S, pointer_kind pk>
+ struct reference_cache_traits_impl: no_op_reference_cache_traits<T> {};
+
+ template <typename T, typename S>
+ struct reference_cache_traits_impl<T, S, pk_raw>
+ {
+ typedef T object_type;
+ typedef typename object_traits<object_type>::pointer_type pointer_type;
+ typedef typename object_traits<object_type>::id_type id_type;
+
+ typedef pointer_cache_traits<pointer_type, S> pointer_traits;
+ typedef typename pointer_traits::position_type position_type;
+ typedef typename pointer_traits::insert_guard insert_guard;
+
+ static position_type
+ insert (odb::database& db, const id_type& id, object_type& obj)
+ {
+ pointer_type p (&obj);
+ return pointer_traits::insert (db, id, p);
+ }
+
+ static position_type
+ insert (odb::database& db, object_type& obj)
+ {
+ pointer_type p (&obj);
+ return pointer_traits::insert (db, p);
+ }
+
+ static void
+ persist (const position_type& p)
+ {
+ pointer_traits::persist (p);
+ }
+
+ static void
+ load (const position_type& p)
+ {
+ pointer_traits::load (p);
+ }
+ };
+
+ template <typename T, typename S>
+ struct reference_cache_traits:
+ reference_cache_traits_impl<
+ T, S, pointer_traits<typename object_traits<T>::pointer_type>::kind> {};
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_CACHE_TRAITS_HXX
diff --git a/libodb/odb/callback.hxx b/libodb/odb/callback.hxx
new file mode 100644
index 0000000..aaa066f
--- /dev/null
+++ b/libodb/odb/callback.hxx
@@ -0,0 +1,42 @@
+// file : odb/callback.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_CALLBACK_HXX
+#define ODB_CALLBACK_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx> // odb::core
+
+namespace odb
+{
+ struct callback_event
+ {
+ enum value
+ {
+ pre_persist,
+ post_persist,
+ pre_load,
+ post_load,
+ pre_update,
+ post_update,
+ pre_erase,
+ post_erase
+ };
+
+ callback_event (value v): v_ (v) {}
+ operator value () const {return v_;}
+
+ private:
+ value v_;
+ };
+
+ namespace common
+ {
+ using odb::callback_event;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_CALLBACK_HXX
diff --git a/libodb/odb/connection.cxx b/libodb/odb/connection.cxx
new file mode 100644
index 0000000..29743a2
--- /dev/null
+++ b/libodb/odb/connection.cxx
@@ -0,0 +1,125 @@
+// file : odb/connection.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/database.hxx>
+#include <odb/connection.hxx>
+#include <odb/result.hxx>
+#include <odb/prepared-query.hxx>
+#include <odb/exceptions.hxx> // prepared_*
+
+using namespace std;
+
+namespace odb
+{
+ // connection
+ //
+ connection::
+ ~connection ()
+ {
+ assert (prepared_queries_ == 0);
+ assert (prepared_map_.empty ());
+ }
+
+ void connection::
+ clear_prepared_map ()
+ {
+ for (prepared_map_type::iterator i (prepared_map_.begin ()),
+ e (prepared_map_.end ()); i != e; ++i)
+ {
+ if (i->second.params != 0)
+ i->second.params_deleter (i->second.params);
+ }
+
+ prepared_map_.clear ();
+ }
+
+ void connection::
+ recycle ()
+ {
+ while (prepared_queries_ != 0)
+ {
+ prepared_queries_->stmt.reset ();
+ prepared_queries_->list_remove ();
+ }
+ }
+
+ void connection::
+ invalidate_results ()
+ {
+ while (results_ != 0)
+ {
+ results_->invalidate ();
+ results_->list_remove ();
+ }
+ }
+
+ void connection::
+ cache_query_ (prepared_query_impl* pq,
+ const type_info& ti,
+ void* params,
+ const type_info* params_info,
+ void (*params_deleter) (void*))
+ {
+ pair<prepared_map_type::iterator, bool> r (
+ prepared_map_.insert (
+ prepared_map_type::value_type (pq->name, prepared_entry_type ())));
+
+ if (!r.second)
+ throw prepared_already_cached (pq->name);
+
+ prepared_entry_type& e (r.first->second);
+
+ // Mark this prepared query as cached , get its ref count to 1
+ // (prepared_query instances now reference this impl object),
+ // and remove it from the invalidation list.
+ //
+ pq->cached = true;
+
+ while (pq->_ref_count () > 1)
+ pq->_dec_ref ();
+
+ pq->list_remove ();
+
+ e.prep_query.reset (pq);
+ e.type_info = &ti;
+ e.params = params;
+ e.params_info = params_info;
+ e.params_deleter = params_deleter;
+ }
+
+ prepared_query_impl* connection::
+ lookup_query_ (const char* name,
+ const type_info& ti,
+ void** params,
+ const type_info* params_info) const
+ {
+ prepared_map_type::const_iterator i (prepared_map_.find (name));
+
+ if (i == prepared_map_.end ())
+ {
+ // Use a factory, if there is one.
+ //
+ if (factory_.database ().call_query_factory (
+ name, const_cast<connection&> (*this)))
+ i = prepared_map_.find (name);
+ }
+
+ if (i == prepared_map_.end ())
+ return 0;
+
+ // Make sure the types match.
+ //
+ if (*i->second.type_info != ti)
+ throw prepared_type_mismatch (name);
+
+ if (params != 0)
+ {
+ if (*i->second.params_info != *params_info)
+ throw prepared_type_mismatch (name);
+
+ *params = i->second.params;
+ }
+
+ return i->second.prep_query.get ();
+ }
+}
diff --git a/libodb/odb/connection.hxx b/libodb/odb/connection.hxx
new file mode 100644
index 0000000..8ce4544
--- /dev/null
+++ b/libodb/odb/connection.hxx
@@ -0,0 +1,228 @@
+// file : odb/connection.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_CONNECTION_HXX
+#define ODB_CONNECTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <string>
+#include <memory> // std::auto_ptr, std::unique_ptr
+#include <cstddef> // std::size_t
+#include <typeinfo>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+#include <odb/query.hxx>
+#include <odb/prepared-query.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/export.hxx>
+#include <odb/details/c-string.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+namespace odb
+{
+ class transaction_impl;
+ class connection_factory;
+
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+
+ class LIBODB_EXPORT connection: public details::shared_base
+ {
+ public:
+ typedef odb::database database_type;
+
+ database_type&
+ database ();
+
+ // Transactions.
+ //
+ public:
+ virtual transaction_impl*
+ begin () = 0;
+
+ // Native database statement execution. Note that unlike the
+ // versions in the database class, these can be executed
+ // without a transaction.
+ //
+ public:
+ unsigned long long
+ execute (const char* statement);
+
+ unsigned long long
+ execute (const std::string& statement);
+
+ virtual unsigned long long
+ execute (const char* statement, std::size_t length) = 0;
+
+ // 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 query<T>&);
+
+ template <typename T>
+ void
+ cache_query (const prepared_query<T>&);
+
+#ifdef ODB_CXX11
+ template <typename T, typename P>
+ void
+ cache_query (const prepared_query<T>&, std::unique_ptr<P> params);
+#else
+ template <typename T, typename P>
+ void
+ cache_query (const prepared_query<T>&, std::auto_ptr<P> params);
+#endif
+
+ template <typename T>
+ prepared_query<T>
+ lookup_query (const char* name);
+
+ template <typename T, typename P>
+ prepared_query<T>
+ lookup_query (const char* name, P*& params);
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef odb::tracer tracer_type;
+
+ void
+ tracer (tracer_type&);
+
+ void
+ tracer (tracer_type*);
+
+ tracer_type*
+ tracer () const;
+
+ public:
+ // Store the transaction-spacific tracer in the connection. If we
+ // were to store it in the transaction, then in order to check if
+ // it was set, we would need to get the transaction instance using
+ // the current() API. But that requires a TLS lookup, which can be
+ // slow.
+ //
+ tracer_type*
+ transaction_tracer () const;
+
+ public:
+ virtual
+ ~connection ();
+
+ // Recycle the connection to be used by another thread. This call
+ // invalidates uncached prepared queries.
+ //
+ void
+ recycle ();
+
+ protected:
+ connection (connection_factory&);
+
+ template <typename T,
+ database_id DB,
+ class_kind kind = class_traits<T>::kind>
+ struct query_;
+
+ virtual void
+ cache_query_ (prepared_query_impl* pq,
+ const std::type_info& ti,
+ void* params,
+ const std::type_info* params_info,
+ void (*params_deleter) (void*));
+
+ prepared_query_impl*
+ lookup_query_ (const char* name,
+ const std::type_info& ti,
+ void** params, // out
+ const std::type_info* params_info) const;
+
+ template <typename P>
+ static void
+ params_deleter (void*);
+
+ private:
+ connection (const connection&);
+ connection& operator= (const connection&);
+
+ // Prepared query cache.
+ //
+ protected:
+ struct prepared_entry_type
+ {
+ details::shared_ptr<prepared_query_impl> prep_query;
+ const std::type_info* type_info;
+ void* params;
+ const std::type_info* params_info;
+ void (*params_deleter) (void*);
+ };
+
+ typedef
+ std::map<const char*, prepared_entry_type, details::c_string_comparator>
+ prepared_map_type;
+
+ prepared_map_type prepared_map_;
+
+ void
+ clear_prepared_map ();
+
+ protected:
+ connection_factory& factory_;
+ tracer_type* tracer_;
+
+ // Active query result list.
+ //
+ protected:
+ friend class result_impl;
+ result_impl* results_;
+
+ void
+ invalidate_results ();
+
+ // Prepared but uncached query list (cached ones are stored in
+ // prepared_map_).
+ //
+ protected:
+ friend class prepared_query_impl;
+ prepared_query_impl* prepared_queries_;
+
+ // Implementation details.
+ //
+ public:
+ tracer_type* transaction_tracer_;
+ };
+
+ class connection_factory
+ {
+ public:
+ typedef odb::database database_type;
+
+ connection_factory (): db_ (0) {}
+
+ database_type&
+ database () {return *db_;}
+
+ protected:
+ database_type* db_;
+ };
+}
+
+#include <odb/connection.ixx>
+#include <odb/connection.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_CONNECTION_HXX
diff --git a/libodb/odb/connection.ixx b/libodb/odb/connection.ixx
new file mode 100644
index 0000000..d19390a
--- /dev/null
+++ b/libodb/odb/connection.ixx
@@ -0,0 +1,131 @@
+// file : odb/connection.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::string
+#include <cassert>
+
+namespace odb
+{
+ inline connection::
+ connection (connection_factory& f)
+ : factory_ (f),
+ tracer_ (0),
+ results_ (0),
+ prepared_queries_ (0),
+ transaction_tracer_ (0)
+ {
+ }
+
+ inline connection::database_type& connection::
+ database ()
+ {
+ return factory_.database ();
+ }
+
+ inline unsigned long long connection::
+ execute (const char* st)
+ {
+ return execute (st, std::strlen (st));
+ }
+
+ inline unsigned long long connection::
+ execute (const std::string& st)
+ {
+ return execute (st.c_str (), st.size ());
+ }
+
+ 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 query<T>& q)
+ {
+ return query_<T, id_common>::call (*this, n, q);
+ }
+
+ template <typename T>
+ inline void connection::
+ cache_query (const prepared_query<T>& pq)
+ {
+ assert (pq);
+ cache_query_ (pq.impl_, typeid (T), 0, 0, 0);
+ }
+
+#ifdef ODB_CXX11
+ template <typename T, typename P>
+ inline void connection::
+ cache_query (const prepared_query<T>& pq, std::unique_ptr<P> params)
+ {
+ assert (pq);
+ assert (params);
+ cache_query_ (
+ pq.impl_, typeid (T), params.get (), &typeid (P), &params_deleter<P>);
+ params.release ();
+ }
+#else
+ template <typename T, typename P>
+ inline void connection::
+ cache_query (const prepared_query<T>& pq, std::auto_ptr<P> params)
+ {
+ assert (pq);
+ assert (params.get () != 0);
+ cache_query_ (
+ pq.impl_, typeid (T), params.get (), &typeid (P), &params_deleter<P>);
+ params.release ();
+ }
+#endif
+
+ template <typename T>
+ inline prepared_query<T> connection::
+ lookup_query (const char* name)
+ {
+ return prepared_query<T> (lookup_query_ (name, typeid (T), 0, 0));
+ }
+
+ template <typename T, typename P>
+ inline prepared_query<T> connection::
+ lookup_query (const char* name, P*& params)
+ {
+ return prepared_query<T> (
+ lookup_query_ (name,
+ typeid (T),
+ reinterpret_cast<void**> (&params),
+ &typeid (P)));
+ }
+
+ inline void connection::
+ tracer (tracer_type& t)
+ {
+ tracer_ = &t;
+ }
+
+ inline void connection::
+ tracer (tracer_type* t)
+ {
+ tracer_ = t;
+ }
+
+ inline connection::tracer_type* connection::
+ tracer () const
+ {
+ return tracer_;
+ }
+
+ inline connection::tracer_type* connection::
+ transaction_tracer () const
+ {
+ return transaction_tracer_;
+ }
+}
diff --git a/libodb/odb/connection.txx b/libodb/odb/connection.txx
new file mode 100644
index 0000000..a082f14
--- /dev/null
+++ b/libodb/odb/connection.txx
@@ -0,0 +1,44 @@
+// file : odb/connection.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ template <typename T, database_id DB>
+ struct connection::query_<T, DB, class_object>
+ {
+ template <typename Q>
+ static prepared_query<T>
+ call (connection& c, const char* n, const Q& q)
+ {
+ // C++ compiler complaining there is no prepare_query()? Perhaps
+ // you forgot to specify --generate-prepared when compiling your
+ // persistent classes.
+ //
+ return prepared_query<T> (
+ object_traits_impl<T, DB>::prepare_query (c, n, q));
+ }
+ };
+
+ template <typename T, database_id DB>
+ struct connection::query_<T, DB, class_view>
+ {
+ template <typename Q>
+ static prepared_query<T>
+ call (connection& c, const char* n, const Q& q)
+ {
+ // C++ compiler complaining there is no prepare_query()? Perhaps
+ // you forgot to specify --generate-prepared when compiling your
+ // views.
+ //
+ return prepared_query<T> (
+ view_traits_impl<T, DB>::prepare_query (c, n, q));
+ }
+ };
+
+ template <typename P>
+ void connection::
+ params_deleter (void* p)
+ {
+ delete static_cast<P*> (p);
+ }
+}
diff --git a/libodb/odb/container-traits.hxx b/libodb/odb/container-traits.hxx
new file mode 100644
index 0000000..e2f44ce
--- /dev/null
+++ b/libodb/odb/container-traits.hxx
@@ -0,0 +1,219 @@
+// file : odb/container-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_CONTAINER_TRAITS_HXX
+#define ODB_CONTAINER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ // Keep this enum synchronized with the one in odb/odb/context.hxx.
+ //
+ enum container_kind
+ {
+ ck_ordered,
+ ck_set,
+ ck_multiset,
+ ck_map,
+ ck_multimap
+ };
+
+ //
+ // Container API provided by the generated code.
+ //
+
+ // Ordered containers.
+ //
+ template <typename I, typename V>
+ struct ordered_functions
+ {
+ typedef I index_type;
+ typedef V value_type;
+
+ // Return true if the order is preserved in the database. If the
+ // order is not preserved, then the index argument in the functions
+ // below is not used.
+ //
+ bool
+ ordered () const
+ {
+ return ordered_;
+ }
+
+ void
+ insert (I index, const V& value) const
+ {
+ insert_ (index, value, data_);
+ }
+
+ bool
+ select (I& next_index, V& next_value) const
+ {
+ return select_ (next_index, next_value, data_);
+ }
+
+ void
+ delete_ () const
+ {
+ delete__ (data_);
+ }
+
+ // Implementation details.
+ //
+ public:
+ ordered_functions (void* data): data_ (data) {}
+
+ public:
+ void* data_;
+ bool ordered_;
+
+ void (*insert_) (I, const V&, void*);
+ bool (*select_) (I&, V&, void*);
+ void (*delete__) (void*);
+ };
+
+ template <typename I, typename V>
+ struct smart_ordered_functions
+ {
+ typedef I index_type;
+ typedef V value_type;
+
+ void
+ insert (I index, const V& value) const
+ {
+ insert_ (index, value, data_);
+ }
+
+ bool
+ select (I& next_index, V& next_value) const
+ {
+ return select_ (next_index, next_value, data_);
+ }
+
+ void
+ update (I index, const V& value) const
+ {
+ update_ (index, value, data_);
+ }
+
+ // Delete all the elements starting with the specified index. To
+ // delete everything, pass 0.
+ //
+ void
+ delete_ (I start_index) const
+ {
+ delete__ (start_index, data_);
+ }
+
+ // Implementation details.
+ //
+ public:
+ smart_ordered_functions (void* data) : data_ (data) {}
+
+ public:
+ void* data_;
+
+ void (*insert_) (I, const V&, void*);
+ bool (*select_) (I&, V&, void*);
+ void (*update_) (I, const V&, void*);
+ void (*delete__) (I, void*);
+ };
+
+ // Set/multiset containers.
+ //
+ template <typename V>
+ struct set_functions
+ {
+ typedef V value_type;
+
+ void
+ insert (const V& value) const
+ {
+ insert_ (value, data_);
+ }
+
+ bool
+ select (V& next_value) const
+ {
+ return select_ (next_value, data_);
+ }
+
+ void
+ delete_ () const
+ {
+ delete__ (data_);
+ }
+
+ // Implementation details.
+ //
+ public:
+ set_functions (void* data): data_ (data) {}
+
+ public:
+ void* data_;
+
+ void (*insert_) (const V&, void*);
+ bool (*select_) (V&, void*);
+ void (*delete__) (void*);
+ };
+
+ // Map/multimap containers.
+ //
+ template <typename K, typename V>
+ struct map_functions
+ {
+ typedef K key_type;
+ typedef V value_type;
+
+ void
+ insert (const K& key, const V& value) const
+ {
+ insert_ (key, value, data_);
+ }
+
+ bool
+ select (K& next_key, V& next_value) const
+ {
+ return select_ (next_key, next_value, data_);
+ }
+
+ void
+ delete_ () const
+ {
+ delete__ (data_);
+ }
+
+ // Implementation details.
+ //
+ public:
+ map_functions (void* data): data_ (data) {}
+
+ public:
+ void* data_;
+
+ void (*insert_) (const K&, const V&, void*);
+ bool (*select_) (K&, V&, void*);
+ void (*delete__) (void*);
+ };
+}
+
+#include <odb/post.hxx>
+
+#include <odb/std-map-traits.hxx>
+#include <odb/std-set-traits.hxx>
+#include <odb/std-list-traits.hxx>
+#include <odb/std-vector-traits.hxx>
+#include <odb/std-deque-traits.hxx>
+
+#ifdef ODB_CXX11
+# include <odb/std-array-traits.hxx>
+# include <odb/std-forward-list-traits.hxx>
+# include <odb/std-unordered-map-traits.hxx>
+# include <odb/std-unordered-set-traits.hxx>
+#endif
+
+#endif // ODB_CONTAINER_TRAITS_HXX
diff --git a/libodb/odb/core.hxx b/libodb/odb/core.hxx
new file mode 100644
index 0000000..bca295d
--- /dev/null
+++ b/libodb/odb/core.hxx
@@ -0,0 +1,20 @@
+// file : odb/core.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_CORE_HXX
+#define ODB_CORE_HXX
+
+#include <odb/pre.hxx>
+
+#ifdef ODB_COMPILER
+# define PRAGMA_DB_IMPL(x) _Pragma (#x)
+# define PRAGMA_DB(x) PRAGMA_DB_IMPL (db x)
+#else
+# define PRAGMA_DB(x)
+#endif
+
+#include <odb/forward.hxx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_CORE_HXX
diff --git a/libodb/odb/database.cxx b/libodb/odb/database.cxx
new file mode 100644
index 0000000..9e098c7
--- /dev/null
+++ b/libodb/odb/database.cxx
@@ -0,0 +1,83 @@
+// file : odb/database.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/database.hxx>
+
+#include <odb/details/lock.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ using details::lock;
+
+ database::
+ ~database ()
+ {
+ }
+
+ unsigned long long database::
+ execute (const char* st, std::size_t n)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+ return c.execute (st, n);
+ }
+
+ const database::schema_version_migration_type& database::
+ schema_version_migration (const string& name) const
+ {
+ lock l (*mutex_); // Prevents concurrent loading.
+
+ schema_version_map::const_iterator i (schema_version_map_.find (name));
+ return i != schema_version_map_.end () && i->second.version != 0
+ ? i->second
+ : load_schema_version (name);
+ }
+
+ void database::
+ schema_version_migration (const schema_version_migration_type& svm,
+ const string& name)
+ {
+ // Note: no lock, not thread-safe.
+
+ schema_version_info& svi (schema_version_map_[name]);
+ if (svi.version != svm.version || svi.migration != svm.migration)
+ {
+ svi.version = svm.version;
+ svi.migration = svm.migration;
+ schema_version_seq_++;
+ }
+ }
+
+ bool database::
+ call_query_factory (const char* name, connection_type& c) const
+ {
+ query_factory_map::const_iterator i (query_factory_map_.find (name));
+
+ if (i == query_factory_map_.end ())
+ i = query_factory_map_.find (""); // Wildcard factory.
+
+ if (i == query_factory_map_.end ())
+ return false;
+
+ const query_factory_wrapper& fw (i->second);
+ if (fw.std_function == 0)
+ fw.function (name, c);
+ else
+ {
+ typedef void (*caller) (const void*, const char*, connection_type&);
+ fw.cast<caller> () (fw.std_function, name, c);
+ }
+
+ return true;
+ }
+
+ void database::
+ query_factory (const char* name, query_factory_wrapper w)
+ {
+ if (w)
+ query_factory_map_[name] = w; // Destructive copy assignment (move).
+ else
+ query_factory_map_.erase (name);
+ }
+}
diff --git a/libodb/odb/database.hxx b/libodb/odb/database.hxx
new file mode 100644
index 0000000..e18e8ee
--- /dev/null
+++ b/libodb/odb/database.hxx
@@ -0,0 +1,657 @@
+// file : odb/database.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DATABASE_HXX
+#define ODB_DATABASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <map>
+#include <string>
+#include <memory> // std::auto_ptr, std::unique_ptr
+#include <cstddef> // std::size_t
+
+#ifdef ODB_CXX11
+# include <utility> // std::move
+# include <functional> // std::function
+# include <type_traits> // std::enable_if, std::is_convertible
+#endif
+
+#include <odb/traits.hxx>
+#include <odb/forward.hxx>
+#include <odb/schema-version.hxx>
+#include <odb/query.hxx>
+#include <odb/prepared-query.hxx>
+#include <odb/result.hxx>
+#include <odb/connection.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/details/export.hxx>
+#include <odb/details/mutex.hxx>
+#include <odb/details/c-string.hxx>
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/function-wrapper.hxx>
+#include <odb/details/meta/answer.hxx>
+
+namespace odb
+{
+ class transaction_impl;
+
+ class LIBODB_EXPORT database
+ {
+ public:
+ virtual
+ ~database ();
+
+#ifdef ODB_CXX11
+ //database (database&&) = default; // VC 2013
+
+ // Note: noexcept is not specified since *_map_ (std::map) can throw.
+ //
+ database (database&& d)
+ : id_ (d.id_),
+ tracer_ (d.tracer_),
+ query_factory_map_ (std::move (d.query_factory_map_)),
+ mutex_ (std::move (d.mutex_)),
+ schema_version_map_ (std::move (d.schema_version_map_)),
+ schema_version_table_ (std::move (d.schema_version_table_)),
+ schema_version_seq_ (d.schema_version_seq_)
+ {
+ }
+#endif
+
+ private:
+ database (const database&);
+ database& operator= (const database&);
+
+#ifdef ODB_CXX11
+ database& operator= (const database&&);
+#endif
+
+ // 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 section_not_loaded exception
+ // if 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 odb::query<T>&);
+
+ // Query API.
+ //
+ template <typename T>
+ result<T>
+ query (bool cache = true);
+
+ template <typename T>
+ result<T>
+ query (const char*, bool cache = true);
+
+ template <typename T>
+ result<T>
+ query (const std::string&, bool cache = true);
+
+ template <typename T>
+ result<T>
+ query (const odb::query<T>&, bool cache = true);
+
+ // Query one API.
+ //
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one ();
+
+ template <typename T>
+ bool
+ query_one (T& object);
+
+ template <typename T>
+ T
+ query_value ();
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const char*);
+
+ template <typename T>
+ bool
+ query_one (const char*, T& object);
+
+ template <typename T>
+ T
+ query_value (const char*);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const std::string&);
+
+ template <typename T>
+ bool
+ query_one (const std::string&, T& object);
+
+ template <typename T>
+ T
+ query_value (const std::string&);
+
+ template <typename T>
+ typename result<T>::pointer_type
+ query_one (const odb::query<T>&);
+
+ template <typename T>
+ bool
+ query_one (const odb::query<T>&, T& object);
+
+ template <typename T>
+ T
+ query_value (const odb::query<T>&);
+
+ // 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 odb::query<T>&);
+
+ template <typename T>
+ void
+ cache_query (const prepared_query<T>&);
+
+#ifdef ODB_CXX11
+ template <typename T, typename P>
+ void
+ cache_query (const prepared_query<T>&, std::unique_ptr<P> params);
+#else
+ template <typename T, typename P>
+ void
+ cache_query (const prepared_query<T>&, std::auto_ptr<P> params);
+#endif
+
+ template <typename T>
+ prepared_query<T>
+ lookup_query (const char* name);
+
+ template <typename T, typename P>
+ prepared_query<T>
+ lookup_query (const char* name, P*& params);
+
+ // Prepared query factory.
+ //
+ public:
+ typedef odb::connection connection_type;
+
+ typedef void query_factory_type (const char* name, connection_type&);
+ typedef query_factory_type* query_factory_ptr;
+ typedef details::function_wrapper<
+ query_factory_type> query_factory_wrapper;
+
+#ifndef ODB_CXX11
+ void
+ query_factory (const char* name, query_factory_ptr);
+#else
+ template <typename F>
+ typename std::enable_if<
+ std::is_convertible<
+ F, std::function<query_factory_type>>::value, void>::type
+ query_factory (const char* name, F f)
+ {
+ query_factory (name, query_factory_wrapper (std::move (f)));
+ }
+#endif
+
+ bool
+ call_query_factory (const char* name, connection_type&) const;
+
+ private:
+ void
+ query_factory (const char* name, query_factory_wrapper);
+
+ // Native database statement execution.
+ //
+ public:
+ unsigned long long
+ execute (const char* statement);
+
+ unsigned long long
+ execute (const std::string& statement);
+
+ unsigned long long
+ execute (const char* statement, std::size_t length);
+
+ // Transactions.
+ //
+ public:
+ virtual transaction_impl*
+ begin () = 0;
+
+ // Connections.
+ //
+ public:
+ connection_ptr
+ connection ();
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef odb::tracer tracer_type;
+
+ void
+ tracer (tracer_type&);
+
+ void
+ tracer (tracer_type*);
+
+ tracer_type*
+ tracer () const;
+
+ // Database schema version.
+ //
+ public:
+ typedef odb::schema_version schema_version_type;
+ typedef odb::schema_version_migration schema_version_migration_type;
+
+ schema_version_type
+ schema_version (const std::string& schema_name = "") const;
+
+ bool
+ schema_migration (const std::string& schema_name = "") const;
+
+ // Note that there is code that relies on the returned reference
+ // being valid until the version is changed or the database instance
+ // is destroyed.
+ //
+ const schema_version_migration_type&
+ schema_version_migration (const std::string& schema_name = "") const;
+
+ // Set schema version and migration state manually.
+ //
+ // Note that the modifier API is not thread-safe. That is, you should
+ // not modify the schema version while other threads may be accessing
+ // or modifying the same information.
+ //
+ void
+ schema_version_migration (schema_version_type,
+ bool migration,
+ const std::string& schema_name = "");
+
+ void
+ schema_version_migration (const schema_version_migration_type&,
+ const std::string& schema_name = "");
+
+ // Set default schema version table for all the schema names. The table
+ // name should already be quoted if necessary.
+ //
+ void
+ schema_version_table (const std::string& table_name);
+
+ // Set schema version table for a specific schema.
+ //
+ void
+ schema_version_table (const std::string& table_name,
+ const std::string& schema_name);
+
+ // Schema version sequence number. It is incremented every time the
+ // schema version or migration flag is changed and can be used to
+ // detect overall version changes. The starting value is 1.
+ //
+ unsigned int
+ schema_version_sequence () const;
+
+ protected:
+ struct schema_version_info: schema_version_migration_type
+ {
+ std::string version_table;
+ };
+
+ virtual const schema_version_info&
+ load_schema_version (const std::string& schema_name) const = 0;
+
+ private:
+ const schema_version_info&
+ schema_version_migration_ (const std::string& schema_name) const;
+
+ // Database id.
+ //
+ public:
+ database_id
+ id () const;
+
+ protected:
+ database (database_id);
+
+ protected:
+ virtual connection_type*
+ connection_ () = 0;
+
+ protected:
+ template <typename T, database_id DB>
+ typename object_traits<T>::id_type
+ persist_ (T&);
+
+ template <typename T, database_id DB>
+ typename object_traits<T>::id_type
+ persist_ (const typename object_traits<T>::pointer_type&);
+
+ template <typename I, database_id DB>
+ void
+ persist_ (I, I, bool);
+
+ template <typename I, typename T, database_id DB>
+ void
+ persist_ (I, I, bool, details::meta::no ptr);
+
+ template <typename I, typename T, database_id DB>
+ void
+ persist_ (I, I, bool, details::meta::yes ptr);
+
+ template <typename T, database_id DB>
+ typename object_traits<T>::pointer_type
+ load_ (const typename object_traits<T>::id_type&);
+
+ template <typename T, database_id DB>
+ void
+ load_ (const typename object_traits<T>::id_type&, T&);
+
+ template <typename T, database_id DB>
+ void
+ load_ (T&, section&);
+
+ template <typename T, database_id DB>
+ void
+ reload_ (T&);
+
+ template <typename T, database_id DB>
+ typename object_traits<T>::pointer_type
+ find_ (const typename object_traits<T>::id_type&);
+
+ template <typename T, database_id DB>
+ bool
+ find_ (const typename object_traits<T>::id_type&, T&);
+
+ template <typename T, database_id DB>
+ void
+ update_ (T&);
+
+ template <typename T, database_id DB>
+ void
+ update_ (const typename object_traits<T>::pointer_type&);
+
+ template <typename I, database_id DB>
+ void
+ update_ (I, I, bool);
+
+ template <typename T, database_id DB>
+ void
+ update_ (const T&, const section&);
+
+ template <typename T, database_id DB>
+ void
+ erase_ (const typename object_traits<T>::id_type&);
+
+ template <typename T, database_id DB>
+ void
+ erase_ (T&);
+
+ template <typename T, database_id DB>
+ void
+ erase_ (const typename object_traits<T>::pointer_type&);
+
+ template <typename I, typename T, database_id DB>
+ void
+ erase_id_ (I, I, bool);
+
+ template <typename I, database_id DB>
+ void
+ erase_object_ (I, I, bool);
+
+ template <typename T, database_id DB, typename Q>
+ typename result<T>::pointer_type
+ query_one_ (const Q&);
+
+ template <typename T, database_id DB, typename Q>
+ bool
+ query_one_ (const Q&, T&);
+
+ template <typename T, database_id DB, typename Q>
+ T
+ query_value_ (const Q&);
+
+ template <typename T,
+ database_id DB,
+ class_kind kind = class_traits<T>::kind>
+ struct query_;
+
+ protected:
+ typedef
+ std::map<const char*, query_factory_wrapper, details::c_string_comparator>
+ query_factory_map;
+
+ typedef std::map<std::string, schema_version_info> schema_version_map;
+
+ database_id id_;
+ tracer_type* tracer_;
+ query_factory_map query_factory_map_;
+
+ details::unique_ptr<details::mutex> mutex_; // Dynamic for move support.
+ mutable schema_version_map schema_version_map_;
+ std::string schema_version_table_;
+ unsigned int schema_version_seq_;
+ };
+}
+
+#include <odb/database.ixx>
+#include <odb/database.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DATABASE_HXX
diff --git a/libodb/odb/database.ixx b/libodb/odb/database.ixx
new file mode 100644
index 0000000..c3cf2e2
--- /dev/null
+++ b/libodb/odb/database.ixx
@@ -0,0 +1,879 @@
+// file : odb/database.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::strlen()
+#include <utility> // std::move
+#include <iterator>
+
+#include <odb/transaction.hxx>
+#include <odb/pointer-traits.hxx>
+
+namespace odb
+{
+ template <typename T>
+ struct object_pointer_traits
+ {
+ typedef details::meta::no result_type;
+ typedef T object_type;
+ static const T& get_ref (const T& x) {return x;}
+ };
+
+ template <typename T>
+ struct object_pointer_traits<T*>
+ {
+ typedef details::meta::yes result_type;
+ typedef T object_type;
+ static const T& get_ref (const T* p) {return *p;}
+ };
+
+ template <typename T>
+ struct object_pointer_traits<T* const>
+ {
+ typedef details::meta::yes result_type;
+ typedef T object_type;
+ static const T& get_ref (const T* p) {return *p;}
+ };
+
+ template <typename T, template <typename> class P>
+ struct object_pointer_traits<P<T> >
+ {
+ typedef details::meta::yes result_type;
+ typedef T object_type;
+ static const T& get_ref (const P<T>& p) {
+ return pointer_traits<P<T> >::get_ref (p);}
+ };
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ struct object_pointer_traits<P<T, A1> >
+ {
+ typedef details::meta::yes result_type;
+ typedef T object_type;
+ static const T& get_ref (const P<T, A1>& p) {
+ return pointer_traits<P<T, A1> >::get_ref (p);}
+ };
+
+ template <typename T, template <typename> class P>
+ struct object_pointer_traits<const P<T> >
+ {
+ typedef details::meta::yes result_type;
+ typedef T object_type;
+ static const T& get_ref (const P<T>& p) {
+ return pointer_traits<P<T> >::get_ref (p);}
+ };
+
+ template <typename T, typename A1, template <typename, typename> class P>
+ struct object_pointer_traits<const P<T, A1> >
+ {
+ typedef details::meta::yes result_type;
+ typedef T object_type;
+ static const T& get_ref (const P<T, A1>& p) {
+ return pointer_traits<P<T, A1> >::get_ref (p);}
+ };
+
+ inline database::
+ database (database_id id)
+ : id_ (id),
+ tracer_ (0),
+ mutex_ (new details::mutex),
+ schema_version_seq_ (1)
+ {
+ }
+
+ inline database_id database::
+ id () const
+ {
+ return id_;
+ }
+
+ inline database::schema_version_type database::
+ schema_version (const std::string& name) const
+ {
+ return schema_version_migration (name).version;
+ }
+
+ inline bool database::
+ schema_migration (const std::string& name) const
+ {
+ return schema_version_migration (name).migration;
+ }
+
+ inline void database::
+ schema_version_migration (schema_version_type v,
+ bool m,
+ const std::string& name)
+ {
+ schema_version_migration (schema_version_migration_type (v, m), name);
+ }
+
+ inline void database::
+ schema_version_table (const std::string& tname)
+ {
+ schema_version_table_ = tname;
+ }
+
+ inline void database::
+ schema_version_table (const std::string& tname, const std::string& sname)
+ {
+ schema_version_map_[sname].version_table = tname;
+ }
+
+ inline unsigned int database::
+ schema_version_sequence () const
+ {
+ return schema_version_seq_;
+ }
+
+ inline connection_ptr database::
+ connection ()
+ {
+ return connection_ptr (connection_ ());
+ }
+
+#ifndef ODB_CXX11
+ inline void database::
+ query_factory (const char* name, query_factory_ptr f)
+ {
+ query_factory (name, query_factory_wrapper (f));
+ }
+#endif
+
+ inline void database::
+ tracer (tracer_type& t)
+ {
+ tracer_ = &t;
+ }
+
+ inline void database::
+ tracer (tracer_type* t)
+ {
+ tracer_ = t;
+ }
+
+ inline database::tracer_type* database::
+ tracer () const
+ {
+ return tracer_;
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (T& obj)
+ {
+ return persist_<T, id_common> (obj);
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::id_type database::
+ persist (const T& obj)
+ {
+ return persist_<const T, id_common> (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_common> (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_common> (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_common> (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_common> (pobj);
+ }
+
+ template <typename I>
+ inline void database::
+ persist (I b, I e, bool cont)
+ {
+ persist_<I, id_common> (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_common> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ load (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return load_<T, id_common> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ load (T& obj, section& s)
+ {
+ return load_<T, id_common> (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_common> (id);
+ }
+
+ template <typename T>
+ inline bool database::
+ find (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ return find_<T, id_common> (id, obj);
+ }
+
+ template <typename T>
+ inline void database::
+ reload (T& obj)
+ {
+ reload_<T, id_common> (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_common> (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_common> (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_common> (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_common> (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_common> (pobj);
+ }
+
+ template <typename I>
+ inline void database::
+ update (I b, I e, bool cont)
+ {
+ update_<I, id_common> (b, e, cont);
+ }
+
+ template <typename T>
+ inline void database::
+ update (const T& obj, const section& s)
+ {
+ update_<T, id_common> (obj, s);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (const typename object_traits<T>::id_type& id)
+ {
+ return erase_<T, id_common> (id);
+ }
+
+ template <typename T>
+ inline void database::
+ erase (T& obj)
+ {
+ return erase_<T, id_common> (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_common> (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_common> (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_common> (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_common> (pobj);
+ }
+
+ template <typename T, typename I>
+ inline void database::
+ erase (I idb, I ide, bool cont)
+ {
+ erase_id_<I, T, id_common> (idb, ide, cont);
+ }
+
+ template <typename I>
+ inline void database::
+ erase (I ob, I oe, bool cont)
+ {
+ erase_object_<I, id_common> (ob, oe, cont);
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query ()
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (odb::query<T> ());
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const char* q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const std::string& q)
+ {
+ // T is always object_type.
+ //
+ return erase_query<T> (odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline unsigned long long database::
+ erase_query (const odb::query<T>& q)
+ {
+ // T is always object_type.
+ //
+ return object_traits_impl<T, id_common>::erase_query (*this, q);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (bool cache)
+ {
+ return query<T> (odb::query<T> (), cache);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const char* q, bool cache)
+ {
+ return query<T> (odb::query<T> (q), cache);
+ }
+
+ template <typename T>
+ inline result<T> database::
+ query (const std::string& q, bool cache)
+ {
+ return query<T> (odb::query<T> (q), cache);
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one ()
+ {
+ return query_one<T> (odb::query<T> ());
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (T& o)
+ {
+ return query_one<T> (odb::query<T> (), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value ()
+ {
+ return query_value<T> (odb::query<T> ());
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const char* q)
+ {
+ return query_one<T> (odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const char* q, T& o)
+ {
+ return query_one<T> (odb::query<T> (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const char* q)
+ {
+ return query_value<T> (odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const std::string& q)
+ {
+ return query_one<T> (odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const std::string& q, T& o)
+ {
+ return query_one<T> (odb::query<T> (q), o);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const std::string& q)
+ {
+ return query_value<T> (odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline bool database::
+ query_one (const odb::query<T>& q, T& o)
+ {
+ return query_one_<T, id_common> (q, o);
+ }
+
+ template <typename T>
+ inline typename result<T>::pointer_type database::
+ query_one (const odb::query<T>& q)
+ {
+ return query_one_<T, id_common> (q);
+ }
+
+ template <typename T>
+ inline T database::
+ query_value (const odb::query<T>& q)
+ {
+ return query_value_<T, id_common> (q);
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const char* q)
+ {
+ return prepare_query<T> (n, odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const std::string& q)
+ {
+ return prepare_query<T> (n, odb::query<T> (q));
+ }
+
+ template <typename T>
+ inline prepared_query<T> database::
+ prepare_query (const char* n, const odb::query<T>& q)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+ return c.prepare_query (n, q);
+ }
+
+ template <typename T>
+ inline void database::
+ cache_query (const prepared_query<T>& pq)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+ c.cache_query (pq);
+ }
+
+#ifdef ODB_CXX11
+ template <typename T, typename P>
+ inline void database::
+ cache_query (const prepared_query<T>& pq, std::unique_ptr<P> params)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+ c.cache_query (pq, std::move (params));
+ }
+#else
+ template <typename T, typename P>
+ inline void database::
+ cache_query (const prepared_query<T>& pq, std::auto_ptr<P> params)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+ c.cache_query (pq, params);
+ }
+#endif
+
+ template <typename T>
+ inline prepared_query<T> database::
+ lookup_query (const char* name)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+ return c.lookup_query<T> (name);
+ }
+
+ template <typename T, typename P>
+ inline prepared_query<T> database::
+ lookup_query (const char* name, P*& params)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+ return c.lookup_query<T, P> (name, params);
+ }
+
+ // Implementations (i.e., the *_() functions).
+ //
+ template <typename I, database_id DB>
+ inline void database::
+ persist_ (I b, I e, bool cont)
+ {
+ // Sun CC with non-standard STL does not have iterator_traits.
+ //
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ typedef typename std::iterator_traits<I>::value_type value_type;
+#else
+ // Assume iterator is just a pointer.
+ //
+ typedef typename object_pointer_traits<I>::object_type value_type;
+#endif
+
+ typedef object_pointer_traits<value_type> opt;
+
+ persist_<I, typename opt::object_type, id_common> (
+ b, e, cont, typename opt::result_type ());
+ }
+
+ template <typename T, database_id DB>
+ inline typename object_traits<T>::pointer_type database::
+ find_ (const typename object_traits<T>::id_type& id)
+ {
+ // T is always object_type.
+ //
+
+ // Compiler error pointing here? Perhaps the object doesn't have the
+ // default constructor?
+ //
+ return object_traits_impl<T, DB>::find (*this, id);
+ }
+
+ template <typename T, database_id DB>
+ inline bool database::
+ find_ (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ // T is always object_type.
+ //
+ return object_traits_impl<T, DB>::find (*this, id, obj);
+ }
+
+ template <typename T, database_id DB>
+ inline void database::
+ update_ (T& obj)
+ {
+ // T can be const T while object_type will always be T.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+
+ // Compiler error pointing here? Perhaps the object is readonly or
+ // doesn't have an object id? Such objects cannot be updated.
+ //
+ object_traits_impl<object_type, DB>::update (*this, obj);
+ }
+
+ template <typename T, database_id DB>
+ inline void database::
+ update_ (const typename object_traits<T>::pointer_type& pobj)
+ {
+ // T can be const T while object_type will always be T.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ T& obj (pointer_traits<pointer_type>::get_ref (pobj));
+
+ // Compiler error pointing here? Perhaps the object is readonly or
+ // doesn't have an object id? Such objects cannot be updated.
+ //
+ object_traits_impl<object_type, DB>::update (*this, obj);
+ }
+
+ template <typename T, database_id DB>
+ inline void database::
+ erase_ (const typename object_traits<T>::id_type& id)
+ {
+ // T is always object_type.
+ //
+ object_traits_impl<T, DB>::erase (*this, id);
+ }
+
+ template <typename T, database_id DB>
+ inline void database::
+ erase_ (T& obj)
+ {
+ // T can be const T while object_type will always be T.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+
+ object_traits_impl<object_type, DB>::erase (*this, obj);
+ }
+
+ template <typename T, database_id DB>
+ inline void database::
+ erase_ (const typename object_traits<T>::pointer_type& pobj)
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ erase_<T, DB> (pointer_traits<pointer_type>::get_ref (pobj));
+ }
+
+ template <typename T, database_id DB, typename Q>
+ inline typename result<T>::pointer_type database::
+ query_one_ (const Q& q)
+ {
+ result<T> r (query_<T, DB>::call (*this, q));
+
+ // We still have to cache the result since loading the object
+ // may result in loading of it's related objects and that would
+ // invalidate the result even for just getting the 'end' status.
+ //
+ r.cache ();
+
+ return r.one ();
+ }
+
+ template <typename T, database_id DB, typename Q>
+ inline bool database::
+ query_one_ (const Q& q, T& o)
+ {
+ result<T> r (query_<T, DB>::call (*this, q));
+ r.cache (); // See above.
+ return r.one (o);
+ }
+
+ template <typename T, database_id DB, typename Q>
+ inline T database::
+ query_value_ (const Q& q)
+ {
+ result<T> r (query_<T, DB>::call (*this, q));
+ r.cache (); // See above.
+
+ // Compiler error pointing here? The object must be default-constructible
+ // in order to use the return-by-value API.
+ //
+ T o;
+ r.value (o);
+ return o;
+ }
+
+ // execute()
+ //
+ inline unsigned long long database::
+ execute (const char* statement)
+ {
+ return execute (statement, std::strlen (statement));
+ }
+
+ inline unsigned long long database::
+ execute (const std::string& statement)
+ {
+ return execute (statement.c_str (), statement.size ());
+ }
+}
diff --git a/libodb/odb/database.txx b/libodb/odb/database.txx
new file mode 100644
index 0000000..5659b6f
--- /dev/null
+++ b/libodb/odb/database.txx
@@ -0,0 +1,491 @@
+// file : odb/database.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/section.hxx>
+#include <odb/exceptions.hxx>
+#include <odb/no-op-cache-traits.hxx>
+#include <odb/pointer-traits.hxx>
+
+namespace odb
+{
+ template <typename T>
+ result<T> database::
+ query (const odb::query<T>& q, bool cache)
+ {
+ // T is always object_type. We also don't need to check for transaction
+ // here; object_traits::query () does this.
+ //
+ result<T> r (query_<T, id_common>::call (*this, q));
+
+ if (cache)
+ r.cache ();
+
+ return r;
+ }
+
+ // Implementations (i.e., the *_() functions).
+ //
+ template <typename T, database_id DB>
+ typename object_traits<T>::id_type database::
+ persist_ (T& obj)
+ {
+ // T can be const T while object_type will always be T.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef object_traits_impl<object_type, DB> object_traits;
+
+ object_traits::persist (*this, obj);
+
+ typename object_traits::reference_cache_traits::position_type p (
+ object_traits::reference_cache_traits::insert (
+ *this, reference_cache_type<T>::convert (obj)));
+
+ object_traits::reference_cache_traits::persist (p);
+
+ return object_traits::id (obj);
+ }
+
+ template <typename T, database_id DB>
+ typename object_traits<T>::id_type database::
+ persist_ (const typename object_traits<T>::pointer_type& pobj)
+ {
+ // T can be const T while object_type will always be T.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, DB> object_traits;
+
+ T& obj (pointer_traits<pointer_type>::get_ref (pobj));
+ object_traits::persist (*this, obj);
+
+ // Get the canonical object pointer and insert it into object cache.
+ //
+ typename object_traits::pointer_cache_traits::position_type p (
+ object_traits::pointer_cache_traits::insert (
+ *this, pointer_cache_type<pointer_type>::convert (pobj)));
+
+ object_traits::pointer_cache_traits::persist (p);
+
+ return object_traits::id (obj);
+ }
+
+ template <typename T, bool = object_traits<T>::auto_id> struct persist_type;
+ template <typename T> struct persist_type<T, true> {typedef T type;};
+ template <typename T> struct persist_type<T, false> {typedef const T type;};
+
+ template <typename I, typename T, database_id DB>
+ void database::
+ persist_ (I b, I e, bool cont, details::meta::no /*ptr*/)
+ {
+ // T can be const T while object_type will always be T.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef object_traits_impl<object_type, DB> object_traits;
+
+ multiple_exceptions mex (typeid (object_already_persistent));
+ try
+ {
+ while (b != e && (cont || mex.empty ()))
+ {
+ std::size_t n (0);
+ T* a[object_traits::batch]; // T instead of persist_type for cache.
+
+ for (; b != e && n < object_traits::batch; ++n)
+ {
+ // Compiler error pointing here? Perhaps the passed range is
+ // of const objects?
+ //
+ typename persist_type<object_type>::type* p (&(*b++));
+
+ a[n] = const_cast<T*> (p);
+ }
+
+ // Compiler error pointing here? Perhaps the object or the
+ // database does not support bulk operations?
+ //
+ object_traits::persist (
+ *this,
+ const_cast<typename persist_type<object_type>::type**> (a),
+ n,
+ mex);
+
+ if (mex.fatal ())
+ break;
+
+ for (std::size_t i (0); i < n; ++i)
+ {
+ if (mex[i] != 0) // Don't cache objects that have failed.
+ continue;
+
+ mex.current (i); // Set position in case the below code throws.
+
+ typename object_traits::reference_cache_traits::position_type p (
+ object_traits::reference_cache_traits::insert (
+ *this, reference_cache_type<T>::convert (*a[i])));
+
+ object_traits::reference_cache_traits::persist (p);
+ }
+
+ mex.delta (n);
+ }
+ }
+ catch (const odb::exception& ex)
+ {
+ mex.insert (ex, true);
+ }
+
+ if (!mex.empty ())
+ {
+ mex.prepare ();
+ throw mex;
+ }
+ }
+
+ template <typename P>
+ struct pointer_copy
+ {
+ const P* ref;
+ P copy;
+
+ void assign (const P& p) {ref = &p;}
+ template <typename P1> void assign (const P1& p1)
+ {
+ // 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 P& p (p1);
+
+ copy = p;
+ ref = &copy;
+ }
+ };
+
+ template <typename I, typename T, database_id DB>
+ void database::
+ persist_ (I b, I e, bool cont, details::meta::yes /*ptr*/)
+ {
+ // T can be const T while object_type will always be T.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ typedef object_traits_impl<object_type, DB> object_traits;
+
+ multiple_exceptions mex (typeid (object_already_persistent));
+ try
+ {
+ while (b != e && (cont || mex.empty ()))
+ {
+ std::size_t n (0);
+ typename persist_type<object_type>::type* a[object_traits::batch];
+ pointer_copy<pointer_type> p[object_traits::batch];
+
+ for (; b != e && n < object_traits::batch; ++n)
+ {
+ p[n].assign (*b++);
+ a[n] = &pointer_traits<pointer_type>::get_ref (*p[n].ref);
+ }
+
+ // Compiler error pointing here? Perhaps the object or the
+ // database does not support bulk operations?
+ //
+ object_traits::persist (*this, a, n, mex);
+
+ if (mex.fatal ())
+ break;
+
+ for (std::size_t i (0); i < n; ++i)
+ {
+ if (mex[i] != 0) // Don't cache objects that have failed.
+ continue;
+
+ mex.current (i); // Set position in case the below code throws.
+
+ // Get the canonical object pointer and insert it into object cache.
+ //
+ typename object_traits::pointer_cache_traits::position_type pos (
+ object_traits::pointer_cache_traits::insert (
+ *this, pointer_cache_type<pointer_type>::convert (*p[i].ref)));
+
+ object_traits::pointer_cache_traits::persist (pos);
+ }
+
+ mex.delta (n);
+ }
+ }
+ catch (const odb::exception& ex)
+ {
+ mex.insert (ex, true);
+ }
+
+ if (!mex.empty ())
+ {
+ mex.prepare ();
+ throw mex;
+ }
+ }
+
+ template <typename T, database_id DB>
+ typename object_traits<T>::pointer_type database::
+ load_ (const typename object_traits<T>::id_type& id)
+ {
+ // T is always object_type.
+ //
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ pointer_type r (find_<T, DB> (id));
+
+ if (pointer_traits<pointer_type>::null_ptr (r))
+ throw object_not_persistent ();
+
+ return r;
+ }
+
+ template <typename T, database_id DB>
+ void database::
+ load_ (const typename object_traits<T>::id_type& id, T& obj)
+ {
+ if (!find_<T, DB> (id, obj))
+ throw object_not_persistent ();
+ }
+
+ template <typename T, database_id DB>
+ void database::
+ load_ (T& obj, section& s)
+ {
+ connection_type& c (transaction::current ().connection (*this));
+
+ // T is always object_type.
+ //
+ if (object_traits_impl<T, DB>::load (c, obj, s))
+ s.reset (true, false); // Loaded, unchanged.
+ else
+ throw section_not_in_object ();
+ }
+
+ template <typename T, database_id DB>
+ void database::
+ reload_ (T& obj)
+ {
+ // T should be object_type (cannot be const). We also don't need to
+ // check for transaction here; object_traits::reload () does this.
+ //
+ if (!object_traits_impl<T, DB>::reload (*this, obj))
+ throw object_not_persistent ();
+ }
+
+ template <typename I, database_id DB>
+ void database::
+ update_ (I b, I e, bool cont)
+ {
+ // Sun CC with non-standard STL does not have iterator_traits.
+ //
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ typedef typename std::iterator_traits<I>::value_type value_type;
+#else
+ // Assume iterator is just a pointer.
+ //
+ typedef typename object_pointer_traits<I>::object_type value_type;
+#endif
+
+ // object_pointer_traits<T>::object_type can be const.
+ //
+ typedef object_pointer_traits<value_type> opt;
+
+ typedef
+ typename object_traits<typename opt::object_type>::object_type
+ object_type;
+
+ typedef object_traits_impl<object_type, DB> object_traits;
+
+ multiple_exceptions mex (
+ object_traits::managed_optimistic_column_count == 0
+ ? typeid (object_not_persistent)
+ : typeid (object_changed));
+
+ try
+ {
+ while (b != e && (cont || mex.empty ()))
+ {
+ std::size_t n (0);
+ const object_type* a[object_traits::batch];
+
+ for (; b != e && n < object_traits::batch; ++n)
+ a[n] = &opt::get_ref (*b++);
+
+ // Compiler error pointing here? Perhaps the object or the
+ // database does not support bulk operations?
+ //
+ object_traits::update (*this, a, n, mex);
+
+ if (mex.fatal ())
+ break;
+
+ mex.delta (n);
+ }
+ }
+ catch (const odb::exception& ex)
+ {
+ mex.insert (ex, true);
+ }
+
+ if (!mex.empty ())
+ {
+ mex.prepare ();
+ throw mex;
+ }
+ }
+
+ template <typename T, database_id DB>
+ void database::
+ update_ (const T& obj, const section& s)
+ {
+ if (!s.loaded ())
+ throw section_not_loaded ();
+
+ transaction& t (transaction::current ());
+
+ // T is always object_type.
+ //
+ if (object_traits_impl<T, DB>::update (t.connection (*this), obj, s))
+ {
+ if (s.changed ())
+ s.reset (true, false, &t); // Clear the change flag.
+ }
+ else
+ throw section_not_in_object ();
+ }
+
+ template <typename I, typename T, database_id DB>
+ void database::
+ erase_id_ (I b, I e, bool cont)
+ {
+ // T is explicitly specified by the caller, so assume it is object type.
+ //
+ typedef T object_type;
+ typedef object_traits_impl<object_type, DB> object_traits;
+ typedef typename object_traits::id_type id_type;
+
+ multiple_exceptions mex (typeid (object_not_persistent));
+ try
+ {
+ while (b != e && (cont || mex.empty ()))
+ {
+ std::size_t n (0);
+ const id_type* a[object_traits::batch];
+
+ for (; b != e && n < object_traits::batch; ++n)
+ // Compiler error pointing here? Perhaps the object id type
+ // and the range element type don't match?
+ //
+ a[n] = &(*b++);
+
+ // Compiler error pointing here? Perhaps the object or the
+ // database does not support bulk operations?
+ //
+ object_traits::erase (*this, a, n, mex);
+
+ if (mex.fatal ())
+ break;
+
+ mex.delta (n);
+ }
+ }
+ catch (const odb::exception& ex)
+ {
+ mex.insert (ex, true);
+ }
+
+ if (!mex.empty ())
+ {
+ mex.prepare ();
+ throw mex;
+ }
+ }
+
+ template <typename I, database_id DB>
+ void database::
+ erase_object_ (I b, I e, bool cont)
+ {
+ // Sun CC with non-standard STL does not have iterator_traits.
+ //
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ typedef typename std::iterator_traits<I>::value_type value_type;
+#else
+ // Assume iterator is just a pointer.
+ //
+ typedef typename object_pointer_traits<I>::object_type value_type;
+#endif
+
+ // object_pointer_traits<T>::object_type can be const.
+ //
+ typedef object_pointer_traits<value_type> opt;
+
+ typedef
+ typename object_traits<typename opt::object_type>::object_type
+ object_type;
+
+ typedef object_traits_impl<object_type, DB> object_traits;
+
+ multiple_exceptions mex (
+ object_traits::managed_optimistic_column_count == 0
+ ? typeid (object_not_persistent)
+ : typeid (object_changed));
+
+ try
+ {
+ while (b != e && (cont || mex.empty ()))
+ {
+ std::size_t n (0);
+ const object_type* a[object_traits::batch];
+
+ for (; b != e && n < object_traits::batch; ++n)
+ a[n] = &opt::get_ref (*b++);
+
+ // Compiler error pointing here? Perhaps the object or the
+ // database does not support bulk operations?
+ //
+ object_traits::erase (*this, a, n, mex);
+
+ if (mex.fatal ())
+ break;
+
+ mex.delta (n);
+ }
+ }
+ catch (const odb::exception& ex)
+ {
+ mex.insert (ex, true);
+ }
+
+ if (!mex.empty ())
+ {
+ mex.prepare ();
+ throw mex;
+ }
+ }
+
+ template <typename T, database_id DB>
+ struct database::query_<T, DB, class_object>
+ {
+ template <typename Q>
+ static result<T>
+ call (database& db, const Q& q)
+ {
+ return object_traits_impl<T, DB>::query (db, q);
+ }
+ };
+
+ template <typename T, database_id DB>
+ struct database::query_<T, DB, class_view>
+ {
+ template <typename Q>
+ static result<T>
+ call (database& db, const Q& q)
+ {
+ return view_traits_impl<T, DB>::query (db, q);
+ }
+ };
+}
diff --git a/libodb/odb/details/buffer.cxx b/libodb/odb/details/buffer.cxx
new file mode 100644
index 0000000..595329e
--- /dev/null
+++ b/libodb/odb/details/buffer.cxx
@@ -0,0 +1,35 @@
+// file : odb/details/buffer.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::memcpy
+
+#include <odb/details/buffer.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace details
+ {
+ void basic_buffer_base::
+ capacity (size_t c, size_t data_size)
+ {
+ if (c > capacity_)
+ {
+ size_t n (capacity_ * 2 > c ? capacity_ * 2 : c);
+ void* d (operator new (n));
+
+ if (data_ != 0)
+ {
+ if (data_size != 0)
+ memcpy (d, data_, data_size);
+
+ operator delete (data_);
+ }
+
+ data_ = d;
+ capacity_ = n;
+ }
+ }
+ }
+}
diff --git a/libodb/odb/details/buffer.hxx b/libodb/odb/details/buffer.hxx
new file mode 100644
index 0000000..558be9b
--- /dev/null
+++ b/libodb/odb/details/buffer.hxx
@@ -0,0 +1,92 @@
+// file : odb/details/buffer.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_BUFFER_DETAILS_HXX
+#define ODB_BUFFER_DETAILS_HXX
+
+#include <odb/pre.hxx>
+
+#include <new>
+#include <cstddef> // std::size_t
+
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT basic_buffer_base
+ {
+ public:
+ ~basic_buffer_base ()
+ {
+ if (data_ != 0)
+ operator delete (data_);
+ }
+
+ basic_buffer_base (std::size_t capacity)
+ : capacity_ (capacity)
+ {
+ data_ = capacity_ == 0 ? 0 : operator new (capacity_);
+ }
+
+ std::size_t
+ capacity () const
+ {
+ return capacity_;
+ }
+
+ void
+ capacity (std::size_t, std::size_t data_size = 0);
+
+ protected:
+ void* data_;
+ std::size_t capacity_;
+ };
+
+ template <typename T>
+ class basic_buffer: public basic_buffer_base
+ {
+ public:
+ basic_buffer (std::size_t capacity = 256)
+ : basic_buffer_base (capacity)
+ {
+ }
+
+ T*
+ data ()
+ {
+ return static_cast<T*> (data_);
+ }
+
+ const T*
+ data () const
+ {
+ return static_cast<T*> (data_);
+ }
+
+ // Note that strictly speaking the return type should be void* const*
+ // but that would make using this function too awkward since we often
+ // store the result as void*.
+ //
+ void**
+ data_ptr ()
+ {
+ return &data_;
+ }
+
+ const void* const*
+ data_ptr () const
+ {
+ return &data_;
+ }
+ };
+
+ typedef basic_buffer<char> buffer;
+ typedef basic_buffer<unsigned char> ubuffer;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_BUFFER_DETAILS_HXX
diff --git a/libodb/odb/details/build2/config-stub.h b/libodb/odb/details/build2/config-stub.h
new file mode 100644
index 0000000..83ea927
--- /dev/null
+++ b/libodb/odb/details/build2/config-stub.h
@@ -0,0 +1,5 @@
+/* file : odb/details/build2/config-stub.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+#include <odb/details/config.h>
diff --git a/libodb/odb/details/build2/config-vc-stub.h b/libodb/odb/details/build2/config-vc-stub.h
new file mode 100644
index 0000000..e440e70
--- /dev/null
+++ b/libodb/odb/details/build2/config-vc-stub.h
@@ -0,0 +1,5 @@
+/* file : odb/details/build2/config-vc-stub.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+#include <odb/details/config-vc.h>
diff --git a/libodb/odb/details/build2/config-vc.h b/libodb/odb/details/build2/config-vc.h
new file mode 100644
index 0000000..77ce106
--- /dev/null
+++ b/libodb/odb/details/build2/config-vc.h
@@ -0,0 +1,23 @@
+/* file : odb/details/build2/config-vc.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* Configuration file for Windows/VC++ for the build2 build. */
+
+#ifndef ODB_DETAILS_CONFIG_VC_H
+#define ODB_DETAILS_CONFIG_VC_H
+
+/* Define LIBODB_BUILD2 for the installed case. */
+#ifndef LIBODB_BUILD2
+# define LIBODB_BUILD2
+#endif
+
+#ifndef ODB_THREADS_NONE
+# if _MSC_VER >= 1900
+# define ODB_THREADS_CXX11
+# else
+# error Unsupoprted MSVC version (no thread_local)
+# endif
+#endif
+
+#endif /* ODB_DETAILS_CONFIG_VC_H */
diff --git a/libodb/odb/details/build2/config.h b/libodb/odb/details/build2/config.h
new file mode 100644
index 0000000..ba434f4
--- /dev/null
+++ b/libodb/odb/details/build2/config.h
@@ -0,0 +1,19 @@
+/* file : odb/details/build2/config.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* Static configuration file for the build2 build. */
+
+#ifndef ODB_DETAILS_CONFIG_H
+#define ODB_DETAILS_CONFIG_H
+
+/* Define LIBODB_BUILD2 for the installed case. */
+#ifndef LIBODB_BUILD2
+# define LIBODB_BUILD2
+#endif
+
+#ifndef ODB_THREADS_NONE
+# define ODB_THREADS_CXX11
+#endif
+
+#endif /* ODB_DETAILS_CONFIG_H */
diff --git a/libodb/odb/details/c-string.hxx b/libodb/odb/details/c-string.hxx
new file mode 100644
index 0000000..6ab1adc
--- /dev/null
+++ b/libodb/odb/details/c-string.hxx
@@ -0,0 +1,28 @@
+// file : odb/details/c-string.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_C_STRING_HXX
+#define ODB_DETAILS_C_STRING_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstring>
+
+namespace odb
+{
+ namespace details
+ {
+ struct c_string_comparator
+ {
+ bool
+ operator() (const char* x, const char* y) const
+ {
+ return std::strcmp (x, y) < 0;
+ }
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_C_STRING_HXX
diff --git a/libodb/odb/details/condition.cxx b/libodb/odb/details/condition.cxx
new file mode 100644
index 0000000..2c4cbdb
--- /dev/null
+++ b/libodb/odb/details/condition.cxx
@@ -0,0 +1,13 @@
+// file : odb/details/condition.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/condition.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ // This otherwise unnecessary file is here to allow instantiation
+ // of inline functions for exporting.
+ }
+}
diff --git a/libodb/odb/details/condition.hxx b/libodb/odb/details/condition.hxx
new file mode 100644
index 0000000..10b6b4a
--- /dev/null
+++ b/libodb/odb/details/condition.hxx
@@ -0,0 +1,68 @@
+// file : odb/details/condition.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_CONDITION_HXX
+#define ODB_DETAILS_CONDITION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx>
+
+#ifdef ODB_THREADS_NONE
+
+namespace odb
+{
+ namespace details
+ {
+ class mutex;
+ class lock;
+
+ class condition
+ {
+ public:
+ condition (mutex&) {}
+
+ void
+ signal () {}
+
+ void
+ wait (lock&) {}
+
+ private:
+ condition (const condition&);
+ condition& operator= (const condition&);
+ };
+ }
+}
+
+#elif defined(ODB_THREADS_CXX11)
+# include <condition_variable>
+# include <odb/details/mutex.hxx>
+# include <odb/details/lock.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class condition: public std::condition_variable
+ {
+ public:
+ condition (mutex&) {}
+
+ void
+ signal () {notify_one ();}
+ };
+ }
+}
+
+#elif defined(ODB_THREADS_POSIX)
+#include <odb/details/posix/condition.hxx>
+#elif defined(ODB_THREADS_WIN32)
+#include <odb/details/win32/condition.hxx>
+#else
+# error unknown threading model
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_CONDITION_HXX
diff --git a/libodb/odb/details/config-vc.h b/libodb/odb/details/config-vc.h
new file mode 100644
index 0000000..b1e38b2
--- /dev/null
+++ b/libodb/odb/details/config-vc.h
@@ -0,0 +1,13 @@
+/* file : odb/details/config-vc.h
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* Configuration file for Windows/VC++. */
+
+#ifndef ODB_DETAILS_CONFIG_VC_H
+#define ODB_DETAILS_CONFIG_VC_H
+
+#define ODB_THREADS_WIN32
+#define ODB_THREADS_TLS_DECLSPEC
+
+#endif /* ODB_DETAILS_CONFIG_VC_H */
diff --git a/libodb/odb/details/config.h.in b/libodb/odb/details/config.h.in
new file mode 100644
index 0000000..2396606
--- /dev/null
+++ b/libodb/odb/details/config.h.in
@@ -0,0 +1,18 @@
+/* file : odb/details/config.h.in
+ * license : GNU GPL v2; see accompanying LICENSE file
+ */
+
+/* This file is automatically processed by configure. */
+
+#ifndef ODB_DETAILS_CONFIG_H
+#define ODB_DETAILS_CONFIG_H
+
+#undef ODB_THREADS_NONE
+#undef ODB_THREADS_POSIX
+#undef ODB_THREADS_WIN32
+#undef ODB_THREADS_TLS_KEYWORD
+#undef ODB_THREADS_TLS_DECLSPEC
+
+#undef LIBODB_STATIC_LIB
+
+#endif /* ODB_DETAILS_CONFIG_H */
diff --git a/libodb/odb/details/config.hxx b/libodb/odb/details/config.hxx
new file mode 100644
index 0000000..a42dd69
--- /dev/null
+++ b/libodb/odb/details/config.hxx
@@ -0,0 +1,80 @@
+// file : odb/details/config.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_CONFIG_HXX
+#define ODB_DETAILS_CONFIG_HXX
+
+// no pre
+
+// C++11 support.
+//
+#ifdef _MSC_VER
+# if _MSC_VER >= 1600 // VC++10 and later have C++11 always enabled.
+# define ODB_CXX11
+# define ODB_CXX11_NULLPTR
+# if _MSC_VER >= 1700
+# define ODB_CXX11_ENUM
+# if _MSC_VER >= 1800
+# define ODB_CXX11_DELETED_FUNCTION
+# define ODB_CXX11_EXPLICIT_CONVERSION_OPERATOR
+# define ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+# define ODB_CXX11_VARIADIC_TEMPLATE
+# define ODB_CXX11_INITIALIZER_LIST
+# if _MSC_VER >= 1900
+# define ODB_CXX11_NOEXCEPT
+# endif
+# endif
+# endif
+# endif
+#else
+# if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
+# define ODB_CXX11
+# ifdef __clang__ // Pretends to be a really old __GNUC__ on some platforms.
+# define ODB_CXX11_NULLPTR
+# define ODB_CXX11_NOEXCEPT
+# elif defined(__GNUC__)
+# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4
+# define ODB_CXX11_NULLPTR
+# define ODB_CXX11_NOEXCEPT
+# endif
+# else
+# define ODB_CXX11_NULLPTR
+# define ODB_CXX11_NOEXCEPT
+# endif
+# define ODB_CXX11_DELETED_FUNCTION
+# define ODB_CXX11_EXPLICIT_CONVERSION_OPERATOR
+# define ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+# define ODB_CXX11_VARIADIC_TEMPLATE
+# define ODB_CXX11_INITIALIZER_LIST
+# define ODB_CXX11_ENUM // GCC 4.4 (forward -- 4.6), Clang 2.9 (3.1).
+# endif
+#endif
+
+#ifdef ODB_CXX11_NOEXCEPT
+# define ODB_NOTHROW_NOEXCEPT noexcept
+#else
+# define ODB_NOTHROW_NOEXCEPT throw()
+#endif
+
+// Once we drop support for C++98, we can probably get rid of config.h except
+// for the autotools case by fixing ODB_THREADS_CXX11 (and perhaps supporting
+// the ODB_THREADS_NONE case via a "global" (command line) define).
+//
+#ifdef ODB_COMPILER
+# define ODB_THREADS_NONE
+# define LIBODB_STATIC_LIB
+#elif defined(LIBODB_BUILD2)
+# if defined(_MSC_VER)
+# include <odb/details/build2/config-vc.h>
+# else
+# include <odb/details/build2/config.h>
+# endif
+#elif defined(_MSC_VER)
+# include <odb/details/config-vc.h>
+#else
+# include <odb/details/config.h>
+#endif
+
+// no post
+
+#endif // ODB_DETAILS_CONFIG_HXX
diff --git a/libodb/odb/details/exception.hxx b/libodb/odb/details/exception.hxx
new file mode 100644
index 0000000..ab838e1
--- /dev/null
+++ b/libodb/odb/details/exception.hxx
@@ -0,0 +1,21 @@
+// file : odb/details/exception.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_EXCEPTION_HXX
+#define ODB_DETAILS_EXCEPTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/exception.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ struct exception: odb::exception {};
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_EXCEPTION_HXX
diff --git a/libodb/odb/details/export.hxx b/libodb/odb/details/export.hxx
new file mode 100644
index 0000000..80fe342
--- /dev/null
+++ b/libodb/odb/details/export.hxx
@@ -0,0 +1,78 @@
+// file : odb/details/export.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_EXPORT_HXX
+#define ODB_DETAILS_EXPORT_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/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_BUILD2
+
+#if defined(LIBODB_STATIC) // Using static.
+# define LIBODB_EXPORT
+#elif defined(LIBODB_STATIC_BUILD) // Building static.
+# define LIBODB_EXPORT
+#elif defined(LIBODB_SHARED) // Using shared.
+# ifdef _WIN32
+# define LIBODB_EXPORT __declspec(dllimport)
+# else
+# define LIBODB_EXPORT
+# endif
+#elif defined(LIBODB_SHARED_BUILD) // Building shared.
+# ifdef _WIN32
+# define LIBODB_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_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_EXPORT // Using static or shared.
+#endif
+
+#else // LIBODB_BUILD2
+
+#ifdef LIBODB_STATIC_LIB
+# define LIBODB_EXPORT
+#else
+# ifdef _WIN32
+# ifdef _MSC_VER
+# ifdef LIBODB_DYNAMIC_LIB
+# define LIBODB_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_EXPORT __declspec(dllimport)
+# endif
+# else
+# ifdef LIBODB_DYNAMIC_LIB
+# ifdef DLL_EXPORT
+# define LIBODB_EXPORT __declspec(dllexport)
+# else
+# define LIBODB_EXPORT
+# endif
+# else
+# define LIBODB_EXPORT __declspec(dllimport)
+# endif
+# endif
+# else
+# define LIBODB_EXPORT
+# endif
+#endif
+
+#endif // LIBODB_BUILD2
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_EXPORT_HXX
diff --git a/libodb/odb/details/function-wrapper.hxx b/libodb/odb/details/function-wrapper.hxx
new file mode 100644
index 0000000..418a625
--- /dev/null
+++ b/libodb/odb/details/function-wrapper.hxx
@@ -0,0 +1,90 @@
+// file : odb/details/function-wrapper.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_FUNCTION_WRAPPER_HXX
+#define ODB_DETAILS_FUNCTION_WRAPPER_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#ifdef ODB_CXX11
+# include <functional> // std::function
+# include <type_traits> // std::enable_if, std::is_convertible
+#endif
+
+namespace odb
+{
+ namespace details
+ {
+ // Low-level 'callable object' wrapper similar to std::function but
+ // that works in both C++98 and 11. In particular, the call site can
+ // be compiled in C++98 and the registration site in C++11 and it
+ // will work.
+ //
+ template <typename F>
+ struct function_wrapper
+ {
+ ~function_wrapper ();
+
+ explicit
+ function_wrapper (F* = 0);
+
+#ifdef ODB_CXX11
+ typedef typename std::function<F> std_function_type;
+
+ // This overload accepts lambdas and std::functions, but when the
+ // argument is convertible to F*, then we disable it in favor of the
+ // other overload (above), which is more efficient.
+ //
+ // Subtlety alert: if you're thinking of changing this to accept a
+ // std::function<F> argument, stop. That creates an overload ambiguity
+ // when the actual parameter is a lambda, which is convertible to either
+ // std::function<F> or F*.
+ //
+ template <typename F1>
+ function_wrapper(F1,
+ typename std::enable_if<
+ !std::is_convertible<F1, F*>::value>::type* = 0);
+#endif
+
+ // Destructive copy construction and assignment (aka move). These
+ // should really only be called by containers when they need to
+ // reallocate the underlying buffer and move the elements.
+ //
+ function_wrapper (const function_wrapper<F>&);
+ function_wrapper&
+ operator= (const function_wrapper<F>&);
+
+ void swap (function_wrapper<F>&);
+
+ // Cleanly cast to an incompatible function type.
+ //
+ template <typename R> R
+ cast () const;
+
+ // Conversion to bool.
+ //
+ public:
+ typedef void (function_wrapper<F>::*bool_convertible) ();
+ void true_value () {}
+
+ operator bool_convertible () const
+ {
+ return function != 0 ? &function_wrapper<F>::true_value : 0;
+ }
+
+ public:
+ F* function;
+ void (*deleter) (const void*);
+ const void* std_function;
+ };
+ }
+}
+
+#include <odb/details/function-wrapper.ixx>
+#include <odb/details/function-wrapper.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_FUNCTION_WRAPPER_HXX
diff --git a/libodb/odb/details/function-wrapper.ixx b/libodb/odb/details/function-wrapper.ixx
new file mode 100644
index 0000000..5b83b96
--- /dev/null
+++ b/libodb/odb/details/function-wrapper.ixx
@@ -0,0 +1,49 @@
+// file : odb/details/function-wrapper.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename F>
+ inline function_wrapper<F>::
+ ~function_wrapper ()
+ {
+ if (deleter != 0)
+ deleter (std_function);
+ }
+
+ template <typename F>
+ inline function_wrapper<F>::
+ function_wrapper (F* f)
+ : function (f), deleter (0), std_function (0)
+ {
+ }
+
+ template <typename F>
+ inline function_wrapper<F>::
+ function_wrapper (const function_wrapper<F>& x)
+ : function (0), deleter (0), std_function (0)
+ {
+ swap (const_cast<function_wrapper<F>&> (x));
+ }
+
+ template <typename F>
+ inline function_wrapper<F>& function_wrapper<F>::
+ operator= (const function_wrapper<F>& x)
+ {
+ swap (const_cast<function_wrapper<F>&> (x));
+ return *this;
+ }
+
+ template <typename F>
+ template <typename R>
+ inline R function_wrapper<F>::
+ cast () const
+ {
+ union { F* f; R r; } r;
+ r.f = function;
+ return r.r;
+ }
+ }
+}
diff --git a/libodb/odb/details/function-wrapper.txx b/libodb/odb/details/function-wrapper.txx
new file mode 100644
index 0000000..db73e8d
--- /dev/null
+++ b/libodb/odb/details/function-wrapper.txx
@@ -0,0 +1,89 @@
+// file : odb/details/function-wrapper.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <utility> // std::swap, std::move
+
+namespace odb
+{
+ namespace details
+ {
+#ifdef ODB_CXX11
+ template <typename F>
+ struct caller_impl;
+
+#ifdef ODB_CXX11_VARIADIC_TEMPLATE
+ template <typename R, typename... A>
+ struct caller_impl<R (A...)>
+ {
+ static R
+ function (const void* f, A... a)
+ {
+ return (*static_cast<const std::function<R (A...)>*> (f)) (a...);
+ }
+ };
+#else
+ template <typename R, typename A1>
+ struct caller_impl<R (A1)>
+ {
+ static R
+ function (const void* f, A1 a1)
+ {
+ return (*static_cast<const std::function<R (A1)>*> (f)) (a1);
+ }
+ };
+
+ template <typename R, typename A1, typename A2>
+ struct caller_impl<R (A1, A2)>
+ {
+ static R
+ function (const void* f, A1 a1, A2 a2)
+ {
+ return (*static_cast<const std::function<R (A1, A2)>*> (f)) (a1, a2);
+ }
+ };
+#endif
+
+ template <typename F>
+ void
+ deleter_impl (const void* f)
+ {
+ delete static_cast<const std::function<F>*> (f);
+ }
+
+ template <typename F>
+ template <typename F1>
+ function_wrapper<F>::
+ function_wrapper (
+ F1 f,
+ typename std::enable_if<!std::is_convertible<F1, F*>::value>::type*)
+ {
+ std_function_type sf (std::move (f));
+
+ if (F* const* const f = sf.template target<F*> ())
+ {
+ function = *f;
+ deleter = 0;
+ std_function = 0;
+ }
+ else
+ {
+ function_wrapper<decltype (caller_impl<F>::function)> fw (
+ &caller_impl<F>::function);
+
+ function = fw.template cast<F*> ();
+ deleter = &deleter_impl<F>;
+ std_function = new std_function_type (std::move (sf));
+ }
+ }
+#endif
+
+ template <typename F>
+ void function_wrapper<F>::
+ swap (function_wrapper<F>& x)
+ {
+ std::swap (function, x.function);
+ std::swap (deleter, x.deleter);
+ std::swap (std_function, x.std_function);
+ }
+ }
+}
diff --git a/libodb/odb/details/lock.cxx b/libodb/odb/details/lock.cxx
new file mode 100644
index 0000000..f474bf5
--- /dev/null
+++ b/libodb/odb/details/lock.cxx
@@ -0,0 +1,13 @@
+// file : odb/details/lock.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/lock.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ // This otherwise unnecessary file is here to allow instantiation
+ // of inline functions for exporting.
+ }
+}
diff --git a/libodb/odb/details/lock.hxx b/libodb/odb/details/lock.hxx
new file mode 100644
index 0000000..0c54f03
--- /dev/null
+++ b/libodb/odb/details/lock.hxx
@@ -0,0 +1,59 @@
+// file : odb/details/lock.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_LOCK_HXX
+#define ODB_DETAILS_LOCK_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/mutex.hxx>
+
+#ifdef ODB_THREADS_CXX11
+# include <mutex>
+namespace odb
+{
+ namespace details
+ {
+ using lock = std::unique_lock<mutex>;
+ }
+}
+#else
+namespace odb
+{
+ namespace details
+ {
+ class lock
+ {
+ public:
+ lock (mutex& m)
+ : mutex_ (&m)
+ {
+ mutex_->lock ();
+ }
+
+ ~lock ()
+ {
+ if (mutex_ != 0)
+ mutex_->unlock ();
+ }
+
+ void
+ unlock ()
+ {
+ if (mutex_ != 0)
+ {
+ mutex_->unlock ();
+ mutex_ = 0;
+ }
+ }
+
+ private:
+ mutex* mutex_;
+ };
+ }
+}
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_LOCK_HXX
diff --git a/libodb/odb/details/meta/answer.hxx b/libodb/odb/details/meta/answer.hxx
new file mode 100644
index 0000000..f15dc43
--- /dev/null
+++ b/libodb/odb/details/meta/answer.hxx
@@ -0,0 +1,30 @@
+// file : odb/details/meta/answer.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_ANSWER_HXX
+#define ODB_DETAILS_META_ANSWER_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ struct yes
+ {
+ char filling;
+ };
+
+ struct no
+ {
+ char filling[2];
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_ANSWER_HXX
diff --git a/libodb/odb/details/meta/class-p.hxx b/libodb/odb/details/meta/class-p.hxx
new file mode 100644
index 0000000..bddb452
--- /dev/null
+++ b/libodb/odb/details/meta/class-p.hxx
@@ -0,0 +1,34 @@
+// file : odb/details/meta/class-p.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_CLASS_HXX
+#define ODB_DETAILS_META_CLASS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/meta/answer.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ // g++ cannot have these inside class_p.
+ //
+ template <typename X> no class_p_test (...);
+ template <typename X> yes class_p_test (void (X::*) ());
+
+ template <typename X>
+ struct class_p
+ {
+ static const bool result =
+ sizeof (class_p_test<X> (0)) == sizeof (yes);
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_CLASS_HXX
diff --git a/libodb/odb/details/meta/polymorphic-p.hxx b/libodb/odb/details/meta/polymorphic-p.hxx
new file mode 100644
index 0000000..10fef6a
--- /dev/null
+++ b/libodb/odb/details/meta/polymorphic-p.hxx
@@ -0,0 +1,57 @@
+// file : odb/details/meta/polymorphic-p.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_POLYMORPHIC_HXX
+#define ODB_DETAILS_META_POLYMORPHIC_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+#include <odb/details/meta/class-p.hxx>
+#include <odb/details/meta/remove-const-volatile.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ template <typename CVX>
+ struct polymorphic_p
+ {
+ typedef typename remove_const_volatile<CVX>::result X;
+
+ template <typename Y, bool C>
+ struct impl
+ {
+ static const bool result = false;
+ };
+
+ template <typename Y>
+ struct impl<Y, true>
+ {
+ struct t1: Y
+ {
+ t1 ();
+ };
+
+ struct t2: Y
+ {
+ t2 ();
+
+ virtual
+ ~t2 () ODB_NOTHROW_NOEXCEPT;
+ };
+
+ static const bool result = sizeof (t1) == sizeof (t2);
+ };
+
+ static const bool result = impl<X, class_p<X>::result>::result;
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_POLYMORPHIC_HXX
diff --git a/libodb/odb/details/meta/remove-const-volatile.hxx b/libodb/odb/details/meta/remove-const-volatile.hxx
new file mode 100644
index 0000000..910ec35
--- /dev/null
+++ b/libodb/odb/details/meta/remove-const-volatile.hxx
@@ -0,0 +1,31 @@
+// file : odb/details/meta/remove-const-volatile.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_REMOVE_CONST_VOLATILE_HXX
+#define ODB_DETAILS_META_REMOVE_CONST_VOLATILE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/meta/remove-const.hxx>
+#include <odb/details/meta/remove-volatile.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ template <typename X>
+ struct remove_const_volatile
+ {
+ typedef
+ typename remove_volatile<typename remove_const<X>::result>::result
+ result;
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_REMOVE_CONST_VOLATILE_HXX
diff --git a/libodb/odb/details/meta/remove-const.hxx b/libodb/odb/details/meta/remove-const.hxx
new file mode 100644
index 0000000..4a92ed3
--- /dev/null
+++ b/libodb/odb/details/meta/remove-const.hxx
@@ -0,0 +1,32 @@
+// file : odb/details/meta/remove-const.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_REMOVE_CONST_HXX
+#define ODB_DETAILS_META_REMOVE_CONST_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ template <typename X>
+ struct remove_const
+ {
+ typedef X result;
+ };
+
+ template <typename X>
+ struct remove_const<const X>
+ {
+ typedef X result;
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_REMOVE_CONST_HXX
diff --git a/libodb/odb/details/meta/remove-pointer.hxx b/libodb/odb/details/meta/remove-pointer.hxx
new file mode 100644
index 0000000..9963fd7
--- /dev/null
+++ b/libodb/odb/details/meta/remove-pointer.hxx
@@ -0,0 +1,32 @@
+// file : odb/details/meta/remove-pointer.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_REMOVE_POINTER_HXX
+#define ODB_DETAILS_META_REMOVE_POINTER_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ template <typename X>
+ struct remove_pointer
+ {
+ typedef X result;
+ };
+
+ template <typename X>
+ struct remove_pointer<X*>
+ {
+ typedef X result;
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_REMOVE_POINTER_HXX
diff --git a/libodb/odb/details/meta/remove-volatile.hxx b/libodb/odb/details/meta/remove-volatile.hxx
new file mode 100644
index 0000000..877e532
--- /dev/null
+++ b/libodb/odb/details/meta/remove-volatile.hxx
@@ -0,0 +1,32 @@
+// file : odb/details/meta/remove-volatile.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_REMOVE_VOLATILE_HXX
+#define ODB_DETAILS_META_REMOVE_VOLATILE_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ template <typename X>
+ struct remove_volatile
+ {
+ typedef X result;
+ };
+
+ template <typename X>
+ struct remove_volatile<volatile X>
+ {
+ typedef X result;
+ };
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_REMOVE_VOLATILE_HXX
diff --git a/libodb/odb/details/meta/static-assert.hxx b/libodb/odb/details/meta/static-assert.hxx
new file mode 100644
index 0000000..a2cc81b
--- /dev/null
+++ b/libodb/odb/details/meta/static-assert.hxx
@@ -0,0 +1,32 @@
+// file : odb/details/meta/static-assert.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_META_STATIC_ASSERT_HXX
+#define ODB_DETAILS_META_STATIC_ASSERT_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#ifndef ODB_CXX11
+
+namespace odb
+{
+ namespace details
+ {
+ namespace meta
+ {
+ template <bool>
+ struct static_assert_test;
+
+ template <>
+ struct static_assert_test<true> {};
+ }
+ }
+}
+
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_META_STATIC_ASSERT_HXX
diff --git a/libodb/odb/details/mutex.cxx b/libodb/odb/details/mutex.cxx
new file mode 100644
index 0000000..df367d8
--- /dev/null
+++ b/libodb/odb/details/mutex.cxx
@@ -0,0 +1,13 @@
+// file : odb/details/mutex.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/mutex.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ // This otherwise unnecessary file is here to allow instantiation
+ // of inline functions for exporting.
+ }
+}
diff --git a/libodb/odb/details/mutex.hxx b/libodb/odb/details/mutex.hxx
new file mode 100644
index 0000000..df12013
--- /dev/null
+++ b/libodb/odb/details/mutex.hxx
@@ -0,0 +1,53 @@
+// file : odb/details/mutex.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_MUTEX_HXX
+#define ODB_DETAILS_MUTEX_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx>
+
+#ifdef ODB_THREADS_NONE
+
+namespace odb
+{
+ namespace details
+ {
+ class mutex
+ {
+ public:
+ mutex () {}
+
+ void
+ lock () {}
+
+ void
+ unlock () {}
+
+ private:
+ mutex (const mutex&);
+ mutex& operator= (const mutex&);
+ };
+ }
+}
+#elif defined(ODB_THREADS_CXX11)
+# include <mutex>
+namespace odb
+{
+ namespace details
+ {
+ using std::mutex;
+ }
+}
+#elif defined(ODB_THREADS_POSIX)
+#include <odb/details/posix/mutex.hxx>
+#elif defined(ODB_THREADS_WIN32)
+#include <odb/details/win32/mutex.hxx>
+#else
+# error unknown threading model
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_MUTEX_HXX
diff --git a/libodb/odb/details/posix/condition.hxx b/libodb/odb/details/posix/condition.hxx
new file mode 100644
index 0000000..4f7c42a
--- /dev/null
+++ b/libodb/odb/details/posix/condition.hxx
@@ -0,0 +1,47 @@
+// file : odb/details/posix/condition.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_POSIX_CONDITION_HXX
+#define ODB_DETAILS_POSIX_CONDITION_HXX
+
+#include <odb/pre.hxx>
+
+#include <pthread.h>
+
+#include <odb/details/export.hxx>
+#include <odb/details/posix/mutex.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class lock;
+
+ class LIBODB_EXPORT condition
+ {
+ public:
+ ~condition ();
+ condition (mutex&);
+
+ void
+ signal ();
+
+ void
+ wait (lock&);
+
+ private:
+ condition (const condition&);
+ condition& operator= (const condition&);
+
+ private:
+ mutex& mutex_;
+ pthread_cond_t cond_;
+ };
+ }
+}
+
+#include <odb/details/posix/condition.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_POSIX_CONDITION_HXX
diff --git a/libodb/odb/details/posix/condition.ixx b/libodb/odb/details/posix/condition.ixx
new file mode 100644
index 0000000..9b68d9f
--- /dev/null
+++ b/libodb/odb/details/posix/condition.ixx
@@ -0,0 +1,38 @@
+// file : odb/details/posix/condition.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/posix/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ inline condition::
+ ~condition ()
+ {
+ pthread_cond_destroy (&cond_);
+ }
+
+ inline condition::
+ condition (mutex& mutex)
+ : mutex_ (mutex)
+ {
+ if (int e = pthread_cond_init (&cond_, 0))
+ throw posix_exception (e);
+ }
+
+ inline void condition::
+ signal ()
+ {
+ if (int e = pthread_cond_signal (&cond_))
+ throw posix_exception (e);
+ }
+
+ inline void condition::
+ wait (lock&)
+ {
+ if (int e = pthread_cond_wait (&cond_, &mutex_.mutex_))
+ throw posix_exception (e);
+ }
+ }
+}
diff --git a/libodb/odb/details/posix/exceptions.cxx b/libodb/odb/details/posix/exceptions.cxx
new file mode 100644
index 0000000..c346655
--- /dev/null
+++ b/libodb/odb/details/posix/exceptions.cxx
@@ -0,0 +1,22 @@
+// file : odb/details/posix/exceptions.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/posix/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ const char* posix_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "POSIX API error";
+ }
+
+ posix_exception* posix_exception::
+ clone () const
+ {
+ return new posix_exception (*this);
+ }
+ }
+}
diff --git a/libodb/odb/details/posix/exceptions.hxx b/libodb/odb/details/posix/exceptions.hxx
new file mode 100644
index 0000000..aff33b6
--- /dev/null
+++ b/libodb/odb/details/posix/exceptions.hxx
@@ -0,0 +1,38 @@
+// file : odb/details/posix/exceptions.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_POSIX_EXCEPTIONS_HXX
+#define ODB_DETAILS_POSIX_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+#include <odb/details/export.hxx>
+#include <odb/details/exception.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ struct LIBODB_EXPORT posix_exception: details::exception
+ {
+ posix_exception (int code) : code_ (code) {}
+
+ int
+ code () const {return code_;}
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual posix_exception*
+ clone () const;
+
+ private:
+ int code_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_POSIX_EXCEPTIONS_HXX
diff --git a/libodb/odb/details/posix/mutex.hxx b/libodb/odb/details/posix/mutex.hxx
new file mode 100644
index 0000000..0cb94db
--- /dev/null
+++ b/libodb/odb/details/posix/mutex.hxx
@@ -0,0 +1,44 @@
+// file : odb/details/posix/mutex.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_POSIX_MUTEX_HXX
+#define ODB_DETAILS_POSIX_MUTEX_HXX
+
+#include <odb/pre.hxx>
+
+#include <pthread.h>
+
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT mutex
+ {
+ public:
+ ~mutex ();
+ mutex ();
+
+ void
+ lock ();
+
+ void
+ unlock ();
+
+ private:
+ mutex (const mutex&);
+ mutex& operator= (const mutex&);
+
+ private:
+ friend class condition;
+ pthread_mutex_t mutex_;
+ };
+ }
+}
+
+#include <odb/details/posix/mutex.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_POSIX_MUTEX_HXX
diff --git a/libodb/odb/details/posix/mutex.ixx b/libodb/odb/details/posix/mutex.ixx
new file mode 100644
index 0000000..ee73d09
--- /dev/null
+++ b/libodb/odb/details/posix/mutex.ixx
@@ -0,0 +1,37 @@
+// file : odb/details/posix/mutex.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/posix/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ inline mutex::
+ ~mutex ()
+ {
+ pthread_mutex_destroy (&mutex_);
+ }
+
+ inline mutex::
+ mutex ()
+ {
+ if (int e = pthread_mutex_init (&mutex_, 0))
+ throw posix_exception (e);
+ }
+
+ inline void mutex::
+ lock ()
+ {
+ if (int e = pthread_mutex_lock (&mutex_))
+ throw posix_exception (e);
+ }
+
+ inline void mutex::
+ unlock ()
+ {
+ if (int e = pthread_mutex_unlock (&mutex_))
+ throw posix_exception (e);
+ }
+ }
+}
diff --git a/libodb/odb/details/posix/thread.cxx b/libodb/odb/details/posix/thread.cxx
new file mode 100644
index 0000000..045f32a
--- /dev/null
+++ b/libodb/odb/details/posix/thread.cxx
@@ -0,0 +1,44 @@
+// file : odb/details/posix/thread.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/posix/thread.hxx>
+#include <odb/details/posix/exceptions.hxx>
+
+typedef void* (*thread_func) (void*);
+
+struct thread_data
+{
+ thread_func func;
+ void* arg;
+};
+
+extern "C" void*
+odb_thread_thunk (void* arg)
+{
+ thread_data* data (static_cast<thread_data*> (arg));
+ thread_func func = data->func;
+ arg = data->arg;
+ delete data;
+ return func (arg);
+}
+
+namespace odb
+{
+ namespace details
+ {
+ thread::
+ thread (void* (*func) (void*), void* arg)
+ : detached_ (false)
+ {
+ unique_ptr<thread_data> data (new thread_data);
+ data->func = func;
+ data->arg = arg;
+
+ if (int e = pthread_create (&id_, 0, &odb_thread_thunk, data.get ()))
+ throw posix_exception (e);
+
+ data.release (); // Thread thunk will free this.
+ }
+ }
+}
diff --git a/libodb/odb/details/posix/thread.hxx b/libodb/odb/details/posix/thread.hxx
new file mode 100644
index 0000000..f0d29a7
--- /dev/null
+++ b/libodb/odb/details/posix/thread.hxx
@@ -0,0 +1,41 @@
+// file : odb/details/posix/thread.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_POSIX_THREAD_HXX
+#define ODB_DETAILS_POSIX_THREAD_HXX
+
+#include <odb/pre.hxx>
+
+#include <pthread.h>
+
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT thread
+ {
+ public:
+ ~thread ();
+ thread (void* (*thread_func) (void*), void* arg = 0);
+
+ void*
+ join ();
+
+ private:
+ thread (const thread&);
+ thread& operator= (const thread&);
+
+ private:
+ bool detached_;
+ pthread_t id_;
+ };
+ }
+}
+
+#include <odb/details/posix/thread.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_POSIX_THREAD_HXX
diff --git a/libodb/odb/details/posix/thread.ixx b/libodb/odb/details/posix/thread.ixx
new file mode 100644
index 0000000..6576101
--- /dev/null
+++ b/libodb/odb/details/posix/thread.ixx
@@ -0,0 +1,29 @@
+// file : odb/details/posix/thread.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/posix/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ inline thread::
+ ~thread ()
+ {
+ if (!detached_)
+ pthread_detach (id_);
+ }
+
+ inline void* thread::
+ join ()
+ {
+ void* r;
+
+ if (int e = pthread_join (id_, &r))
+ throw posix_exception (e);
+
+ detached_ = true;
+ return r;
+ }
+ }
+}
diff --git a/libodb/odb/details/posix/tls.hxx b/libodb/odb/details/posix/tls.hxx
new file mode 100644
index 0000000..e868819
--- /dev/null
+++ b/libodb/odb/details/posix/tls.hxx
@@ -0,0 +1,106 @@
+// file : odb/details/posix/tls.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_POSIX_TLS_HXX
+#define ODB_DETAILS_POSIX_TLS_HXX
+
+#include <odb/pre.hxx>
+
+#include <pthread.h>
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ class tls
+ {
+ public:
+ tls ();
+
+ T&
+ get () const;
+
+ void
+ free ();
+
+ private:
+ tls (const tls&);
+ tls& operator= (const tls&);
+
+ private:
+ static void
+ key_init ();
+
+ static void
+ destructor (void*);
+
+ private:
+ static int error_;
+ static pthread_once_t once_;
+ static pthread_key_t key_;
+ };
+
+ template <typename T>
+ class tls<T*>
+ {
+ public:
+ tls ();
+
+ T*
+ get () const;
+
+ void
+ set (T* p);
+
+ private:
+ tls (const tls&);
+ tls& operator= (const tls&);
+
+ private:
+ static void
+ key_init ();
+
+ private:
+ static int error_;
+ static pthread_once_t once_;
+ static pthread_key_t key_;
+ };
+
+ template <typename T>
+ inline T&
+ tls_get (const tls<T>& t)
+ {
+ return t.get ();
+ }
+
+ template <typename T>
+ inline void
+ tls_free (tls<T>& t)
+ {
+ t.free ();
+ }
+
+ template <typename T>
+ inline T*
+ tls_get (const tls<T*>& t)
+ {
+ return t.get ();
+ }
+
+ template <typename T, typename T1>
+ inline void
+ tls_set (tls<T*>& t, T1* p1)
+ {
+ T* p (p1);
+ t.set (p);
+ }
+ }
+}
+
+#include <odb/details/posix/tls.ixx>
+#include <odb/details/posix/tls.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_POSIX_TLS_HXX
diff --git a/libodb/odb/details/posix/tls.ixx b/libodb/odb/details/posix/tls.ixx
new file mode 100644
index 0000000..7acc173
--- /dev/null
+++ b/libodb/odb/details/posix/tls.ixx
@@ -0,0 +1,20 @@
+// file : odb/details/posix/tls.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ inline tls<T>::
+ tls ()
+ {
+ }
+
+ template <typename T>
+ inline tls<T*>::
+ tls ()
+ {
+ }
+ }
+}
diff --git a/libodb/odb/details/posix/tls.txx b/libodb/odb/details/posix/tls.txx
new file mode 100644
index 0000000..e4c5b8f
--- /dev/null
+++ b/libodb/odb/details/posix/tls.txx
@@ -0,0 +1,121 @@
+// file : odb/details/posix/tls.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/posix/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ // tls<T>
+ //
+
+ template <typename T>
+ int tls<T>::error_ = 0;
+
+ template <typename T>
+ pthread_once_t tls<T>::once_ = PTHREAD_ONCE_INIT;
+
+ template <typename T>
+ pthread_key_t tls<T>::key_;
+
+ template <typename T>
+ T& tls<T>::
+ get () const
+ {
+ int e (pthread_once (&once_, key_init));
+
+ if (e != 0 || error_ != 0)
+ throw posix_exception (e ? e : error_);
+
+ if (void* v = pthread_getspecific (key_))
+ return *static_cast<T*> (v);
+
+ unique_ptr<T> p (new T);
+
+ if ((e = pthread_setspecific (key_, p.get ())))
+ throw posix_exception (e);
+
+ T& r (*p);
+ p.release ();
+ return r;
+ }
+
+ template <typename T>
+ void tls<T>::
+ free ()
+ {
+ int e (pthread_once (&once_, key_init));
+
+ if (e != 0 || error_ != 0)
+ throw posix_exception (e ? e : error_);
+
+ if (void* v = pthread_getspecific (key_))
+ {
+ if ((e = pthread_setspecific (key_, 0)))
+ throw posix_exception (e);
+
+ delete static_cast<T*> (v);
+ }
+ }
+
+ template <typename T>
+ void tls<T>::
+ key_init ()
+ {
+ error_ = pthread_key_create (&key_, destructor);
+ }
+
+ template <typename T>
+ void tls<T>::
+ destructor (void* v)
+ {
+ delete static_cast<T*> (v);
+ }
+
+ // tls<T*>
+ //
+
+ template <typename T>
+ int tls<T*>::error_ = 0;
+
+ template <typename T>
+ pthread_once_t tls<T*>::once_ = PTHREAD_ONCE_INIT;
+
+ template <typename T>
+ pthread_key_t tls<T*>::key_;
+
+ template <typename T>
+ T* tls<T*>::
+ get () const
+ {
+ int e (pthread_once (&once_, key_init));
+
+ if (e != 0 || error_ != 0)
+ throw posix_exception (e ? e : error_);
+
+ return static_cast<T*> (pthread_getspecific (key_));
+ }
+
+ template <typename T>
+ void tls<T*>::
+ set (T* p)
+ {
+ int e (pthread_once (&once_, key_init));
+
+ if (e != 0 || error_ != 0)
+ throw posix_exception (e ? e : error_);
+
+ if ((e = pthread_setspecific (key_, p)))
+ throw posix_exception (e);
+ }
+
+ template <typename T>
+ void tls<T*>::
+ key_init ()
+ {
+ error_ = pthread_key_create (&key_, 0);
+ }
+ }
+}
diff --git a/libodb/odb/details/shared-ptr-fwd.hxx b/libodb/odb/details/shared-ptr-fwd.hxx
new file mode 100644
index 0000000..73377b9
--- /dev/null
+++ b/libodb/odb/details/shared-ptr-fwd.hxx
@@ -0,0 +1,24 @@
+// file : odb/details/shared-ptr-fwd.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_SHARED_PTR_FWD_HXX
+#define ODB_DETAILS_SHARED_PTR_FWD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/shared-ptr/counter-type.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename X>
+ class shared_ptr;
+
+ class shared_base;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_SHARED_PTR_FWD_HXX
diff --git a/libodb/odb/details/shared-ptr.hxx b/libodb/odb/details/shared-ptr.hxx
new file mode 100644
index 0000000..5a1e842
--- /dev/null
+++ b/libodb/odb/details/shared-ptr.hxx
@@ -0,0 +1,167 @@
+// file : odb/details/shared-ptr.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_SHARED_PTR_HXX
+#define ODB_DETAILS_SHARED_PTR_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/shared-ptr-fwd.hxx>
+#include <odb/details/shared-ptr/base.hxx>
+#include <odb/details/shared-ptr/exception.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename X>
+ class shared_ptr:
+ private bits::counter_ops<typename bits::counter_type<X>::r, X>
+ {
+ typedef bits::counter_ops<typename bits::counter_type<X>::r, X> base;
+
+ public:
+ ~shared_ptr ()
+ {
+ if (x_ != 0)
+ base::dec (x_);
+ }
+
+ explicit
+ shared_ptr (X* x = 0)
+ : base (x), x_ (x)
+ {
+ }
+
+ shared_ptr (const shared_ptr& x)
+ : base (x), x_ (x.x_)
+ {
+ if (x_ != 0)
+ base::inc (x_);
+ }
+
+ template <typename Y>
+ shared_ptr (const shared_ptr<Y>& x)
+ : base (x), x_ (x.x_)
+ {
+ if (x_ != 0)
+ base::inc (x_);
+ }
+
+ shared_ptr&
+ operator= (const shared_ptr& x)
+ {
+ if (x_ != x.x_)
+ {
+ if (x_ != 0)
+ base::dec (x_);
+
+ static_cast<base&> (*this) = x;
+ x_ = x.x_;
+
+ if (x_ != 0)
+ base::inc (x_);
+ }
+
+ return *this;
+ }
+
+ template <typename Y>
+ shared_ptr&
+ operator= (const shared_ptr<Y>& x)
+ {
+ if (x_ != x.x_)
+ {
+ if (x_ != 0)
+ base::dec (x_);
+
+ static_cast<base&> (*this) = x;
+ x_ = x.x_;
+
+ if (x_ != 0)
+ base::inc (x_);
+ }
+
+ return *this;
+ }
+
+ public:
+ X*
+ operator-> () const
+ {
+ return x_;
+ }
+
+ X&
+ operator* () const
+ {
+ return *x_;
+ }
+
+ // Conversion to bool.
+ //
+ typedef void (shared_ptr::*boolean_convertible)();
+ void true_value () {}
+
+ operator boolean_convertible () const
+ {
+ return x_ ? &shared_ptr<X>::true_value : 0;
+ }
+
+ public:
+ X*
+ get () const
+ {
+ return x_;
+ }
+
+ X*
+ release ()
+ {
+ X* r (x_);
+ x_ = 0;
+ return r;
+ }
+
+ void
+ reset (X* x = 0)
+ {
+ if (x_ != 0)
+ base::dec (x_);
+
+ base::reset (x);
+ x_ = x;
+ }
+
+ std::size_t
+ count () const
+ {
+ return x_ != 0 ? base::count (x_) : 0;
+ }
+
+ private:
+ template <typename>
+ friend class shared_ptr;
+
+ X* x_;
+ };
+
+ template <typename X, typename Y>
+ inline bool
+ operator== (const shared_ptr<X>& x, const shared_ptr<Y>& y)
+ {
+ return x.get () == y.get ();
+ }
+
+ template <typename X, typename Y>
+ inline bool
+ operator!= (const shared_ptr<X>& x, const shared_ptr<Y>& y)
+ {
+ return x.get () != y.get ();
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_SHARED_PTR_HXX
diff --git a/libodb/odb/details/shared-ptr/base.cxx b/libodb/odb/details/shared-ptr/base.cxx
new file mode 100644
index 0000000..d937400
--- /dev/null
+++ b/libodb/odb/details/shared-ptr/base.cxx
@@ -0,0 +1,83 @@
+// file : odb/details/shared-ptr/base.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/shared-ptr/base.hxx>
+#include <odb/details/shared-ptr/exception.hxx>
+
+using std::size_t;
+
+namespace odb
+{
+ namespace details
+ {
+ share shared = share (1);
+ share exclusive = share (2);
+
+ const char* not_shared::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "object is not shared";
+ }
+
+ not_shared* not_shared::
+ clone () const
+ {
+ return new not_shared (*this);
+ }
+
+ namespace bits
+ {
+ size_t* locator_common::
+ counter (void* x)
+ {
+ size_t* p (static_cast<size_t*> (x));
+
+ if (*(--p) != 0xDEADBEEF)
+ throw not_shared ();
+
+ return --p;
+ }
+ }
+ }
+}
+
+void*
+#ifdef ODB_CXX11
+operator new (size_t n, odb::details::share s)
+#else
+operator new (size_t n, odb::details::share s) throw (std::bad_alloc)
+#endif
+{
+ if (s == odb::details::shared)
+ {
+ // Here we need to make sure we don't break the alignment of the
+ // returned block. For that we need to know the maximum alignment
+ // of this platform. Twice the pointer size is a good guess for
+ // most platforms.
+ //
+ size_t* p = static_cast<size_t*> (operator new (n + 2 * sizeof (size_t)));
+ *p++ = 1; // Initial count.
+ *p++ = 0xDEADBEEF; // Signature.
+ return p;
+ }
+ else
+ return operator new (n);
+
+}
+
+void
+operator delete (void* p, odb::details::share s) ODB_NOTHROW_NOEXCEPT
+{
+ // This version of operator delete is only called when the c-tor
+ // fails. In this case there is no object and we can just free the
+ // memory.
+ //
+ if (s == odb::details::shared)
+ {
+ size_t* sp = static_cast<size_t*> (p);
+ sp -= 2;
+ operator delete (sp);
+ }
+ else
+ operator delete (p);
+}
diff --git a/libodb/odb/details/shared-ptr/base.hxx b/libodb/odb/details/shared-ptr/base.hxx
new file mode 100644
index 0000000..8cd4c86
--- /dev/null
+++ b/libodb/odb/details/shared-ptr/base.hxx
@@ -0,0 +1,131 @@
+// file : odb/details/shared-ptr/base.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_SHARED_PTR_BASE_HXX
+#define ODB_DETAILS_SHARED_PTR_BASE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11, ODB_NOTHROW_NOEXCEPT
+
+#include <new>
+#include <cstddef> // std::size_t
+
+#ifdef ODB_CXX11
+#include <atomic>
+#endif
+
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr/counter-type.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ struct share
+ {
+ explicit
+ share (char id);
+
+ bool
+ operator== (share) const;
+
+ private:
+ char id_;
+ };
+
+ extern LIBODB_EXPORT share shared;
+ extern LIBODB_EXPORT share exclusive;
+ }
+}
+
+#ifdef ODB_CXX11
+LIBODB_EXPORT void*
+operator new (std::size_t, odb::details::share);
+#else
+LIBODB_EXPORT void*
+operator new (std::size_t, odb::details::share) throw (std::bad_alloc);
+#endif
+
+LIBODB_EXPORT void
+operator delete (void*, odb::details::share) ODB_NOTHROW_NOEXCEPT;
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT shared_base
+ {
+ public:
+ shared_base ();
+ shared_base (const shared_base&);
+ shared_base&
+ operator= (const shared_base&);
+
+ void
+ _inc_ref ();
+
+ bool
+ _dec_ref ();
+
+ std::size_t
+ _ref_count () const;
+
+#ifdef ODB_CXX11
+ void*
+ operator new (std::size_t);
+
+ void*
+ operator new (std::size_t, share);
+#else
+ void*
+ operator new (std::size_t) throw (std::bad_alloc);
+
+ void*
+ operator new (std::size_t, share) throw (std::bad_alloc);
+#endif
+
+ void
+ operator delete (void*, share) ODB_NOTHROW_NOEXCEPT;
+
+ void
+ operator delete (void*) ODB_NOTHROW_NOEXCEPT;
+
+ struct refcount_callback
+ {
+ void* arg;
+
+ // Return true if the object should be deleted, false otherwise.
+ //
+ bool (*zero_counter) (void*);
+ };
+
+ protected:
+#ifdef ODB_CXX11
+ std::atomic<std::size_t> counter_;
+#else
+ std::size_t counter_;
+#endif
+ refcount_callback* callback_;
+ };
+
+ template <typename X>
+ inline X*
+ inc_ref (X*);
+
+ template <typename X>
+ inline void
+ dec_ref (X*);
+
+ template <typename X>
+ inline std::size_t
+ ref_count (const X*);
+ }
+}
+
+#include <odb/details/shared-ptr/base.ixx>
+#include <odb/details/shared-ptr/base.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_SHARED_PTR_BASE_HXX
diff --git a/libodb/odb/details/shared-ptr/base.ixx b/libodb/odb/details/shared-ptr/base.ixx
new file mode 100644
index 0000000..1e2fd4b
--- /dev/null
+++ b/libodb/odb/details/shared-ptr/base.ixx
@@ -0,0 +1,119 @@
+// file : odb/details/shared-ptr/base.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace details
+ {
+ // share
+ //
+
+ inline share::
+ share (char id)
+ : id_ (id)
+ {
+ }
+
+ inline bool share::
+ operator== (share x) const
+ {
+ return id_ == x.id_;
+ }
+
+ // shared_base
+ //
+
+ inline shared_base::
+ shared_base ()
+ : counter_ (1), callback_ (0)
+ {
+ }
+
+ inline shared_base::
+ shared_base (const shared_base&)
+ : counter_ (1), callback_ (0)
+ {
+ }
+
+ inline shared_base& shared_base::
+ operator= (const shared_base&)
+ {
+ return *this;
+ }
+
+ inline void shared_base::
+ _inc_ref ()
+ {
+#ifdef ODB_CXX11
+ counter_.fetch_add (1, std::memory_order_relaxed);
+#else
+ ++counter_;
+#endif
+ }
+
+ inline bool shared_base::
+ _dec_ref ()
+ {
+ // While there are ways to avoid acquire (which is unnecessary except
+ // when the counter drops to zero), for our use-cases we'd rather keep
+ // it simple.
+ //
+ return
+#ifdef ODB_CXX11
+ counter_.fetch_sub (1, std::memory_order_acq_rel) == 1
+#else
+ --counter_ == 0
+#endif
+ ? callback_ == 0 || callback_->zero_counter (callback_->arg)
+ : false;
+ }
+
+ inline std::size_t shared_base::
+ _ref_count () const
+ {
+#ifdef ODB_CXX11
+ return counter_.load (std::memory_order_relaxed);
+#else
+ return counter_;
+#endif
+ }
+
+#ifdef ODB_CXX11
+ inline void* shared_base::
+ operator new (std::size_t n)
+ {
+ return ::operator new (n);
+ }
+
+ inline void* shared_base::
+ operator new (std::size_t n, share)
+ {
+ return ::operator new (n);
+ }
+#else
+ inline void* shared_base::
+ operator new (std::size_t n) throw (std::bad_alloc)
+ {
+ return ::operator new (n);
+ }
+
+ inline void* shared_base::
+ operator new (std::size_t n, share) throw (std::bad_alloc)
+ {
+ return ::operator new (n);
+ }
+#endif
+
+ inline void shared_base::
+ operator delete (void* p, share) ODB_NOTHROW_NOEXCEPT
+ {
+ ::operator delete (p);
+ }
+
+ inline void shared_base::
+ operator delete (void* p) ODB_NOTHROW_NOEXCEPT
+ {
+ ::operator delete (p);
+ }
+ }
+}
diff --git a/libodb/odb/details/shared-ptr/base.txx b/libodb/odb/details/shared-ptr/base.txx
new file mode 100644
index 0000000..77a957d
--- /dev/null
+++ b/libodb/odb/details/shared-ptr/base.txx
@@ -0,0 +1,198 @@
+// file : odb/details/shared-ptr/base.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/meta/answer.hxx>
+#include <odb/details/meta/polymorphic-p.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ namespace bits
+ {
+ // Support for locating the counter in the memory block.
+ //
+ struct LIBODB_EXPORT locator_common
+ {
+ static std::size_t*
+ counter (void*);
+ };
+
+ template <typename X, bool poly = meta::polymorphic_p<X>::result>
+ struct locator;
+
+ template <typename X>
+ struct locator<X, false>: locator_common
+ {
+ static std::size_t*
+ counter (X* x)
+ {
+ return locator_common::counter (x);
+ }
+ };
+
+ template <typename X>
+ struct locator<X, true>: locator_common
+ {
+ static std::size_t*
+ counter (X* x)
+ {
+ return locator_common::counter (dynamic_cast<void*> (x));
+ }
+ };
+
+ template <typename X>
+ std::size_t*
+ counter (const X* p)
+ {
+ return bits::locator<X>::counter (const_cast<X*> (p));
+ }
+
+ // Counter type and operations.
+ //
+ meta::no test (...);
+ meta::yes test (shared_base*);
+
+ template <typename X,
+ std::size_t A = sizeof (bits::test (reinterpret_cast<X*> (0)))>
+ struct counter_type;
+
+ template <typename X>
+ struct counter_type<X, sizeof (meta::no)>
+ {
+ typedef typename details::counter_type<X>::counter r;
+ };
+
+ template <typename X>
+ struct counter_type<X, sizeof (meta::yes)>
+ {
+ typedef shared_base r;
+ };
+
+ template <typename X, typename Y>
+ struct counter_ops;
+
+ template <typename X>
+ struct counter_ops<X, X>
+ {
+ counter_ops (const X* p) : counter_ (p ? bits::counter (p) : 0) {}
+ counter_ops (const counter_ops& x) : counter_ (x.counter_) {}
+
+ template <typename Z>
+ counter_ops (const counter_ops<Z, Z>& x) : counter_ (x.counter_) {}
+
+ counter_ops&
+ operator= (const counter_ops& x)
+ {
+ counter_ = x.counter_;
+ return *this;
+ }
+
+ template <typename Z>
+ counter_ops&
+ operator= (const counter_ops<Z, Z>& x)
+ {
+ counter_ = x.counter_;
+ return *this;
+ }
+
+ void
+ reset (const X* p)
+ {
+ counter_ = p ? bits::counter (p) : 0;
+ }
+
+ void
+ inc (X*)
+ {
+ (*counter_)++;
+ }
+
+ void
+ dec (X* p)
+ {
+ if (--(*counter_) == 0)
+ {
+ p->~X ();
+
+ // Counter is the top of the memory block.
+ //
+ operator delete (counter_);
+ }
+ }
+
+ std::size_t
+ count (const X*) const
+ {
+ return *counter_;
+ }
+
+ std::size_t* counter_;
+ };
+
+ template <typename Y>
+ struct counter_ops<shared_base, Y>
+ {
+ counter_ops (const Y*) {}
+ counter_ops (const counter_ops&) {}
+
+ template <typename Z>
+ counter_ops (const counter_ops<shared_base, Z>&) {}
+
+ counter_ops&
+ operator= (const counter_ops&)
+ {
+ return *this;
+ }
+
+ template <typename Z>
+ counter_ops&
+ operator= (const counter_ops<shared_base, Z>&)
+ {
+ return *this;
+ }
+
+ void
+ reset (const Y*) {}
+
+ void
+ inc (shared_base* p) {p->_inc_ref ();}
+
+ void
+ dec (Y* p)
+ {
+ if (static_cast<shared_base*> (p)->_dec_ref ())
+ delete p;
+ }
+
+ std::size_t
+ count (const shared_base* p) const {return p->_ref_count ();}
+ };
+ }
+
+ template <typename X>
+ inline X*
+ inc_ref (X* p)
+ {
+ bits::counter_ops<typename bits::counter_type<X>::r, X> c (p);
+ c.inc (p);
+ return p;
+ }
+
+ template <typename X>
+ inline void
+ dec_ref (X* p)
+ {
+ bits::counter_ops<typename bits::counter_type<X>::r, X> c (p);
+ c.dec (p);
+ }
+
+ template <typename X>
+ inline std::size_t
+ ref_count (const X* p)
+ {
+ bits::counter_ops<typename bits::counter_type<X>::r, X> c (p);
+ return c.count (p);
+ }
+ }
+}
diff --git a/libodb/odb/details/shared-ptr/counter-type.hxx b/libodb/odb/details/shared-ptr/counter-type.hxx
new file mode 100644
index 0000000..2b6caad
--- /dev/null
+++ b/libodb/odb/details/shared-ptr/counter-type.hxx
@@ -0,0 +1,23 @@
+// file : odb/details/shared-ptr/counter-type.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_SHARED_PTR_COUNTER_TYPE_HXX
+#define ODB_DETAILS_SHARED_PTR_COUNTER_TYPE_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename X>
+ struct counter_type
+ {
+ typedef X counter;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_SHARED_PTR_COUNTER_TYPE_HXX
diff --git a/libodb/odb/details/shared-ptr/exception.hxx b/libodb/odb/details/shared-ptr/exception.hxx
new file mode 100644
index 0000000..0ed50be
--- /dev/null
+++ b/libodb/odb/details/shared-ptr/exception.hxx
@@ -0,0 +1,31 @@
+// file : odb/details/shared-ptr/exception.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_SHARED_PTR_EXCEPTION_HXX
+#define ODB_DETAILS_SHARED_PTR_EXCEPTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/exception.hxx>
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ struct LIBODB_EXPORT not_shared: exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual not_shared*
+ clone () const;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_SHARED_PTR_EXCEPTION_HXX
diff --git a/libodb/odb/details/thread.cxx b/libodb/odb/details/thread.cxx
new file mode 100644
index 0000000..b1fbe42
--- /dev/null
+++ b/libodb/odb/details/thread.cxx
@@ -0,0 +1,22 @@
+// file : odb/details/thread.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/thread.hxx>
+
+// We might be compiled with ODB_THREADS_NONE.
+//
+#ifdef ODB_THREADS_CXX11
+
+namespace odb
+{
+ namespace details
+ {
+ void thread::
+ thunk (void* (*f) (void*), void* a, std::promise<void*> p)
+ {
+ p.set_value (f (a));
+ }
+ }
+}
+
+#endif
diff --git a/libodb/odb/details/thread.hxx b/libodb/odb/details/thread.hxx
new file mode 100644
index 0000000..9095f68
--- /dev/null
+++ b/libodb/odb/details/thread.hxx
@@ -0,0 +1,65 @@
+// file : odb/details/thread.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_THREAD_HXX
+#define ODB_DETAILS_THREAD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx>
+#include <odb/details/export.hxx>
+
+#ifdef ODB_THREADS_NONE
+# error no thread support available
+#elif defined(ODB_THREADS_CXX11)
+# include <thread>
+# include <future>
+# include <utility> // move()
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT thread
+ {
+ public:
+ thread (void* (*thread_func) (void*), void* arg = 0)
+ {
+ std::promise<void*> p;
+ f_ = p.get_future ();
+ t_ = std::thread (thunk, thread_func, arg, std::move (p));
+ }
+
+ void*
+ join ()
+ {
+ f_.wait ();
+ t_.join ();
+ return f_.get ();
+ }
+
+ thread (const thread&) = delete;
+ thread& operator= (const thread&) = delete;
+
+ private:
+ static void
+ thunk (void* (*) (void*), void*, std::promise<void*>);
+
+ private:
+ std::thread t_;
+ std::future<void*> f_;
+ };
+ }
+}
+
+#elif defined(ODB_THREADS_POSIX)
+#include <odb/details/posix/thread.hxx>
+#elif defined(ODB_THREADS_WIN32)
+#include <odb/details/win32/thread.hxx>
+#else
+# error unknown threading model
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_THREAD_HXX
diff --git a/libodb/odb/details/tls.hxx b/libodb/odb/details/tls.hxx
new file mode 100644
index 0000000..de6c344
--- /dev/null
+++ b/libodb/odb/details/tls.hxx
@@ -0,0 +1,168 @@
+// file : odb/details/tls.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_TLS_HXX
+#define ODB_DETAILS_TLS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx>
+
+#ifdef ODB_THREADS_NONE
+
+# define ODB_TLS_POINTER(type) type*
+# define ODB_TLS_OBJECT(type) type
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ inline T&
+ tls_get (T& x)
+ {
+ return x;
+ }
+
+ // If early destructions is possible, destroy the object and free
+ // any allocated resources.
+ //
+ template <typename T>
+ inline void
+ tls_free (T&)
+ {
+ }
+
+ template <typename T>
+ inline T*
+ tls_get (T* p)
+ {
+ return p;
+ }
+
+ template <typename T, typename T1>
+ inline void
+ tls_set (T*& rp, T1* p)
+ {
+ rp = p;
+ }
+ }
+}
+
+#elif defined(ODB_THREADS_CXX11)
+
+// Apparently Apple's Clang "temporarily disabled" C++11 thread_local until
+// they can implement a "fast" version, which reportedly happened in XCode 8.
+// So for now we will continue using __thread for this target.
+//
+# if defined(__apple_build_version__) && __apple_build_version__ < 8000000
+# define ODB_TLS_POINTER(type) __thread type*
+# define ODB_TLS_OBJECT(type) thread_local type
+# else
+# define ODB_TLS_POINTER(type) thread_local type*
+# define ODB_TLS_OBJECT(type) thread_local type
+# endif
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ inline T&
+ tls_get (T& x)
+ {
+ return x;
+ }
+
+ template <typename T>
+ inline void
+ tls_free (T&)
+ {
+ }
+
+ template <typename T>
+ inline T*
+ tls_get (T* p)
+ {
+ return p;
+ }
+
+ template <typename T, typename T1>
+ inline void
+ tls_set (T*& rp, T1* p)
+ {
+ rp = p;
+ }
+ }
+}
+
+#elif defined(ODB_THREADS_POSIX)
+
+# include <odb/details/posix/tls.hxx>
+
+# ifdef ODB_THREADS_TLS_KEYWORD
+# define ODB_TLS_POINTER(type) __thread type*
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ inline T*
+ tls_get (T* p)
+ {
+ return p;
+ }
+
+ template <typename T, typename T1>
+ inline void
+ tls_set (T*& rp, T1* p)
+ {
+ rp = p;
+ }
+ }
+}
+
+# else
+# define ODB_TLS_POINTER(type) tls<type*>
+# endif
+# define ODB_TLS_OBJECT(type) tls<type>
+
+#elif defined(ODB_THREADS_WIN32)
+
+# include <odb/details/win32/tls.hxx>
+
+# ifdef ODB_THREADS_TLS_DECLSPEC
+# define ODB_TLS_POINTER(type) __declspec(thread) type*
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ inline T*
+ tls_get (T* p)
+ {
+ return p;
+ }
+
+ template <typename T, typename T1>
+ inline void
+ tls_set (T*& rp, T1* p)
+ {
+ rp = p;
+ }
+ }
+}
+
+# else
+# define ODB_TLS_POINTER(type) tls<type*>
+# endif
+# define ODB_TLS_OBJECT(type) tls<type>
+#else
+# error unknown threading model
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_TLS_HXX
diff --git a/libodb/odb/details/transfer-ptr.hxx b/libodb/odb/details/transfer-ptr.hxx
new file mode 100644
index 0000000..4b63df6
--- /dev/null
+++ b/libodb/odb/details/transfer-ptr.hxx
@@ -0,0 +1,73 @@
+// file : odb/details/transfer-ptr.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_TRANSFER_PTR_HXX
+#define ODB_DETAILS_TRANSFER_PTR_HXX
+
+#include <odb/pre.hxx>
+
+#include <memory> // std::auto_ptr, std::unique_ptr
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ class transfer_ptr
+ {
+ public:
+ typedef T element_type;
+
+ transfer_ptr (): p_ (0) {}
+
+#ifndef ODB_CXX11
+ template <typename T1>
+ transfer_ptr (std::auto_ptr<T1> p): p_ (p.release ()) {}
+
+ private:
+ transfer_ptr& operator= (const transfer_ptr&);
+
+ public:
+ // In our usage transfer_ptr is always created implicitly and
+ // never const. So while this is not very clean, it is legal.
+ // Plus it will all go away once we drop C++98 (I can hardly
+ // wait).
+ //
+ transfer_ptr (const transfer_ptr& p)
+ : p_ (const_cast<transfer_ptr&> (p).transfer ()) {}
+#else
+#ifdef ODB_CXX11_NULLPTR
+ transfer_ptr (std::nullptr_t): p_ (0) {}
+#endif
+ template <typename T1>
+ transfer_ptr (std::unique_ptr<T1>&& p): p_ (p.release ()) {}
+
+ private:
+ transfer_ptr (const transfer_ptr&);
+ transfer_ptr& operator= (const transfer_ptr&);
+
+ public:
+ transfer_ptr (transfer_ptr&& p) noexcept: p_ (p.transfer ()) {}
+#endif
+
+ ~transfer_ptr () {delete p_;}
+
+ T*
+ transfer ()
+ {
+ T* r (p_);
+ p_ = 0;
+ return r;
+ }
+
+ private:
+ T* p_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_TRANSFER_PTR_HXX
diff --git a/libodb/odb/details/type-info.hxx b/libodb/odb/details/type-info.hxx
new file mode 100644
index 0000000..fe01699
--- /dev/null
+++ b/libodb/odb/details/type-info.hxx
@@ -0,0 +1,36 @@
+// file : odb/details/type-info.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_TYPE_INFO_HXX
+#define ODB_DETAILS_TYPE_INFO_HXX
+
+#include <odb/pre.hxx>
+
+#include <typeinfo>
+
+namespace odb
+{
+ namespace details
+ {
+ struct type_info_comparator
+ {
+ bool
+ operator() (const std::type_info* x, const std::type_info* y) const
+ {
+ // XL C++ on AIX has buggy type_info::before() in that
+ // it returns true for two different type_info objects
+ // that happened to be for the same type.
+ //
+#if defined(__xlC__) && defined(_AIX)
+ return *x != *y && x->before (*y);
+#else
+ return x->before (*y);
+#endif
+ }
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_TYPE_INFO_HXX
diff --git a/libodb/odb/details/unique-ptr.hxx b/libodb/odb/details/unique-ptr.hxx
new file mode 100644
index 0000000..06b2c76
--- /dev/null
+++ b/libodb/odb/details/unique-ptr.hxx
@@ -0,0 +1,95 @@
+// file : odb/details/unique-ptr.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_UNIQUE_PTR_HXX
+#define ODB_DETAILS_UNIQUE_PTR_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ class unique_ptr
+ {
+ public:
+ typedef T element_type;
+
+ explicit unique_ptr (T* p = 0): p_ (p) {}
+ ~unique_ptr () {delete p_;}
+
+#ifdef ODB_CXX11
+ unique_ptr (unique_ptr&& p) noexcept: p_ (p.p_) {p.p_ = 0;}
+ unique_ptr& operator= (unique_ptr&& p) noexcept
+ {
+ if (this != &p)
+ {
+ delete p_;
+ p_ = p.p_;
+ p.p_ = 0;
+ }
+ return *this;
+ }
+#endif
+
+ private:
+ unique_ptr (const unique_ptr&);
+ unique_ptr& operator= (const unique_ptr&);
+
+ public:
+ T*
+ operator-> () const {return p_;}
+
+ T&
+ operator* () const {return *p_;}
+
+ typedef T* unique_ptr::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return p_ != 0 ? &unique_ptr::p_ : 0;
+ }
+
+ T*
+ get () const {return p_;}
+
+ void
+ reset (T* p = 0)
+ {
+ delete p_;
+ p_ = p;
+ }
+
+ T*
+ release ()
+ {
+ T* r (p_);
+ p_ = 0;
+ return r;
+ }
+
+ private:
+ T* p_;
+ };
+
+ template <typename T1, typename T2>
+ inline bool
+ operator== (const unique_ptr<T1>& a, const unique_ptr<T2>& b)
+ {
+ return a.get () == b.get ();
+ }
+
+ template <typename T1, typename T2>
+ inline bool
+ operator!= (const unique_ptr<T1>& a, const unique_ptr<T2>& b)
+ {
+ return a.get () != b.get ();
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_UNIQUE_PTR_HXX
diff --git a/libodb/odb/details/unused.hxx b/libodb/odb/details/unused.hxx
new file mode 100644
index 0000000..8364c44
--- /dev/null
+++ b/libodb/odb/details/unused.hxx
@@ -0,0 +1,21 @@
+// file : odb/details/unused.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_UNUSED_DETAILS_HXX
+#define ODB_UNUSED_DETAILS_HXX
+
+#include <odb/pre.hxx>
+
+// VC++ and xlC don't like the (void)x expression if x is a reference
+// to an incomplete type. On the other hand, GCC warns that (void*)&x
+// doesn't have any effect.
+//
+#if defined(_MSC_VER) || defined(__xlC__)
+# define ODB_POTENTIALLY_UNUSED(x) (void*)&x
+#else
+# define ODB_POTENTIALLY_UNUSED(x) (void)x
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_UNUSED_DETAILS_HXX
diff --git a/libodb/odb/details/win32/condition.cxx b/libodb/odb/details/win32/condition.cxx
new file mode 100644
index 0000000..3a4b605
--- /dev/null
+++ b/libodb/odb/details/win32/condition.cxx
@@ -0,0 +1,54 @@
+// file : odb/details/win32/condition.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/windows.hxx>
+#include <odb/details/win32/condition.hxx>
+#include <odb/details/win32/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ void condition::
+ signal ()
+ {
+ mutex_.lock ();
+
+ if (waiters_ > signals_)
+ {
+ if (signals_++ == 0)
+ {
+ if (SetEvent (event_) == 0)
+ throw win32_exception ();
+ }
+ }
+
+ mutex_.unlock ();
+ }
+
+ void condition::
+ wait (lock&)
+ {
+ // When we enter this functions the mutex is locked. When we
+ // return from this function the mutex must be locked.
+ //
+ waiters_++;
+ mutex_.unlock ();
+
+ if (WaitForSingleObject (event_, INFINITE) != 0)
+ throw win32_exception ();
+
+ mutex_.lock ();
+ waiters_--;
+ signals_--;
+
+ if (signals_ > 0)
+ {
+ // Wake up the next thread.
+ //
+ if (SetEvent (event_) == 0)
+ throw win32_exception ();
+ }
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/condition.hxx b/libodb/odb/details/win32/condition.hxx
new file mode 100644
index 0000000..69972a0
--- /dev/null
+++ b/libodb/odb/details/win32/condition.hxx
@@ -0,0 +1,52 @@
+// file : odb/details/win32/condition.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_CONDITION_HXX
+#define ODB_DETAILS_WIN32_CONDITION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/win32/windows.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/details/export.hxx>
+#include <odb/details/win32/mutex.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class lock;
+
+ class LIBODB_EXPORT condition
+ {
+ public:
+ ~condition ();
+ condition (mutex&);
+
+ void
+ signal ();
+
+ void
+ wait (lock&);
+
+ private:
+ condition (const condition&);
+ condition& operator= (const condition&);
+
+ private:
+ mutex& mutex_;
+ HANDLE event_;
+
+ std::size_t waiters_;
+ std::size_t signals_;
+ };
+ }
+}
+
+#include <odb/details/win32/condition.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_CONDITION_HXX
diff --git a/libodb/odb/details/win32/condition.ixx b/libodb/odb/details/win32/condition.ixx
new file mode 100644
index 0000000..37a2bac
--- /dev/null
+++ b/libodb/odb/details/win32/condition.ixx
@@ -0,0 +1,30 @@
+// file : odb/details/win32/condition.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ inline condition::
+ ~condition ()
+ {
+ CloseHandle (event_);
+ }
+
+ inline condition::
+ condition (mutex& mutex)
+ : mutex_ (mutex), waiters_ (0), signals_ (0)
+ {
+ // Auto-reset event. Releases one waiting thread and automatically
+ // resets the event state. If no threads are waiting the event
+ // remains signalled.
+ //
+ event_ = CreateEvent (0, false, false, 0);
+
+ if (event_ == 0)
+ throw win32_exception ();
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/dll.cxx b/libodb/odb/details/win32/dll.cxx
new file mode 100644
index 0000000..49b660c
--- /dev/null
+++ b/libodb/odb/details/win32/dll.cxx
@@ -0,0 +1,51 @@
+// file : odb/details/win32/dll.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// If we are building a static library from VC++ (LIBODB_STATIC_LIB) or
+// a static library from automake (!DLL_EXPORT), then omit DllMain.
+//
+
+#if (defined(_MSC_VER) && defined(LIBODB_DYNAMIC_LIB)) || \
+ (!defined(_MSC_VER) && defined(DLL_EXPORT))
+
+#include <odb/details/win32/windows.hxx>
+#include <odb/details/win32/init.hxx>
+
+using namespace odb::details;
+
+extern "C" BOOL WINAPI
+DllMain (HINSTANCE, DWORD reason, LPVOID reserved)
+{
+ switch (reason)
+ {
+ case DLL_PROCESS_ATTACH:
+ {
+ process_start ();
+ thread_start ();
+ break;
+ }
+
+ case DLL_THREAD_ATTACH:
+ {
+ thread_start ();
+ break;
+ }
+
+ case DLL_THREAD_DETACH:
+ {
+ thread_end ();
+ break;
+ }
+
+ case DLL_PROCESS_DETACH:
+ {
+ thread_end ();
+ process_end (reserved == NULL);
+ break;
+ }
+ }
+
+ return 1;
+}
+
+#endif
diff --git a/libodb/odb/details/win32/exceptions.cxx b/libodb/odb/details/win32/exceptions.cxx
new file mode 100644
index 0000000..3cf11c2
--- /dev/null
+++ b/libodb/odb/details/win32/exceptions.cxx
@@ -0,0 +1,22 @@
+// file : odb/details/win32/exceptions.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ const char* win32_exception::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "Win32 API error";
+ }
+
+ win32_exception* win32_exception::
+ clone () const
+ {
+ return new win32_exception (*this);
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/exceptions.hxx b/libodb/odb/details/win32/exceptions.hxx
new file mode 100644
index 0000000..b61a447
--- /dev/null
+++ b/libodb/odb/details/win32/exceptions.hxx
@@ -0,0 +1,40 @@
+// file : odb/details/win32/exceptions.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_EXCEPTIONS_HXX
+#define ODB_DETAILS_WIN32_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+#include <odb/details/export.hxx>
+#include <odb/details/exception.hxx>
+#include <odb/details/win32/windows.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ struct LIBODB_EXPORT win32_exception: details::exception
+ {
+ win32_exception () : code_ (GetLastError ()) {}
+ win32_exception (DWORD code) : code_ (code) {}
+
+ DWORD
+ code () const {return code_;}
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual win32_exception*
+ clone () const;
+
+ private:
+ DWORD code_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_EXCEPTIONS_HXX
diff --git a/libodb/odb/details/win32/init.cxx b/libodb/odb/details/win32/init.cxx
new file mode 100644
index 0000000..f6e0f9a
--- /dev/null
+++ b/libodb/odb/details/win32/init.cxx
@@ -0,0 +1,41 @@
+// file : odb/details/win32/init.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/init.hxx>
+#include <odb/details/win32/once-init.hxx>
+#include <odb/details/win32/tls-init.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ void
+ process_start ()
+ {
+ // The order is important.
+ //
+ once_process_start ();
+ tls_process_start ();
+ }
+
+ void
+ process_end (bool safe)
+ {
+ // The order is important.
+ //
+ tls_process_end (safe);
+ once_process_end (safe);
+ }
+
+ void
+ thread_start ()
+ {
+ }
+
+ void
+ thread_end ()
+ {
+ tls_thread_end ();
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/init.hxx b/libodb/odb/details/win32/init.hxx
new file mode 100644
index 0000000..1c15ffd
--- /dev/null
+++ b/libodb/odb/details/win32/init.hxx
@@ -0,0 +1,36 @@
+// file : odb/details/win32/init.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_INIT_HXX
+#define ODB_DETAILS_WIN32_INIT_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ void
+ process_start ();
+
+ // The safe parameter indicates whether it is safe to free heap objects.
+ // If the process is terminated by a call to ExitProcess(), some threads
+ // might have been killed leaving things in inconsistent state.
+ //
+ void
+ process_end (bool safe = true);
+
+ void
+ thread_start ();
+
+ // This function may be called even for thread for which thread_start()
+ // hasn't been called.
+ //
+ void
+ thread_end ();
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_INIT_HXX
diff --git a/libodb/odb/details/win32/lock.hxx b/libodb/odb/details/win32/lock.hxx
new file mode 100644
index 0000000..2e81ac6
--- /dev/null
+++ b/libodb/odb/details/win32/lock.hxx
@@ -0,0 +1,49 @@
+// file : odb/details/win32/lock.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_LOCK_HXX
+#define ODB_DETAILS_WIN32_LOCK_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/win32/windows.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ // Critical section lock. Not exported; for internal use only.
+ //
+ struct win32_lock
+ {
+ win32_lock (CRITICAL_SECTION& cs)
+ : cs_ (&cs)
+ {
+ EnterCriticalSection (cs_);
+ }
+
+ ~win32_lock ()
+ {
+ if (cs_ != 0)
+ LeaveCriticalSection (cs_);
+ }
+
+ void
+ unlock ()
+ {
+ if (cs_ != 0)
+ {
+ LeaveCriticalSection (cs_);
+ cs_ = 0;
+ }
+ }
+
+ private:
+ CRITICAL_SECTION* cs_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_LOCK_HXX
diff --git a/libodb/odb/details/win32/mutex.hxx b/libodb/odb/details/win32/mutex.hxx
new file mode 100644
index 0000000..b2cd997
--- /dev/null
+++ b/libodb/odb/details/win32/mutex.hxx
@@ -0,0 +1,43 @@
+// file : odb/details/win32/mutex.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_MUTEX_HXX
+#define ODB_DETAILS_WIN32_MUTEX_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/win32/windows.hxx>
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT mutex
+ {
+ public:
+ ~mutex ();
+ mutex ();
+
+ void
+ lock ();
+
+ void
+ unlock ();
+
+ private:
+ mutex (const mutex&);
+ mutex& operator= (const mutex&);
+
+ private:
+ friend class condition;
+ CRITICAL_SECTION cs_;
+ };
+ }
+}
+
+#include <odb/details/win32/mutex.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_MUTEX_HXX
diff --git a/libodb/odb/details/win32/mutex.ixx b/libodb/odb/details/win32/mutex.ixx
new file mode 100644
index 0000000..bb06415
--- /dev/null
+++ b/libodb/odb/details/win32/mutex.ixx
@@ -0,0 +1,32 @@
+// file : odb/details/win32/mutex.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace details
+ {
+ inline mutex::
+ ~mutex ()
+ {
+ DeleteCriticalSection (&cs_);
+ }
+
+ inline mutex::
+ mutex ()
+ {
+ InitializeCriticalSection (&cs_);
+ }
+
+ inline void mutex::
+ lock ()
+ {
+ EnterCriticalSection (&cs_);
+ }
+
+ inline void mutex::
+ unlock ()
+ {
+ LeaveCriticalSection (&cs_);
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/once-init.hxx b/libodb/odb/details/win32/once-init.hxx
new file mode 100644
index 0000000..a465c90
--- /dev/null
+++ b/libodb/odb/details/win32/once-init.hxx
@@ -0,0 +1,23 @@
+// file : odb/details/win32/once-init.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_ONCE_INIT_HXX
+#define ODB_DETAILS_WIN32_ONCE_INIT_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ void
+ once_process_start ();
+
+ void
+ once_process_end (bool safe);
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_ONCE_INIT_HXX
diff --git a/libodb/odb/details/win32/once.cxx b/libodb/odb/details/win32/once.cxx
new file mode 100644
index 0000000..7b98d80
--- /dev/null
+++ b/libodb/odb/details/win32/once.cxx
@@ -0,0 +1,26 @@
+// file : odb/details/win32/once.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/windows.hxx>
+#include <odb/details/win32/once.hxx>
+#include <odb/details/win32/once-init.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ CRITICAL_SECTION win32_once_cs_;
+
+ void
+ once_process_start ()
+ {
+ InitializeCriticalSection (&win32_once_cs_);
+ }
+
+ void
+ once_process_end (bool)
+ {
+ DeleteCriticalSection (&win32_once_cs_);
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/once.hxx b/libodb/odb/details/win32/once.hxx
new file mode 100644
index 0000000..45748b8
--- /dev/null
+++ b/libodb/odb/details/win32/once.hxx
@@ -0,0 +1,50 @@
+// file : odb/details/win32/once.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_ONCE_HXX
+#define ODB_DETAILS_WIN32_ONCE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/win32/windows.hxx>
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT once
+ {
+ public:
+ once ();
+
+ void
+ call (void (*func) ());
+
+ private:
+ once (const once&);
+ once& operator= (const once&);
+
+ private:
+ bool called_;
+ };
+
+ // Low-level, POSIX-like API that can be used safely during static
+ // initialization (that is, win32_once() can be called during static
+ // initialization) provided once_process_start() has been called.
+ //
+ typedef unsigned int win32_once_t;
+ const win32_once_t WIN32_ONCE_INIT = 0;
+
+ LIBODB_EXPORT void
+ win32_once (win32_once_t&, void (*func) ());
+
+ extern LIBODB_EXPORT CRITICAL_SECTION win32_once_cs_;
+ }
+}
+
+#include <odb/details/win32/once.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_ONCE_HXX
diff --git a/libodb/odb/details/win32/once.ixx b/libodb/odb/details/win32/once.ixx
new file mode 100644
index 0000000..1638706
--- /dev/null
+++ b/libodb/odb/details/win32/once.ixx
@@ -0,0 +1,42 @@
+// file : odb/details/win32/once.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/lock.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ inline void
+ win32_once (win32_once_t& o, void (*func) ())
+ {
+ win32_lock l (win32_once_cs_);
+
+ if (o == 0)
+ {
+ o = 1;
+ l.unlock ();
+ func ();
+ }
+ }
+
+ inline once::
+ once ()
+ : called_ (false)
+ {
+ }
+
+ inline void once::
+ call (void (*func) ())
+ {
+ win32_lock l (win32_once_cs_);
+
+ if (!called_)
+ {
+ called_ = true;
+ l.unlock ();
+ func ();
+ }
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/thread.cxx b/libodb/odb/details/win32/thread.cxx
new file mode 100644
index 0000000..46720d4
--- /dev/null
+++ b/libodb/odb/details/win32/thread.cxx
@@ -0,0 +1,88 @@
+// file : odb/details/win32/thread.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/windows.hxx>
+#include <process.h> // _beginthreadex, _endthreadex
+
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/win32/thread.hxx>
+#include <odb/details/win32/exceptions.hxx>
+
+unsigned int __stdcall
+odb_thread_thunk (void* arg)
+{
+ odb::details::thread::thread_thunk (arg);
+ _endthreadex (0);
+ return 0;
+}
+
+namespace odb
+{
+ namespace details
+ {
+ void thread::
+ thread_thunk (void* arg)
+ {
+ data* d (static_cast<data*> (arg));
+ d->ret = d->func (d->arg);
+ d->mutex.lock ();
+ unsigned char count = --d->count;
+ d->mutex.unlock ();
+
+ if (count == 0)
+ delete d;
+ }
+
+ thread::
+ ~thread ()
+ {
+ if (handle_ != 0)
+ {
+ CloseHandle (handle_);
+
+ // Win32 mutex implementation does not throw.
+ //
+ data_->mutex.lock ();
+ unsigned char count = --data_->count;
+ data_->mutex.unlock ();
+
+ if (count == 0)
+ delete data_;
+ }
+ }
+
+ thread::
+ thread (void* (*func) (void*), void* arg)
+ {
+ unique_ptr<data> d (new data);
+ d->func = func;
+ d->arg = arg;
+ d->count = 2; // One for the thread and one for us.
+
+ handle_ = (HANDLE)_beginthreadex (
+ 0, 0, &odb_thread_thunk, d.get (), 0, 0);
+
+ if (handle_ == 0)
+ throw win32_exception ();
+
+ data_ = d.release ();
+ }
+
+ void* thread::
+ join ()
+ {
+ void* r;
+
+ if (WaitForSingleObject (handle_, INFINITE) != 0)
+ throw win32_exception ();
+
+ r = data_->ret;
+
+ CloseHandle (handle_);
+ delete data_;
+ handle_ = 0;
+ data_ = 0;
+ return r;
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/thread.hxx b/libodb/odb/details/win32/thread.hxx
new file mode 100644
index 0000000..a4e1a15
--- /dev/null
+++ b/libodb/odb/details/win32/thread.hxx
@@ -0,0 +1,59 @@
+// file : odb/details/win32/thread.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_THREAD_HXX
+#define ODB_DETAILS_WIN32_THREAD_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/win32/windows.hxx>
+#include <odb/details/export.hxx>
+#include <odb/details/win32/mutex.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT thread
+ {
+ public:
+ ~thread ();
+ thread (void* (*thread_func) (void*), void* arg = 0);
+
+ void*
+ join ();
+
+ private:
+ thread (const thread&);
+ thread& operator= (const thread&);
+
+ private:
+ typedef void* (*thread_func) (void*);
+
+ struct data
+ {
+ thread_func func;
+ void* arg;
+ void* ret;
+
+ // Thread-safe reference counter.
+ //
+ details::mutex mutex;
+ unsigned char count;
+ };
+
+
+ public:
+ static void
+ thread_thunk (void*);
+
+ private:
+ HANDLE handle_;
+ data* data_;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_THREAD_HXX
diff --git a/libodb/odb/details/win32/tls-init.hxx b/libodb/odb/details/win32/tls-init.hxx
new file mode 100644
index 0000000..0a44a10
--- /dev/null
+++ b/libodb/odb/details/win32/tls-init.hxx
@@ -0,0 +1,26 @@
+// file : odb/details/win32/tls-init.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_TLS_INIT_HXX
+#define ODB_DETAILS_WIN32_TLS_INIT_HXX
+
+#include <odb/pre.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ void
+ tls_process_start ();
+
+ void
+ tls_process_end (bool safe);
+
+ void
+ tls_thread_end ();
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_TLS_INIT_HXX
diff --git a/libodb/odb/details/win32/tls.cxx b/libodb/odb/details/win32/tls.cxx
new file mode 100644
index 0000000..2edc364
--- /dev/null
+++ b/libodb/odb/details/win32/tls.cxx
@@ -0,0 +1,245 @@
+// file : odb/details/win32/tls.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/win32/windows.hxx>
+#include <winerror.h> // ERROR_INVALID_INDEX
+
+#include <new>
+#include <cstddef> // std::size_t
+
+#include <odb/details/win32/lock.hxx>
+#include <odb/details/win32/tls.hxx>
+#include <odb/details/win32/tls-init.hxx>
+#include <odb/details/win32/exceptions.hxx>
+
+#ifdef _MSC_VER
+# pragma warning (disable:4200) // zero-sized array in struct
+#endif
+
+using namespace std;
+
+namespace odb
+{
+ namespace details
+ {
+ typedef void (*dtor_func) (void*);
+
+ struct entry
+ {
+ void* value;
+ dtor_func dtor;
+ };
+
+ struct thread_data
+ {
+ size_t size;
+ size_t capacity;
+ entry entries[0];
+ };
+
+ struct process_data
+ {
+ size_t size;
+ size_t capacity;
+ dtor_func dtors[0];
+ };
+
+ static DWORD index_ = TLS_OUT_OF_INDEXES;
+ static CRITICAL_SECTION cs_;
+ static process_data* proc_data_;
+
+ const size_t init_capacity = 4;
+
+ void
+ tls_process_start ()
+ {
+ index_ = TlsAlloc ();
+
+ if (index_ == TLS_OUT_OF_INDEXES)
+ throw win32_exception ();
+
+ InitializeCriticalSection (&cs_);
+
+ process_data* pd (
+ static_cast<process_data*> (
+ operator new (
+ sizeof (process_data) + sizeof (dtor_func) * init_capacity)));
+
+ pd->size = 0;
+ pd->capacity = init_capacity;
+ memset (pd->dtors, 0, sizeof (dtor_func) * init_capacity);
+
+ proc_data_ = pd;
+ }
+
+ void
+ tls_process_end (bool)
+ {
+ operator delete (proc_data_);
+ DeleteCriticalSection (&cs_);
+
+ if (index_ != TLS_OUT_OF_INDEXES)
+ {
+ if (!TlsFree (index_))
+ throw win32_exception ();
+ }
+ }
+
+ void
+ tls_thread_end ()
+ {
+ if (thread_data* d = static_cast<thread_data*> (TlsGetValue (index_)))
+ {
+ // Call destructors. Implement the pthread semantics in that the
+ // destructors are called until all the values become 0.
+ //
+ for (bool pass (true); pass;)
+ {
+ pass = false;
+
+ for (size_t i (0); i < d->size; ++i)
+ {
+ if (d->entries[i].dtor != 0 && d->entries[i].value != 0)
+ {
+ pass = true;
+ void* tmp (d->entries[i].value);
+ d->entries[i].value = 0;
+ d->entries[i].dtor (tmp);
+ }
+ }
+ }
+
+ operator delete (d);
+ }
+ }
+
+ //
+ // tls_common
+ //
+
+ std::size_t tls_common::
+ _allocate (dtor_func dtor)
+ {
+ win32_lock l (cs_);
+
+ size_t n (proc_data_->size);
+ size_t c (proc_data_->capacity);
+
+ if (n == c)
+ {
+ c *= 2;
+
+ // Try to do "atomic" switch-over so that proc_data_ always points
+ // to memory that can be freed even if this thread is killed in the
+ // middle.
+ //
+ process_data* pd (
+ static_cast<process_data*> (
+ operator new (sizeof (process_data) + sizeof (dtor_func) * c)));
+
+ memcpy (pd->dtors, proc_data_->dtors, n * sizeof (dtor_func));
+ memset (pd->dtors + n, 0, sizeof (dtor_func) * (c - n));
+
+ pd->size = n;
+ pd->capacity = c;
+
+ process_data* old (proc_data_);
+ proc_data_ = pd;
+ operator delete (old);
+ }
+
+ proc_data_->dtors[n] = dtor;
+ return proc_data_->size++;
+ }
+
+ void* tls_common::
+ _get (std::size_t key)
+ {
+ if (thread_data* d = static_cast<thread_data*> (TlsGetValue (index_)))
+ {
+ if (key < d->size)
+ return d->entries[key].value;
+ }
+
+ // Check if this key is valid.
+ //
+ win32_lock l (cs_);
+
+ if (key < proc_data_->size)
+ return 0;
+
+ throw win32_exception (ERROR_INVALID_INDEX);
+ }
+
+ void tls_common::
+ _set (std::size_t key, void* value)
+ {
+ thread_data* d (static_cast<thread_data*> (TlsGetValue (index_)));
+
+ if (d != 0 && key < d->capacity)
+ {
+ if (key >= d->size)
+ {
+ // Check if this key is valid. If so then we need to copy
+ // dtors for new slots.
+ //
+ win32_lock l (cs_);
+
+ size_t n (proc_data_->size);
+
+ if (key >= n)
+ throw win32_exception (ERROR_INVALID_INDEX);
+
+ for (size_t i (d->size); i < n; ++i)
+ d->entries[i].dtor = proc_data_->dtors[i];
+
+ d->size = n;
+ }
+
+ d->entries[key].value = value;
+ }
+ else
+ {
+ // Check if this key is valid. If so then we need to (re)-allocate
+ // our storage.
+ //
+ win32_lock l (cs_);
+
+ size_t n (proc_data_->size);
+
+ if (key >= n)
+ throw win32_exception (ERROR_INVALID_INDEX);
+
+ size_t c (proc_data_->capacity);
+
+ thread_data* nd (
+ static_cast<thread_data*> (
+ operator new (sizeof (thread_data) + sizeof (entry) * c)));
+
+ size_t on (d == 0 ? 0 : d->size);
+
+ // Copy over the data.
+ //
+ if (on != 0)
+ memcpy (nd->entries, d->entries, sizeof (entry) * on);
+
+ // Zero out the rest.
+ //
+ memset (nd->entries + on, 0, sizeof (entry) * (c - on));
+
+ // Assign destructors to new slots [on, n).
+ //
+ for (size_t i (on); i < n; ++i)
+ nd->entries[i].dtor = proc_data_->dtors[i];
+
+ nd->size = n;
+ nd->capacity = c;
+
+ operator delete (d);
+ TlsSetValue (index_, nd);
+
+ nd->entries[key].value = value;
+ }
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/tls.hxx b/libodb/odb/details/win32/tls.hxx
new file mode 100644
index 0000000..2a75cc8
--- /dev/null
+++ b/libodb/odb/details/win32/tls.hxx
@@ -0,0 +1,120 @@
+// file : odb/details/win32/tls.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_TLS_HXX
+#define ODB_DETAILS_WIN32_TLS_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/details/export.hxx>
+#include <odb/details/win32/once.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ class LIBODB_EXPORT tls_common
+ {
+ public:
+ static std::size_t
+ _allocate (void (*dtor) (void*));
+
+ static void*
+ _get (std::size_t key);
+
+ static void
+ _set (std::size_t key, void* value);
+ };
+
+ template <typename T>
+ class tls: protected tls_common
+ {
+ public:
+ tls ();
+
+ T&
+ get () const;
+
+ void
+ free ();
+
+ private:
+ tls (const tls&);
+ tls& operator= (const tls&);
+
+ private:
+ static void
+ key_init ();
+
+ static void
+ destructor (void*);
+
+ private:
+ static win32_once_t once_;
+ static std::size_t key_;
+ };
+
+ template <typename T>
+ class tls<T*>: protected tls_common
+ {
+ public:
+ tls ();
+
+ T*
+ get () const;
+
+ void
+ set (T* p);
+
+ private:
+ tls (const tls&);
+ tls& operator= (const tls&);
+
+ private:
+ static void
+ key_init ();
+
+ private:
+ static win32_once_t once_;
+ static std::size_t key_;
+ };
+
+ template <typename T>
+ inline T&
+ tls_get (const tls<T>& t)
+ {
+ return t.get ();
+ }
+
+ template <typename T>
+ inline void
+ tls_free (tls<T>& t)
+ {
+ t.free ();
+ }
+
+ template <typename T>
+ inline T*
+ tls_get (const tls<T*>& t)
+ {
+ return t.get ();
+ }
+
+ template <typename T, typename T1>
+ inline void
+ tls_set (tls<T*>& t, T1* p1)
+ {
+ T* p (p1);
+ t.set (p);
+ }
+ }
+}
+
+#include <odb/details/win32/tls.ixx>
+#include <odb/details/win32/tls.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_TLS_HXX
diff --git a/libodb/odb/details/win32/tls.ixx b/libodb/odb/details/win32/tls.ixx
new file mode 100644
index 0000000..fbcc3dd
--- /dev/null
+++ b/libodb/odb/details/win32/tls.ixx
@@ -0,0 +1,20 @@
+// file : odb/details/win32/tls.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace details
+ {
+ template <typename T>
+ inline tls<T>::
+ tls ()
+ {
+ }
+
+ template <typename T>
+ inline tls<T*>::
+ tls ()
+ {
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/tls.txx b/libodb/odb/details/win32/tls.txx
new file mode 100644
index 0000000..96bed4c
--- /dev/null
+++ b/libodb/odb/details/win32/tls.txx
@@ -0,0 +1,94 @@
+// file : odb/details/win32/tls.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/details/unique-ptr.hxx>
+#include <odb/details/win32/exceptions.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ // tls<T>
+ //
+ template <typename T>
+ win32_once_t tls<T>::once_= WIN32_ONCE_INIT;
+
+ template <typename T>
+ size_t tls<T>::key_;
+
+ template <typename T>
+ T& tls<T>::
+ get () const
+ {
+ win32_once (once_, key_init);
+
+ if (void* v = _get (key_))
+ return *static_cast<T*> (v);
+
+ unique_ptr<T> p (new T);
+ _set (key_, p.get ());
+
+ T& r (*p);
+ p.release ();
+ return r;
+ }
+
+ template <typename T>
+ void tls<T>::
+ free ()
+ {
+ win32_once (once_, key_init);
+
+ if (void* v = _get (key_))
+ {
+ _set (key_, 0);
+ delete static_cast<T*> (v);
+ }
+ }
+
+ template <typename T>
+ void tls<T>::
+ key_init ()
+ {
+ key_ = _allocate (destructor);
+ }
+
+ template <typename T>
+ void tls<T>::
+ destructor (void* v)
+ {
+ delete static_cast<T*> (v);
+ }
+
+ // tls<T*>
+ //
+ template <typename T>
+ win32_once_t tls<T*>::once_ = WIN32_ONCE_INIT;
+
+ template <typename T>
+ size_t tls<T*>::key_;
+
+ template <typename T>
+ T* tls<T*>::
+ get () const
+ {
+ win32_once (once_, key_init);
+ return static_cast<T*> (_get (key_));
+ }
+
+ template <typename T>
+ void tls<T*>::
+ set (T* p)
+ {
+ win32_once (once_, key_init);
+ _set (key_, p);
+ }
+
+ template <typename T>
+ void tls<T*>::
+ key_init ()
+ {
+ key_ = _allocate (0);
+ }
+ }
+}
diff --git a/libodb/odb/details/win32/windows.hxx b/libodb/odb/details/win32/windows.hxx
new file mode 100644
index 0000000..9ff4cb4
--- /dev/null
+++ b/libodb/odb/details/win32/windows.hxx
@@ -0,0 +1,33 @@
+// file : odb/details/win32/windows.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WIN32_WINDOWS_HXX
+#define ODB_DETAILS_WIN32_WINDOWS_HXX
+
+#include <odb/pre.hxx>
+
+// Try to include <windows.h> so that it doesn't mess other things up.
+//
+#ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# ifndef NOMINMAX // No min and max macros.
+# define NOMINMAX
+# include <windows.h>
+# undef NOMINMAX
+# else
+# include <windows.h>
+# endif
+# undef WIN32_LEAN_AND_MEAN
+#else
+# ifndef NOMINMAX
+# define NOMINMAX
+# include <windows.h>
+# undef NOMINMAX
+# else
+# include <windows.h>
+# endif
+#endif
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WIN32_WINDOWS_HXX
diff --git a/libodb/odb/details/wrapper-p.hxx b/libodb/odb/details/wrapper-p.hxx
new file mode 100644
index 0000000..8f72b5d
--- /dev/null
+++ b/libodb/odb/details/wrapper-p.hxx
@@ -0,0 +1,38 @@
+// file : odb/details/wrapper-p.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_DETAILS_WRAPPER_P_HXX
+#define ODB_DETAILS_WRAPPER_P_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/wrapper-traits.hxx>
+
+#include <odb/details/meta/answer.hxx>
+
+namespace odb
+{
+ namespace details
+ {
+ // GCC doesn't like these to be inside wrapper_p.
+ //
+ template <typename T>
+ meta::no
+ wrapper_p_test (...);
+
+ template <typename T>
+ meta::yes
+ wrapper_p_test (typename wrapper_traits<T>::wrapped_type*);
+
+ template <typename T>
+ struct wrapper_p
+ {
+ static const bool r =
+ sizeof (wrapper_p_test<T> (0)) == sizeof (meta::yes);
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_DETAILS_WRAPPER_P_HXX
diff --git a/libodb/odb/exception.hxx b/libodb/odb/exception.hxx
new file mode 100644
index 0000000..39daf92
--- /dev/null
+++ b/libodb/odb/exception.hxx
@@ -0,0 +1,36 @@
+// file : odb/exception.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_EXCEPTION_HXX
+#define ODB_EXCEPTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <exception>
+
+#include <odb/forward.hxx> // odb::core
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr/base.hxx>
+
+namespace odb
+{
+ struct LIBODB_EXPORT exception: std::exception, details::shared_base
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT = 0;
+
+ virtual exception*
+ clone () const = 0;
+ };
+
+ namespace common
+ {
+ using odb::exception;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_EXCEPTION_HXX
diff --git a/libodb/odb/exceptions.cxx b/libodb/odb/exceptions.cxx
new file mode 100644
index 0000000..bb13b6c
--- /dev/null
+++ b/libodb/odb/exceptions.cxx
@@ -0,0 +1,430 @@
+// file : odb/exceptions.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstring> // std::strlen
+#include <sstream>
+#include <cassert>
+
+#include <odb/exceptions.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ const char* null_pointer::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "NULL pointer";
+ }
+
+ null_pointer* null_pointer::
+ clone () const
+ {
+ return new null_pointer (*this);
+ }
+
+ const char* already_in_transaction::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "transaction already in progress in this thread";
+ }
+
+ already_in_transaction* already_in_transaction::
+ clone () const
+ {
+ return new already_in_transaction (*this);
+ }
+
+ const char* not_in_transaction::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "operation can only be performed in transaction";
+ }
+
+ not_in_transaction* not_in_transaction::
+ clone () const
+ {
+ return new not_in_transaction (*this);
+ }
+
+ const char* transaction_already_finalized::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "transaction already committed or rolled back";
+ }
+
+ transaction_already_finalized* transaction_already_finalized::
+ clone () const
+ {
+ return new transaction_already_finalized (*this);
+ }
+
+ const char* already_in_session::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "session already in effect in this thread";
+ }
+
+ already_in_session* already_in_session::
+ clone () const
+ {
+ return new already_in_session (*this);
+ }
+
+ const char* not_in_session::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "session not in effect in this thread";
+ }
+
+ not_in_session* not_in_session::
+ clone () const
+ {
+ return new not_in_session (*this);
+ }
+
+ const char* session_required::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "session required to load this object relationship";
+ }
+
+ session_required* session_required::
+ clone () const
+ {
+ return new session_required (*this);
+ }
+
+ const char* deadlock::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "transaction aborted due to deadlock";
+ }
+
+ deadlock* deadlock::
+ clone () const
+ {
+ return new deadlock (*this);
+ }
+
+ const char* connection_lost::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "connection to database lost";
+ }
+
+ connection_lost* connection_lost::
+ clone () const
+ {
+ return new connection_lost (*this);
+ }
+
+ const char* timeout::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "database operation timeout";
+ }
+
+ timeout* timeout::
+ clone () const
+ {
+ return new timeout (*this);
+ }
+
+ const char* object_not_persistent::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "object not persistent";
+ }
+
+ object_not_persistent* object_not_persistent::
+ clone () const
+ {
+ return new object_not_persistent (*this);
+ }
+
+ const char* object_already_persistent::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "object already persistent";
+ }
+
+ object_already_persistent* object_already_persistent::
+ clone () const
+ {
+ return new object_already_persistent (*this);
+ }
+
+ const char* object_changed::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "object changed concurrently";
+ }
+
+ object_changed* object_changed::
+ clone () const
+ {
+ return new object_changed (*this);
+ }
+
+ const char* result_not_cached::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "query result is not cached";
+ }
+
+ result_not_cached* result_not_cached::
+ clone () const
+ {
+ return new result_not_cached (*this);
+ }
+
+ const char* abstract_class::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "database operation on instance of abstract class";
+ }
+
+ abstract_class* abstract_class::
+ clone () const
+ {
+ return new abstract_class (*this);
+ }
+
+ const char* no_type_info::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "no type information";
+ }
+
+ no_type_info* no_type_info::
+ clone () const
+ {
+ return new no_type_info (*this);
+ }
+
+ prepared_already_cached::
+ prepared_already_cached (const char* name)
+ : name_ (name)
+ {
+ what_ = "prepared query '";
+ what_ += name;
+ what_ += "' is already cached";
+ }
+
+ prepared_already_cached::
+ ~prepared_already_cached () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* prepared_already_cached::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ prepared_already_cached* prepared_already_cached::
+ clone () const
+ {
+ return new prepared_already_cached (*this);
+ }
+
+ prepared_type_mismatch::
+ prepared_type_mismatch (const char* name)
+ : name_ (name)
+ {
+ what_ = "type mismatch while looking up prepared query '";
+ what_ += name;
+ what_ += "'";
+ }
+
+ prepared_type_mismatch::
+ ~prepared_type_mismatch () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* prepared_type_mismatch::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ prepared_type_mismatch* prepared_type_mismatch::
+ clone () const
+ {
+ return new prepared_type_mismatch (*this);
+ }
+
+ unknown_schema::
+ unknown_schema (const string& name)
+ : name_ (name)
+ {
+ what_ = "unknown database schema '";
+ what_ += name;
+ what_ += "'";
+ }
+
+ unknown_schema::
+ ~unknown_schema () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* unknown_schema::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ unknown_schema* unknown_schema::
+ clone () const
+ {
+ return new unknown_schema (*this);
+ }
+
+ unknown_schema_version::
+ unknown_schema_version (schema_version v)
+ : version_ (v)
+ {
+ ostringstream os;
+ os << v;
+ what_ = "unknown database schema version ";
+ what_ += os.str ();
+ }
+
+ unknown_schema_version::
+ ~unknown_schema_version () ODB_NOTHROW_NOEXCEPT
+ {
+ }
+
+ const char* unknown_schema_version::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ unknown_schema_version* unknown_schema_version::
+ clone () const
+ {
+ return new unknown_schema_version (*this);
+ }
+
+ const char* section_not_loaded::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "section is not loaded";
+ }
+
+ section_not_loaded* section_not_loaded::
+ clone () const
+ {
+ return new section_not_loaded (*this);
+ }
+
+ const char* section_not_in_object::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return "section instance is not part of an object (section was copied?)";
+ }
+
+ section_not_in_object* section_not_in_object::
+ clone () const
+ {
+ return new section_not_in_object (*this);
+ }
+
+ // multiple_exceptions
+ //
+ multiple_exceptions::
+ ~multiple_exceptions () ODB_NOTHROW_NOEXCEPT {}
+
+ void multiple_exceptions::
+ insert (size_t p, bool maybe, const odb::exception& e, bool fatal)
+ {
+ details::shared_ptr<odb::exception> pe;
+
+ if (common_exception_ti_ != typeid (e))
+ pe.reset (e.clone ());
+ else
+ {
+ if (common_exception_ == 0)
+ common_exception_.reset (e.clone ());
+
+ pe = common_exception_;
+ }
+
+ set_.insert (value_type (delta_ + p, maybe, pe));
+ fatal_ = fatal_ || fatal;
+ }
+
+ const multiple_exceptions::value_type* multiple_exceptions::
+ lookup (size_t p) const
+ {
+ p += delta_; // Called while populating multiple_exceptions.
+
+ iterator i (set_.find (value_type (p)));
+ return i == set_.end () ? 0 : &*i;
+ }
+
+ void multiple_exceptions::
+ prepare ()
+ {
+ current_ = 0;
+ delta_ = 0;
+ common_exception_.reset ();
+
+ ostringstream os;
+ os << "multiple exceptions, "
+ << attempted_ << " element" << (attempted_ != 1 ? "s" : "") <<
+ " attempted, "
+ << failed () << " failed"
+ << (fatal_ ? ", fatal" : "") << ":";
+
+ for (iterator i (begin ()); i != end ();)
+ {
+ size_t p (i->position ());
+ const odb::exception& e (i->exception ());
+
+ os << '\n';
+
+ if (!i->maybe ())
+ {
+ os << '[' << p << ']';
+ ++i;
+ }
+ else
+ {
+ // In this case we will normally have a large number of maybe
+ // failures in a row (usually the whole batch). So let's try
+ // to represent them all as a single range.
+ //
+ size_t n (0);
+ for (++i; i != end () && i->maybe (); ++i)
+ {
+ assert (&e == &i->exception ()); // The same common exception.
+ n++;
+ }
+
+ if (n == 0)
+ os << '[' << p << ']';
+ else
+ os << '[' << p << '-' << (p + n) << "] (some)";
+ }
+
+ os << ' ' << e.what ();
+ }
+
+ what_ = os.str ();
+ }
+
+ const char* multiple_exceptions::
+ what () const ODB_NOTHROW_NOEXCEPT
+ {
+ return what_.c_str ();
+ }
+
+ multiple_exceptions* multiple_exceptions::
+ clone () const
+ {
+ return new multiple_exceptions (*this);
+ }
+}
diff --git a/libodb/odb/exceptions.hxx b/libodb/odb/exceptions.hxx
new file mode 100644
index 0000000..d283010
--- /dev/null
+++ b/libodb/odb/exceptions.hxx
@@ -0,0 +1,523 @@
+// file : odb/exceptions.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_EXCEPTIONS_HXX
+#define ODB_EXCEPTIONS_HXX
+
+#include <odb/pre.hxx>
+
+#include <set>
+#include <string>
+#include <cstddef> // std::size_t
+#include <typeinfo>
+
+#include <odb/forward.hxx> // schema_version, odb::core
+#include <odb/exception.hxx>
+
+#include <odb/details/config.hxx> // ODB_NOTHROW_NOEXCEPT
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+namespace odb
+{
+ struct LIBODB_EXPORT null_pointer: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual null_pointer*
+ clone () const;
+ };
+
+ // Transaction exceptions.
+ //
+ struct LIBODB_EXPORT already_in_transaction: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual already_in_transaction*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT not_in_transaction: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual not_in_transaction*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT transaction_already_finalized: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual transaction_already_finalized*
+ clone () const;
+ };
+
+ // Session exceptions.
+ //
+ struct LIBODB_EXPORT already_in_session: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual already_in_session*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT not_in_session: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual not_in_session*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT session_required: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual session_required*
+ clone () const;
+ };
+
+ // Database operations exceptions.
+ //
+ struct LIBODB_EXPORT recoverable: odb::exception
+ {
+ // Abstract.
+ };
+
+ struct LIBODB_EXPORT connection_lost: recoverable
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual connection_lost*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT timeout: recoverable
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual timeout*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT deadlock: recoverable
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual deadlock*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT object_not_persistent: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual object_not_persistent*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT object_already_persistent: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual object_already_persistent*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT object_changed: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual object_changed*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT result_not_cached: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual result_not_cached*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT database_exception: odb::exception
+ {
+ // Abstract.
+ };
+
+ // Polymorphism support exceptions.
+ //
+ struct LIBODB_EXPORT abstract_class: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual abstract_class*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT no_type_info: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual no_type_info*
+ clone () const;
+ };
+
+ // Prepared query support exceptions.
+ //
+ struct LIBODB_EXPORT prepared_already_cached: odb::exception
+ {
+ prepared_already_cached (const char* name);
+ ~prepared_already_cached () ODB_NOTHROW_NOEXCEPT;
+
+ const char*
+ name () const
+ {
+ return name_;
+ }
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual prepared_already_cached*
+ clone () const;
+
+ private:
+ const char* name_;
+ std::string what_;
+ };
+
+ struct LIBODB_EXPORT prepared_type_mismatch: odb::exception
+ {
+ prepared_type_mismatch (const char* name);
+ ~prepared_type_mismatch () ODB_NOTHROW_NOEXCEPT;
+
+ const char*
+ name () const {return name_;}
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual prepared_type_mismatch*
+ clone () const;
+
+ private:
+ const char* name_;
+ std::string what_;
+ };
+
+ // Schema catalog exceptions.
+ //
+ struct LIBODB_EXPORT unknown_schema: odb::exception
+ {
+ unknown_schema (const std::string& name);
+ ~unknown_schema () ODB_NOTHROW_NOEXCEPT;
+
+ const std::string&
+ name () const {return name_;}
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual unknown_schema*
+ clone () const;
+
+ private:
+ std::string name_;
+ std::string what_;
+ };
+
+ struct LIBODB_EXPORT unknown_schema_version: odb::exception
+ {
+ unknown_schema_version (schema_version);
+ ~unknown_schema_version () ODB_NOTHROW_NOEXCEPT;
+
+ schema_version
+ version () const {return version_;}
+
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual unknown_schema_version*
+ clone () const;
+
+ private:
+ schema_version version_;
+ std::string what_;
+ };
+
+ // Section exceptions.
+ //
+ struct LIBODB_EXPORT section_not_loaded: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual section_not_loaded*
+ clone () const;
+ };
+
+ struct LIBODB_EXPORT section_not_in_object: odb::exception
+ {
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual section_not_in_object*
+ clone () const;
+ };
+
+ // Bulk operation exceptions.
+ //
+ struct LIBODB_EXPORT multiple_exceptions: odb::exception
+ {
+ struct value_type
+ {
+ std::size_t
+ position () const {return p_;}
+
+ // If true, then this means that some positions in the batch have
+ // triggered the exception but it is not possible, due to the
+ // limitations of the underlying database API, to discern exactly
+ // which ones. As a result, all the positions in the batch are
+ // marked as "maybe failed".
+ //
+ bool
+ maybe () const {return m_;}
+
+ const odb::exception&
+ exception () const {return *e_;}
+
+ // Implementation details.
+ //
+ public:
+ value_type (std::size_t p,
+ bool maybe,
+ details::shared_ptr<odb::exception> e)
+ : m_ (maybe), p_ (p), e_ (e) {}
+
+ value_type (std::size_t p): p_ (p) {} // "Key" for set lookup.
+
+ private:
+ bool m_;
+ std::size_t p_;
+ details::shared_ptr<odb::exception> e_;
+ };
+
+ struct comparator_type
+ {
+ bool
+ operator() (const value_type& x, const value_type& y) const
+ {
+ return x.position () < y.position ();
+ }
+ };
+
+ typedef std::set<value_type, comparator_type> set_type;
+
+ // Iteration.
+ //
+ public:
+ typedef set_type::const_iterator iterator;
+ typedef set_type::const_iterator const_iterator; // For pedantic types.
+
+ iterator
+ begin () const {return set_.begin ();}
+
+ iterator
+ end () const {return set_.end ();}
+
+ // Lookup.
+ //
+ public:
+ // Return NULL if the element at this position has no exception. Note
+ // that the returned value is value_type* and not odb::exception* in
+ // order to provide access to maybe(); see value_type::maybe() for
+ // details.
+ //
+ const value_type*
+ operator[] (std::size_t p) const
+ {
+ return set_.empty () ? 0 : lookup (p);
+ }
+
+ // Severity, failed and attempt counts.
+ //
+ public:
+ // Return the number of elements for which the operation has been
+ // attempted.
+ //
+ std::size_t
+ attempted () const {return attempted_;}
+
+ // Return the number of positions for which the operation has failed.
+ // Note that this count includes the maybe failed positions.
+ //
+ std::size_t
+ failed () const {return set_.size ();}
+
+ // If fatal() returns true, then (some of) the exceptions were fatal.
+ // In this case, even for elements that were processed but did not
+ // cause the exception, no attempts were made to complete the bulk
+ // operation and the transaction must be aborted.
+ //
+ // If fatal() returns false, then the operation on the elements that
+ // don't have an exception has succeeded. The application can try to
+ // correct the errors and re-attempt the operation on the elements
+ // that did cause an exception. In either case, the transaction can
+ // be committed.
+ //
+ bool
+ fatal () const {return fatal_;}
+
+ // Normally you shouldn't need to do this explicitly but you can
+ // "upgrade" an exception to fatal, for example, for specific
+ // database error codes.
+ //
+ void
+ fatal (bool f) {fatal_ = fatal_ || f;}
+
+ // odb::exception interface.
+ //
+ public:
+ virtual const char*
+ what () const ODB_NOTHROW_NOEXCEPT;
+
+ virtual multiple_exceptions*
+ clone () const;
+
+ // Direct set access.
+ //
+ public:
+ const set_type&
+ set () const {return set_;}
+
+ // Implementation details.
+ //
+ public:
+ ~multiple_exceptions () ODB_NOTHROW_NOEXCEPT;
+
+ // All instances of the common exception must be equal since we are
+ // going to create and share just one.
+ //
+ multiple_exceptions (const std::type_info& common_exception_ti)
+ : common_exception_ti_ (common_exception_ti),
+ fatal_ (false),
+ delta_ (0),
+ current_ (0) {}
+
+ // Set the attempted count as (delta + n).
+ //
+ void
+ attempted (std::size_t n) {attempted_ = delta_ + n;}
+
+ // Increment the position of the current batch. Also resets the
+ // current position in the batch.
+ //
+ void
+ delta (std::size_t d) {delta_ += d; current_ = 0;}
+
+ // Current position in the batch.
+ //
+ std::size_t
+ current () const {return current_;}
+
+ void
+ current (std::size_t c) {current_ = c;}
+
+ void
+ insert (std::size_t p,
+ bool maybe,
+ const odb::exception& e,
+ bool fatal = false);
+
+ void
+ insert (std::size_t p, const odb::exception& e, bool fatal = false)
+ {
+ insert (p, false, e, fatal);
+ }
+
+ void
+ insert (const odb::exception& e, bool fatal = false)
+ {
+ insert (current_, e, fatal);
+ }
+
+ bool
+ empty () const {return set_.empty ();}
+
+ void
+ prepare ();
+
+ private:
+ const value_type*
+ lookup (std::size_t p) const;
+
+ private:
+ const std::type_info& common_exception_ti_;
+ details::shared_ptr<odb::exception> common_exception_;
+
+ set_type set_;
+ bool fatal_;
+ std::size_t attempted_;
+ std::size_t delta_; // Position of the batch.
+ std::size_t current_; // Position in the batch.
+ std::string what_;
+ };
+
+ namespace common
+ {
+ using odb::null_pointer;
+
+ using odb::already_in_transaction;
+ using odb::not_in_transaction;
+ using odb::transaction_already_finalized;
+
+ using odb::already_in_session;
+ using odb::not_in_session;
+ using odb::session_required;
+
+ using odb::recoverable;
+ using odb::deadlock;
+ using odb::connection_lost;
+ using odb::timeout;
+ using odb::object_not_persistent;
+ using odb::object_already_persistent;
+ using odb::object_changed;
+ using odb::result_not_cached;
+ using odb::database_exception;
+
+ using odb::abstract_class;
+ using odb::no_type_info;
+
+ using odb::unknown_schema;
+ using odb::unknown_schema_version;
+
+ using odb::section_not_loaded;
+ using odb::section_not_in_object;
+
+ using odb::multiple_exceptions;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_EXCEPTIONS_HXX
diff --git a/libodb/odb/forward.hxx b/libodb/odb/forward.hxx
new file mode 100644
index 0000000..6f1176d
--- /dev/null
+++ b/libodb/odb/forward.hxx
@@ -0,0 +1,178 @@
+// file : odb/forward.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_FORWARD_HXX
+#define ODB_FORWARD_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr-fwd.hxx>
+
+namespace odb
+{
+ // Common and core namespaces. The idea is that you can use the
+ // using directive to get database-independent (common) names or
+ // all core names (core).
+ //
+ namespace common {}
+
+ namespace core
+ {
+ using namespace common;
+ }
+
+ //
+ //
+ typedef unsigned long long schema_version;
+ struct schema_version_migration;
+
+ class database;
+ class connection;
+ typedef details::shared_ptr<connection> connection_ptr;
+ class transaction;
+ class statement;
+ class session;
+ class section;
+ template <typename T> class result;
+
+ namespace common
+ {
+ using odb::schema_version;
+ using odb::schema_version_migration;
+ using odb::session;
+ using odb::section;
+ using odb::result;
+ }
+
+ namespace core
+ {
+ using odb::database;
+ using odb::connection;
+ using odb::connection_ptr;
+ using odb::transaction;
+ using odb::statement;
+ }
+
+ // Tracing.
+ //
+ class tracer; // Not in core.
+ extern LIBODB_EXPORT tracer& stderr_tracer;
+ extern LIBODB_EXPORT tracer& stderr_full_tracer;
+
+ namespace common
+ {
+ using odb::stderr_tracer;
+ }
+
+ // Implementation details.
+ //
+
+ // Keep real databases first since their enumerators are used as array
+ // indexes.
+ //
+ enum database_id
+ {
+ id_mysql,
+ id_sqlite,
+ id_pgsql,
+ id_oracle,
+ id_mssql,
+ id_common
+ };
+
+ // Number of real databases (i.e., excluding default) in the database_id
+ // enum.
+ //
+ const std::size_t database_count = id_common;
+
+ // Traits.
+ //
+ class access
+ {
+ public:
+ template <typename T>
+ class object_traits;
+
+ template <typename T, database_id DB>
+ class object_traits_impl;
+
+ template <typename T, typename P>
+ class object_factory;
+
+ template <typename T>
+ class view_traits;
+
+ template <typename T, database_id DB>
+ class view_traits_impl;
+
+ template <typename T, typename P>
+ class view_factory;
+
+ template <typename T, typename P>
+ class pointer_factory;
+
+ template <typename T, database_id DB>
+ class composite_value_traits;
+
+ template <typename C>
+ class container_traits;
+ };
+
+ template <typename T>
+ struct object_traits;
+
+ template <typename T, database_id DB>
+ struct object_traits_impl;
+
+ template <typename T>
+ struct view_traits;
+
+ template <typename T, database_id DB>
+ struct view_traits_impl;
+
+ // Cache traits.
+ //
+ template <typename T> struct no_id_pointer_cache_traits;
+ template <typename T> struct no_op_pointer_cache_traits;
+ template <typename T, typename S> struct pointer_cache_traits;
+ template <typename T> struct no_id_reference_cache_traits;
+ template <typename T> struct no_op_reference_cache_traits;
+ template <typename T, typename S> struct reference_cache_traits;
+
+ //
+ //
+ class query_base;
+
+ template <typename T>
+ struct query_column;
+
+ //
+ //
+ class result_impl;
+ class prepared_query_impl;
+
+ //
+ //
+ struct multiple_exceptions;
+
+ // Polymorphism support.
+ //
+ template <typename R>
+ struct polymorphic_map;
+
+ namespace details
+ {
+ template <>
+ struct counter_type<connection>
+ {
+ typedef shared_base counter;
+ };
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_FORWARD_HXX
diff --git a/libodb/odb/function-table.hxx b/libodb/odb/function-table.hxx
new file mode 100644
index 0000000..b1a5a94
--- /dev/null
+++ b/libodb/odb/function-table.hxx
@@ -0,0 +1,50 @@
+// file : odb/function-table.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_FUNCTION_TABLE_HXX
+#define ODB_FUNCTION_TABLE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+
+namespace odb
+{
+ template <typename T, database_id DB>
+ struct object_function_table_entry
+ {
+ typedef access::object_traits_impl<T, id_common> common_traits;
+
+ object_function_table_entry (
+ const typename common_traits::function_table_type* t)
+ {
+ common_traits::function_table[DB] = t;
+ }
+
+ ~object_function_table_entry ()
+ {
+ common_traits::function_table[DB] = 0;
+ }
+ };
+
+ template <typename T, database_id DB>
+ struct view_function_table_entry
+ {
+ typedef access::view_traits_impl<T, id_common> common_traits;
+
+ view_function_table_entry (
+ const typename common_traits::function_table_type* t)
+ {
+ common_traits::function_table[DB] = t;
+ }
+
+ ~view_function_table_entry ()
+ {
+ common_traits::function_table[DB] = 0;
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_FUNCTION_TABLE_HXX
diff --git a/libodb/odb/lazy-pointer-traits.hxx b/libodb/odb/lazy-pointer-traits.hxx
new file mode 100644
index 0000000..2a6c8eb
--- /dev/null
+++ b/libodb/odb/lazy-pointer-traits.hxx
@@ -0,0 +1,141 @@
+// file : odb/lazy-pointer-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_LAZY_POINTER_TRAITS_HXX
+#define ODB_LAZY_POINTER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/pointer-traits.hxx>
+#include <odb/lazy-ptr.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ template <typename T>
+ class pointer_traits< lazy_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_raw;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef lazy_ptr<element_type> pointer_type;
+ typedef element_type* eager_pointer_type;
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ template <class O /* = T */>
+ static typename object_traits<O>::id_type
+ object_id (const pointer_type& p)
+ {
+ return p.template object_id<O> ();
+ }
+ };
+
+#ifndef ODB_CXX11
+ template <typename T>
+ class pointer_traits< lazy_auto_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_unique;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef lazy_auto_ptr<element_type> pointer_type;
+ typedef std::auto_ptr<element_type> eager_pointer_type;
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ template <class O /* = T */>
+ static typename object_traits<O>::id_type
+ object_id (const pointer_type& p)
+ {
+ return p.template object_id<O> ();
+ }
+ };
+#endif
+
+#ifdef ODB_CXX11
+ template <typename T, typename D>
+ class pointer_traits<lazy_unique_ptr<T, D>>
+ {
+ public:
+ static const pointer_kind kind = pk_unique;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef lazy_unique_ptr<element_type, D> pointer_type;
+ typedef std::unique_ptr<element_type, D> eager_pointer_type;
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ template <class O /* = T */>
+ static typename object_traits<O>::id_type
+ object_id (const pointer_type& p)
+ {
+ return p.template object_id<O> ();
+ }
+ };
+
+ template <typename T>
+ class pointer_traits<lazy_shared_ptr<T>>
+ {
+ public:
+ static const pointer_kind kind = pk_shared;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef lazy_shared_ptr<element_type> pointer_type;
+ typedef std::shared_ptr<element_type> eager_pointer_type;
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ template <class O /* = T */>
+ static typename object_traits<O>::id_type
+ object_id (const pointer_type& p)
+ {
+ return p.template object_id<O> ();
+ }
+ };
+
+ template <typename T>
+ class pointer_traits<lazy_weak_ptr<T>>
+ {
+ public:
+ static const pointer_kind kind = pk_weak;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef lazy_weak_ptr<element_type> pointer_type;
+ typedef lazy_shared_ptr<element_type> strong_pointer_type;
+ typedef std::weak_ptr<element_type> eager_pointer_type;
+
+ static strong_pointer_type
+ lock (const pointer_type& p)
+ {
+ return p.lock ();
+ }
+ };
+#endif // ODB_CXX11
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_LAZY_POINTER_TRAITS_HXX
diff --git a/libodb/odb/lazy-ptr-impl.hxx b/libodb/odb/lazy-ptr-impl.hxx
new file mode 100644
index 0000000..89fe798
--- /dev/null
+++ b/libodb/odb/lazy-ptr-impl.hxx
@@ -0,0 +1,188 @@
+// file : odb/lazy-ptr-impl.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_LAZY_PTR_IMPL_HXX
+#define ODB_LAZY_PTR_IMPL_HXX
+
+#include <odb/pre.hxx>
+
+#include <utility> // std::move
+
+#include <odb/forward.hxx> // odb::database
+#include <odb/traits.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ struct lazy_ptr_impl_ref
+ {
+ void* id_;
+ database* db_;
+ void* loader_;
+ void (*free_) (void*);
+ void* (*copy_) (const void*);
+ };
+
+ class lazy_ptr_base
+ {
+ public:
+ typedef odb::database database_type;
+
+ ~lazy_ptr_base ();
+ lazy_ptr_base ();
+ lazy_ptr_base (const lazy_ptr_base&);
+ lazy_ptr_base (const lazy_ptr_impl_ref&);
+
+ lazy_ptr_base&
+ operator= (const lazy_ptr_base&);
+
+ lazy_ptr_base&
+ operator= (const lazy_ptr_impl_ref&);
+
+ // C++11 support.
+ //
+ public:
+#ifdef ODB_CXX11
+ lazy_ptr_base (lazy_ptr_base&&) noexcept;
+
+ lazy_ptr_base&
+ operator= (lazy_ptr_base&&) noexcept;
+#endif
+
+ public:
+ // Reset both the id and database.
+ //
+ void
+ reset ();
+
+ // Reset the id.
+ //
+ void
+ reset_id ();
+
+ void
+ swap (lazy_ptr_base&);
+
+ database_type*
+ database () const;
+
+ typedef void* lazy_ptr_base::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return db_ != 0 ? &lazy_ptr_base::id_ : 0;
+ }
+
+ operator lazy_ptr_impl_ref ();
+
+ protected:
+ typedef void (*free_func) (void*);
+ typedef void* (*copy_func) (const void*);
+
+ // Makes a copy of id.
+ //
+ void
+ reset_ (database_type*,
+ void* loader,
+ const void* id,
+ free_func, copy_func);
+
+ template <typename T>
+ static void
+ free (void*);
+
+ template <typename T>
+ static void*
+ copy (const void*);
+
+ template <typename T, typename DB>
+ static typename object_traits<T>::pointer_type
+ loader (database_type&, const typename object_traits<T>::id_type&);
+
+ protected:
+ void* id_;
+ database_type* db_;
+ void* loader_;
+
+ private:
+ free_func free_;
+ copy_func copy_;
+ };
+
+ template <typename T>
+ class lazy_ptr_impl: public lazy_ptr_base
+ {
+ public:
+ lazy_ptr_impl ();
+
+ template <typename DB, typename ID>
+ lazy_ptr_impl (DB&, const ID&);
+
+ lazy_ptr_impl (const lazy_ptr_impl&);
+
+ template <typename Y>
+ lazy_ptr_impl (const lazy_ptr_impl<Y>&);
+
+ lazy_ptr_impl (const lazy_ptr_impl_ref&);
+
+ lazy_ptr_impl&
+ operator= (const lazy_ptr_impl&);
+
+ template <typename Y>
+ lazy_ptr_impl&
+ operator= (const lazy_ptr_impl<Y>&);
+
+ lazy_ptr_impl&
+ operator= (const lazy_ptr_impl_ref&);
+
+ // C++11 support.
+ //
+ public:
+#ifdef ODB_CXX11
+ lazy_ptr_impl (lazy_ptr_impl&&) noexcept;
+
+ template <typename Y>
+ lazy_ptr_impl (lazy_ptr_impl<Y>&&);
+
+ lazy_ptr_impl&
+ operator= (lazy_ptr_impl&&) noexcept;
+
+ template <typename Y>
+ lazy_ptr_impl&
+ operator= (lazy_ptr_impl<Y>&&);
+#endif
+
+ public:
+ using lazy_ptr_base::reset;
+ using lazy_ptr_base::reset_id;
+
+ template <typename DB, typename ID>
+ void
+ reset (DB&, const ID&);
+
+ // Reset the id and set the database to the new value.
+ //
+ template <typename DB>
+ void
+ reset_db (DB&);
+
+ template <typename ID>
+ void
+ reset_id (const ID&);
+
+ template <typename O /* = T */>
+ typename object_traits<O>::pointer_type
+ load (bool reset_id);
+
+ template <typename O /* = T */>
+ typename object_traits<O>::id_type
+ object_id () const;
+ };
+}
+
+#include <odb/lazy-ptr-impl.ixx>
+#include <odb/lazy-ptr-impl.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_LAZY_PTR_IMPL_HXX
diff --git a/libodb/odb/lazy-ptr-impl.ixx b/libodb/odb/lazy-ptr-impl.ixx
new file mode 100644
index 0000000..9ab0471
--- /dev/null
+++ b/libodb/odb/lazy-ptr-impl.ixx
@@ -0,0 +1,397 @@
+// file : odb/lazy-ptr-impl.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // lazy_ptr_base
+ //
+
+ inline lazy_ptr_base::
+ lazy_ptr_base ()
+ : id_ (0), db_ (0), loader_ (0), free_ (0), copy_ (0)
+ {
+ }
+
+ inline lazy_ptr_base::
+ lazy_ptr_base (const lazy_ptr_base& r)
+ : id_ (0), db_ (r.db_), loader_ (r.loader_),
+ free_ (r.free_), copy_ (r.copy_)
+ {
+ if (r.id_)
+ id_ = copy_ (r.id_);
+ }
+
+ inline lazy_ptr_base::
+ lazy_ptr_base (const lazy_ptr_impl_ref& r)
+ : id_ (r.id_), db_ (r.db_), loader_ (r.loader_),
+ free_ (r.free_), copy_ (r.copy_)
+ {
+ }
+
+#ifdef ODB_CXX11
+ inline lazy_ptr_base::
+ lazy_ptr_base (lazy_ptr_base&& r) noexcept
+ : id_ (r.id_), db_ (r.db_), loader_ (r.loader_),
+ free_ (r.free_), copy_ (r.copy_)
+ {
+ r.id_ = 0;
+ }
+#endif
+
+ inline void lazy_ptr_base::
+ reset_id ()
+ {
+ if (id_)
+ free_ (id_);
+
+ id_ = 0;
+ }
+
+ inline void lazy_ptr_base::
+ reset_ (database_type* db,
+ void* loader,
+ const void* id,
+ free_func free,
+ copy_func copy)
+ {
+ void* idc (id ? copy (id) : 0);
+
+ if (id_)
+ free_ (id_);
+
+ free_ = free;
+ copy_ = copy;
+
+ id_ = idc;
+ db_ = db;
+ loader_ = loader;
+ }
+
+ inline void lazy_ptr_base::
+ reset ()
+ {
+ reset_id ();
+ db_ = 0;
+ loader_ = 0;
+ }
+
+#ifdef ODB_CXX11
+ inline lazy_ptr_base& lazy_ptr_base::
+ operator= (lazy_ptr_base&& r) noexcept
+ {
+ if (id_ != r.id_)
+ {
+ reset_id ();
+ id_ = r.id_;
+ free_ = r.free_;
+ copy_ = r.copy_;
+
+ r.id_ = 0;
+ }
+
+ db_ = r.db_;
+ loader_ = r.loader_;
+ return *this;
+ }
+#endif
+
+ inline lazy_ptr_base& lazy_ptr_base::
+ operator= (const lazy_ptr_base& r)
+ {
+ if (id_ != r.id_)
+ reset_ (r.db_, r.loader_, r.id_, r.free_, r.copy_);
+ else
+ {
+ db_ = r.db_;
+ loader_ = r.loader_;
+ }
+
+ return *this;
+ }
+
+ inline lazy_ptr_base& lazy_ptr_base::
+ operator= (const lazy_ptr_impl_ref& r)
+ {
+ if (id_ != r.id_)
+ {
+ reset_id ();
+ id_ = r.id_;
+ free_ = r.free_;
+ copy_ = r.copy_;
+ }
+
+ db_ = r.db_;
+ loader_ = r.loader_;
+ return *this;
+ }
+
+ inline lazy_ptr_base::
+ ~lazy_ptr_base ()
+ {
+ if (id_)
+ free_ (id_);
+ }
+
+ inline void lazy_ptr_base::
+ swap (lazy_ptr_base& r)
+ {
+ void* id (id_);
+ database_type* db (db_);
+ void* l (loader_);
+ free_func f (free_);
+ copy_func c (copy_);
+
+ id_ = r.id_;
+ db_ = r.db_;
+ loader_ = r.loader_;
+ free_ = r.free_;
+ copy_ = r.copy_;
+
+ r.id_ = id;
+ r.db_ = db;
+ r.loader_ = l;
+ r.free_ = f;
+ r.copy_ = c;
+ }
+
+ inline lazy_ptr_base::database_type* lazy_ptr_base::
+ database () const
+ {
+ return db_;
+ }
+
+ inline lazy_ptr_base::
+ operator lazy_ptr_impl_ref ()
+ {
+ lazy_ptr_impl_ref r;
+ r.id_ = id_;
+ r.db_ = db_;
+ r.loader_ = loader_;
+ r.free_ = free_;
+ r.copy_ = copy_;
+ id_ = 0;
+ db_ = 0;
+ loader_ = 0;
+ return r;
+ }
+
+ //
+ // lazy_ptr_impl
+ //
+
+ template <typename T>
+ inline lazy_ptr_impl<T>::
+ lazy_ptr_impl ()
+ {
+ }
+
+ template <typename T>
+ template <typename DB, typename ID>
+ inline lazy_ptr_impl<T>::
+ lazy_ptr_impl (DB& db, const ID& id)
+ {
+ typedef typename object_traits<T>::id_type id_type;
+ typedef typename object_traits<T>::pointer_type pointer_type;
+ typedef pointer_type (*loader_type) (database_type&, const id_type&);
+
+ // Make sure that ID and T's object id types are the same
+ // (or implicit-convertible). If you get a compile error
+ // pointing here, then you most likely used a wrong object
+ // id argument in the constructor call.
+ //
+ const id_type& r (id);
+
+ // Compiler error pointing here? Perhaps db is not an
+ // odb::<database>::database instance?
+ //
+ database_type& bdb (db);
+
+ // For some reason GCC needs this statically-typed pointer in
+ // order to instantiate the functions.
+ //
+ loader_type ldr (&loader<T, DB>);
+
+ reset_ (&bdb,
+ reinterpret_cast<void*> (ldr),
+ &r,
+ &free<id_type>,
+ &copy<id_type>);
+ }
+
+ template <typename T>
+ inline lazy_ptr_impl<T>::
+ lazy_ptr_impl (const lazy_ptr_impl& r)
+ : lazy_ptr_base (r)
+ {
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline lazy_ptr_impl<T>::
+ lazy_ptr_impl (const lazy_ptr_impl<Y>& r)
+ : lazy_ptr_base (r)
+ {
+ }
+
+ template <typename T>
+ inline lazy_ptr_impl<T>::
+ lazy_ptr_impl (const lazy_ptr_impl_ref& r)
+ : lazy_ptr_base (r)
+ {
+ }
+
+ template <typename T>
+ inline lazy_ptr_impl<T>& lazy_ptr_impl<T>::
+ operator= (const lazy_ptr_impl& r)
+ {
+ lazy_ptr_base& b (*this);
+ b = r;
+ return *this;
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline lazy_ptr_impl<T>& lazy_ptr_impl<T>::
+ operator= (const lazy_ptr_impl<Y>& r)
+ {
+ lazy_ptr_base& b (*this);
+ b = r;
+ return *this;
+ }
+
+ template <typename T>
+ inline lazy_ptr_impl<T>& lazy_ptr_impl<T>::
+ operator= (const lazy_ptr_impl_ref& r)
+ {
+ lazy_ptr_base& b (*this);
+ b = r;
+ return *this;
+ }
+
+#ifdef ODB_CXX11
+ template <typename T>
+ inline lazy_ptr_impl<T>::
+ lazy_ptr_impl (lazy_ptr_impl&& r) noexcept
+ : lazy_ptr_base (std::move (r))
+ {
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline lazy_ptr_impl<T>::
+ lazy_ptr_impl (lazy_ptr_impl<Y>&& r)
+ : lazy_ptr_base (std::move (r))
+ {
+ }
+
+ template <typename T>
+ inline lazy_ptr_impl<T>& lazy_ptr_impl<T>::
+ operator= (lazy_ptr_impl&& r) noexcept
+ {
+ lazy_ptr_base& b (*this);
+ b = std::move (r);
+ return *this;
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline lazy_ptr_impl<T>& lazy_ptr_impl<T>::
+ operator= (lazy_ptr_impl<Y>&& r)
+ {
+ lazy_ptr_base& b (*this);
+ b = std::move (r);
+ return *this;
+ }
+#endif
+
+ template <typename T>
+ template <typename DB, typename ID>
+ inline void lazy_ptr_impl<T>::
+ reset (DB& db, const ID& id)
+ {
+ typedef typename object_traits<T>::id_type id_type;
+ typedef typename object_traits<T>::pointer_type pointer_type;
+ typedef pointer_type (*loader_type) (database_type&, const id_type&);
+
+ // Make sure that ID and T's object id types are the same
+ // (or implicit-convertible). If you get a compile error
+ // pointing here, then you most likely used a wrong object
+ // id argument in the constructor call.
+ //
+ const id_type& r (id);
+
+ // Compiler error pointing here? Perhaps db is not an
+ // odb::<database>::database instance?
+ //
+ database_type& bdb (db);
+
+ // For some reason GCC needs this statically-typed pointer in
+ // order to instantiate the functions.
+ //
+ loader_type ldr (&loader<T, DB>);
+
+ reset_ (&bdb,
+ reinterpret_cast<void*> (ldr),
+ &r,
+ &free<id_type>,
+ &copy<id_type>);
+ }
+
+ template <typename T>
+ template <typename DB>
+ inline void lazy_ptr_impl<T>::
+ reset_db (DB& db)
+ {
+ typedef typename object_traits<T>::id_type id_type;
+ typedef typename object_traits<T>::pointer_type pointer_type;
+ typedef pointer_type (*loader_type) (database_type&, const id_type&);
+
+ reset_id ();
+
+ // Compiler error pointing here? Perhaps db is not an
+ // odb::<database>::database instance?
+ //
+ db_ = &db;
+
+ // For some reason GCC needs this statically-typed pointer in
+ // order to instantiate the functions.
+ //
+ loader_type ldr (&loader<T, DB>);
+ loader_ = reinterpret_cast<void*> (ldr);
+ }
+
+ template <typename T>
+ template <typename ID>
+ inline void lazy_ptr_impl<T>::
+ reset_id (const ID& id)
+ {
+ typedef typename object_traits<T>::id_type id_type;
+
+ // Make sure that ID and T's object id types are the same
+ // (or implicit-convertible). If you get a compile error
+ // pointing here, then you most likely used a wrong object
+ // id argument in the constructor call.
+ //
+ const id_type& r (id);
+
+ reset_ (db_, loader_, &r, &free<id_type>, &copy<id_type>);
+ }
+
+ template <typename T>
+ template <typename O>
+ inline typename object_traits<O>::id_type lazy_ptr_impl<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::id_type id_type;
+ const id_type& id (*static_cast<const id_type*> (id_));
+
+ // Make sure that O' and T's object id types are the same
+ // (or implicit-convertible). If you get a compile error
+ // pointing here, then you most likely used a wrong type
+ // as a template argument in the object_id() call.
+ //
+ const typename object_traits<O>::id_type& r (id);
+ return r;
+ }
+}
diff --git a/libodb/odb/lazy-ptr-impl.txx b/libodb/odb/lazy-ptr-impl.txx
new file mode 100644
index 0000000..7aea9c3
--- /dev/null
+++ b/libodb/odb/lazy-ptr-impl.txx
@@ -0,0 +1,60 @@
+// file : odb/lazy-ptr-impl.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // lazy_ptr_base
+ //
+
+ template <typename T>
+ void lazy_ptr_base::
+ free (void* p)
+ {
+ delete static_cast<T*> (p);
+ }
+
+ template <typename T>
+ void* lazy_ptr_base::
+ copy (const void* p)
+ {
+ return new T (*static_cast<const T*> (p));
+ }
+
+ template <typename T, typename DB>
+ typename object_traits<T>::pointer_type lazy_ptr_base::
+ loader (database_type& db, const typename object_traits<T>::id_type& id)
+ {
+ // Compiler error pointing here? Perhaps you did not include
+ // <odb/database.hxx>?
+ //
+ return static_cast<DB&> (db).template load<
+ typename object_traits<T>::object_type> (id);
+ }
+
+ //
+ // lazy_ptr_impl
+ //
+
+ template <typename T>
+ template <typename O>
+ inline typename object_traits<O>::pointer_type lazy_ptr_impl<T>::
+ load (bool reset)
+ {
+ typedef typename object_traits<T>::id_type id_type;
+ typedef typename object_traits<T>::pointer_type pointer_type;
+ typedef pointer_type (*loader_type) (database_type&, const id_type&);
+
+ loader_type loader (reinterpret_cast<loader_type> (loader_));
+ const id_type& id (*static_cast<const id_type*> (id_));
+ pointer_type p (loader (*db_, id));
+
+ if (reset)
+ reset_id ();
+
+ // If you get a compile error pointing here, then you most likely
+ // used a wrong type as a template argument in the load() call.
+ //
+ return p;
+ }
+}
diff --git a/libodb/odb/lazy-ptr.hxx b/libodb/odb/lazy-ptr.hxx
new file mode 100644
index 0000000..ab31dfc
--- /dev/null
+++ b/libodb/odb/lazy-ptr.hxx
@@ -0,0 +1,681 @@
+// file : odb/lazy-ptr.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_LAZY_PTR_HXX
+#define ODB_LAZY_PTR_HXX
+
+#include <odb/pre.hxx>
+
+#include <memory> // std::auto_ptr, std::shared_ptr/weak_ptr
+#include <utility> // std::move
+
+#include <odb/forward.hxx> // odb::core, odb::database
+#include <odb/traits.hxx>
+#include <odb/lazy-ptr-impl.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ // Raw pointer lazy version.
+ //
+ template <class T>
+ class lazy_ptr
+ {
+ // Pointer interface.
+ //
+ public:
+ typedef T element_type;
+
+ lazy_ptr ();
+ template <class Y> lazy_ptr (Y*);
+
+ lazy_ptr (const lazy_ptr&);
+ template <class Y> lazy_ptr (const lazy_ptr<Y>&);
+
+ lazy_ptr& operator= (const lazy_ptr&);
+ template <class Y> lazy_ptr& operator= (Y*);
+ template <class Y> lazy_ptr& operator= (const lazy_ptr<Y>&);
+
+ void swap (lazy_ptr&);
+ void reset ();
+ template <class Y> void reset (Y*);
+
+ T& operator* () const;
+ T* operator-> () const;
+ T* get () const;
+
+ typedef T* lazy_ptr::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return (p_ || i_) ? &lazy_ptr::p_ : 0;
+ }
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // NULL loaded()
+ //
+ // true true NULL pointer to transient object
+ // false true valid pointer to persistent object
+ // true false unloaded pointer to persistent object
+ // false false valid pointer to transient object
+ //
+ bool loaded () const;
+
+ T* load () const;
+
+ // Unload the pointer. For transient objects this function is
+ // equivalent to reset().
+ //
+ void unload () const;
+
+ // Get the underlying eager pointer. If this is an unloaded pointer
+ // to a persistent object, then the returned pointer will be NULL.
+ //
+ T* get_eager () const;
+
+ template <class DB, class ID> lazy_ptr (DB&, const ID&);
+ template <class DB, class Y> lazy_ptr (DB&, Y*);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB, class Y> void reset (DB&, Y*);
+
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /* = T */>
+#endif
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ // Helpers.
+ //
+ public:
+ template <class Y> bool equal (const lazy_ptr<Y>&) const;
+
+ private:
+ template <class Y> friend class lazy_ptr;
+
+ mutable T* p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+
+ // operator< and operator<< are not provided.
+ //
+ template <class T, class Y>
+ bool operator== (const lazy_ptr<T>&, const lazy_ptr<Y>&);
+
+ template <class T, class Y>
+ bool operator!= (const lazy_ptr<T>&, const lazy_ptr<Y>&);
+
+ template <class T> void swap (lazy_ptr<T>&, lazy_ptr<T>&);
+
+ // std::auto_ptr lazy version.
+ //
+#ifndef ODB_CXX11
+ template <class T>
+ struct lazy_auto_ptr_ref
+ {
+ explicit lazy_auto_ptr_ref (T*, const lazy_ptr_impl_ref&);
+
+ T* p_;
+ lazy_ptr_impl_ref i_;
+ };
+
+ template <class T>
+ class lazy_auto_ptr
+ {
+ // Standard auto_ptr interface.
+ //
+ public:
+ typedef T element_type;
+
+ explicit lazy_auto_ptr (T* = 0);
+ lazy_auto_ptr (lazy_auto_ptr&);
+ template <class Y> lazy_auto_ptr (lazy_auto_ptr<Y>&);
+
+ lazy_auto_ptr& operator= (lazy_auto_ptr&);
+ template <class Y> lazy_auto_ptr& operator= (lazy_auto_ptr<Y>&);
+
+ T& operator* () const;
+ T* operator-> () const;
+ T* get () const;
+ T* release ();
+ void reset (T* = 0);
+
+ lazy_auto_ptr (const lazy_auto_ptr_ref<T>&);
+ lazy_auto_ptr& operator= (const lazy_auto_ptr_ref<T>&);
+ template <class Y> operator lazy_auto_ptr_ref<Y> ();
+ template <class Y> operator lazy_auto_ptr<Y> ();
+
+ // Extension: conversion to bool.
+ //
+ public:
+ typedef std::auto_ptr<T> lazy_auto_ptr::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return (p_.get () != 0 || i_) ? &lazy_auto_ptr::p_ : 0;
+ }
+
+ // Initialization/assignment from auto_ptr.
+ //
+ public:
+ template <class Y> lazy_auto_ptr (std::auto_ptr<Y>&);
+ lazy_auto_ptr (std::auto_ptr_ref<T>);
+
+ template <class Y> lazy_auto_ptr& operator= (std::auto_ptr<Y>&);
+ lazy_auto_ptr& operator= (std::auto_ptr_ref<T>);
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // NULL loaded()
+ //
+ // true true NULL pointer to transient object
+ // false true valid pointer to persistent object
+ // true false unloaded pointer to persistent object
+ // false false valid pointer to transient object
+ //
+ bool loaded () const;
+
+ std::auto_ptr<T>& load () const;
+
+ // Unload the pointer. For transient objects this function is
+ // equivalent to reset().
+ //
+ void unload () const;
+
+ // Get the underlying eager pointer. If this is an unloaded pointer
+ // to a persistent object, then the returned pointer will be NULL.
+ //
+ std::auto_ptr<T>& get_eager () const;
+
+ template <class DB, class ID> lazy_auto_ptr (DB&, const ID&);
+ template <class DB> lazy_auto_ptr (DB&, T*);
+ template <class DB, class Y> lazy_auto_ptr (DB&, std::auto_ptr<Y>&);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB> void reset (DB&, T*);
+ template <class DB, class Y> void reset (DB&, std::auto_ptr<Y>&);
+
+ template <class O /* = T */>
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ private:
+ template <class Y> friend class lazy_auto_ptr;
+
+ // Note that it is possible to have a situation where p_ is NULL,
+ // i_.id is NULL and i_.db is not NULL. This will happen if the
+ // auto_ptr reference returned by load() is transferred to another
+ // pointer or reset.
+ //
+ mutable std::auto_ptr<T> p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+#endif
+
+#ifdef ODB_CXX11
+
+ // C++11 std::unique_ptr lazy version.
+ //
+ template <class T, class D = std::default_delete<T>>
+ class lazy_unique_ptr
+ {
+ // Standard lazy_unique_ptr interface.
+ //
+ public:
+ typedef T* pointer; // For now assume it is T*.
+ typedef T element_type;
+ typedef D deleter_type;
+
+ /*constexpr*/ lazy_unique_ptr () /*noexcept*/;
+#ifdef ODB_CXX11_NULLPTR
+ /*constexpr*/ lazy_unique_ptr (std::nullptr_t) /*noexcept*/;
+#endif
+ explicit lazy_unique_ptr (pointer) /*noexcept*/;
+
+ // For now assume D is non-reference.
+ //
+ lazy_unique_ptr (pointer, const deleter_type&) /*noexcept*/;
+ lazy_unique_ptr (pointer, deleter_type&&) /*noexcept*/;
+
+ lazy_unique_ptr (lazy_unique_ptr&&) noexcept;
+ template <class T1, class D1> lazy_unique_ptr (lazy_unique_ptr<T1, D1>&&) /*noexcept*/;
+ //template <class T1> lazy_unique_ptr (std::auto_ptr<T1>&&) /*noexcept*/;
+
+#ifdef ODB_CXX11_NULLPTR
+ lazy_unique_ptr& operator= (std::nullptr_t) /*noexcept*/;
+#endif
+ lazy_unique_ptr& operator= (lazy_unique_ptr&&) noexcept;
+ template <class T1, class D1> lazy_unique_ptr& operator= (lazy_unique_ptr<T1, D1>&&) /*noexcept*/;
+
+ T& operator* () const;
+ pointer operator-> () const /*noexcept*/;
+ pointer get () const /*noexcept*/;
+#ifdef ODB_CXX11_EXPLICIT_CONVERSION_OPERATOR
+ explicit operator bool() const /*noexcept*/;
+#else
+ typedef std::unique_ptr<T, D> lazy_unique_ptr::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return (p_ || i_) ? &lazy_unique_ptr::p_ : 0;
+ }
+#endif
+
+ pointer release () /*noexcept*/;
+ void reset (pointer = pointer ()) /*noexcept*/;
+ void swap (lazy_unique_ptr&) /*noexcept*/;
+
+ deleter_type& get_deleter () /*noexcept*/;
+ const deleter_type& get_deleter () const /*noexcept*/;
+
+#ifdef ODB_CXX11_DELETED_FUNCTION
+ lazy_unique_ptr (const lazy_unique_ptr&) = delete;
+ lazy_unique_ptr& operator= (const lazy_unique_ptr&) = delete;
+#else
+ private:
+ lazy_unique_ptr (const lazy_unique_ptr&);
+ lazy_unique_ptr& operator= (const lazy_unique_ptr&);
+#endif
+
+ // Initialization/assignment from unique_ptr.
+ //
+ public:
+ template <class T1, class D1> lazy_unique_ptr (std::unique_ptr<T1, D1>&&) /*noexcept*/;
+ template <class T1, class D1> lazy_unique_ptr& operator= (std::unique_ptr<T1, D1>&&) /*noexcept*/;
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // NULL loaded()
+ //
+ // true true NULL pointer to transient object
+ // false true valid pointer to persistent object
+ // true false unloaded pointer to persistent object
+ // false false valid pointer to transient object
+ //
+ bool loaded () const;
+
+ std::unique_ptr<T, D>& load () const;
+
+ // Unload the pointer. For transient objects this function is
+ // equivalent to reset().
+ //
+ void unload () const;
+
+ // Get the underlying eager pointer. If this is an unloaded pointer
+ // to a persistent object, then the returned pointer will be NULL.
+ //
+ std::unique_ptr<T, D>& get_eager () const;
+
+ template <class DB, class ID> lazy_unique_ptr (DB&, const ID&);
+ template <class DB> lazy_unique_ptr (DB&, pointer);
+ template <class DB> lazy_unique_ptr (DB&, pointer, const deleter_type&);
+ template <class DB> lazy_unique_ptr (DB&, pointer, deleter_type&&);
+ template <class DB, class T1, class D1> lazy_unique_ptr (DB&, std::unique_ptr<T1, D1>&&);
+ //template <class DB, class T1> lazy_unique_ptr (DB&, std::auto_ptr<T1>&&);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB> void reset (DB&, pointer);
+ template <class DB, class T1, class D1> void reset (DB&, std::unique_ptr<T1, D1>&&);
+ //template <class DB, class T1> void reset (DB&, std::auto_ptr<T1>&&);
+
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /*= T*/>
+#endif
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ // Helpers.
+ //
+ public:
+ template <class T1, class D1> bool equal (const lazy_unique_ptr<T1, D1>&) const;
+
+ private:
+ template <class T1, class D1> friend class lazy_unique_ptr;
+
+ // Note that it is possible to have a situation where p_ is NULL,
+ // i_.id is NULL and i_.db is not NULL. This will happen if the
+ // unique_ptr reference returned by load() is transferred to
+ // another pointer or reset.
+ //
+ mutable std::unique_ptr<T, D> p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+
+ template <class T> void swap (lazy_unique_ptr<T>&, lazy_unique_ptr<T>&) /*noexcept*/;
+
+ // operator< and operator<< are not provided.
+ //
+ template <class T1, class D1, class T2, class D2>
+ bool operator== (const lazy_unique_ptr<T1, D1>&, const lazy_unique_ptr<T2, D2>&);
+
+ template <class T1, class D1, class T2, class D2>
+ bool operator!= (const lazy_unique_ptr<T1, D1>&, const lazy_unique_ptr<T2, D2>&);
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T, class D>
+ bool operator== (const lazy_unique_ptr<T, D>&, std::nullptr_t) /*noexcept*/;
+
+ template <class T, class D>
+ bool operator== (std::nullptr_t, const lazy_unique_ptr<T, D>&) /*noexcept*/;
+
+ template <class T, class D>
+ bool operator!= (const lazy_unique_ptr<T, D>&, std::nullptr_t) /*noexcept*/;
+
+ template <class T, class D>
+ bool operator!= (std::nullptr_t, const lazy_unique_ptr<T, D>&) /*noexcept*/;
+#endif
+
+ // C++11 std::shared_ptr lazy version.
+ //
+ template <class T>
+ class lazy_weak_ptr;
+
+ template <class T>
+ class lazy_shared_ptr
+ {
+ // The standard shared_ptr interface.
+ //
+ public:
+ typedef T element_type;
+
+ /*constexpr*/ lazy_shared_ptr () /*noexcept*/;
+#ifdef ODB_CXX11_NULLPTR
+ /*constexpr*/ lazy_shared_ptr (std::nullptr_t) /*noexcept*/;
+#endif
+ template <class Y> explicit lazy_shared_ptr (Y*);
+ template <class Y, class D> lazy_shared_ptr (Y*, D);
+ template <class Y, class D, class A> lazy_shared_ptr (Y*, D, A);
+#ifdef ODB_CXX11_NULLPTR
+ template <class D> lazy_shared_ptr (std::nullptr_t, D);
+ template <class D, class A> lazy_shared_ptr (std::nullptr_t, D, A);
+#endif
+ template <class Y> lazy_shared_ptr (const lazy_shared_ptr<Y>&, T*) /*noexcept*/;
+
+ lazy_shared_ptr (const lazy_shared_ptr&) /*noexcept*/;
+ template <class Y> lazy_shared_ptr (const lazy_shared_ptr<Y>&) /*noexcept*/;
+ lazy_shared_ptr (lazy_shared_ptr&&) noexcept;
+ template <class Y> lazy_shared_ptr (lazy_shared_ptr<Y>&&) /*noexcept*/;
+ template <class Y> explicit lazy_shared_ptr (const lazy_weak_ptr<Y>&);
+ //template <class Y> explicit lazy_shared_ptr (std::auto_ptr<Y>&&);
+ template <class Y, class D> lazy_shared_ptr (std::unique_ptr<Y, D>&&);
+
+ ~lazy_shared_ptr ();
+
+ lazy_shared_ptr& operator= (const lazy_shared_ptr&) /*noexcept*/;
+ template <class Y> lazy_shared_ptr& operator= (const lazy_shared_ptr<Y>&) /*noexcept*/;
+ lazy_shared_ptr& operator= (lazy_shared_ptr&&) noexcept;
+ template <class Y> lazy_shared_ptr& operator= (lazy_shared_ptr<Y>&&) /*noexcept*/;
+ //template <class Y> lazy_shared_ptr& operator= (std::auto_ptr<Y>&&);
+ template <class Y, class D> lazy_shared_ptr& operator= (std::unique_ptr<Y, D>&&);
+
+ void swap (lazy_shared_ptr&) /*noexcept*/;
+ void reset () /*noexcept*/;
+ template <class Y> void reset (Y*);
+ template <class Y, class D> void reset (Y*, D);
+ template <class Y, class D, class A> void reset (Y*, D, A);
+
+ T* get () const /*noexcept*/;
+ T& operator* () const /*noexcept*/;
+ T* operator-> () const /*noexcept*/;
+ long use_count () const /*noexcept*/;
+ bool unique () const /*noexcept*/;
+#ifdef ODB_CXX11_EXPLICIT_CONVERSION_OPERATOR
+ explicit operator bool () const /*noexcept*/;
+#else
+ typedef std::shared_ptr<T> lazy_shared_ptr::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return (p_ || i_) ? &lazy_shared_ptr::p_ : 0;
+ }
+#endif
+
+ // owner_before () is not provded.
+
+ // Initialization/assignment from shared_ptr and weak_ptr.
+ //
+ public:
+ template <class Y> lazy_shared_ptr (const std::shared_ptr<Y>&);
+ template <class Y> lazy_shared_ptr (std::shared_ptr<Y>&&);
+ template <class Y> explicit lazy_shared_ptr (const std::weak_ptr<Y>&);
+
+ template <class Y> lazy_shared_ptr& operator= (const std::shared_ptr<Y>&);
+ template <class Y> lazy_shared_ptr& operator= (std::shared_ptr<Y>&&);
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // NULL loaded()
+ //
+ // true true NULL pointer to transient object
+ // false true valid pointer to persistent object
+ // true false unloaded pointer to persistent object
+ // false false valid pointer to transient object
+ //
+ bool loaded () const;
+
+ std::shared_ptr<T> load () const;
+
+ // Unload the pointer. For transient objects this function is
+ // equivalent to reset().
+ //
+ void unload () const;
+
+ // Get the underlying eager pointer. If this is an unloaded pointer
+ // to a persistent object, then the returned pointer will be NULL.
+ //
+ std::shared_ptr<T> get_eager () const;
+
+ template <class DB, class ID> lazy_shared_ptr (DB&, const ID&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, Y*);
+ template <class DB, class Y, class D> lazy_shared_ptr (DB&, Y*, D);
+ template <class DB, class Y, class D, class A> lazy_shared_ptr (DB&, Y*, D, A);
+ //template <class DB, class Y> lazy_shared_ptr (DB&, std::auto_ptr<Y>&&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, const std::shared_ptr<Y>&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, std::shared_ptr<Y>&&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, const std::weak_ptr<Y>&);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB, class Y> void reset (DB&, Y*);
+ template <class DB, class Y, class D> void reset (DB&, Y*, D);
+ template <class DB, class Y, class D, class A> void reset (DB&, Y*, D, A);
+ //template <class DB, class Y> void reset (DB&, std::auto_ptr<Y>&&);
+ template <class DB, class Y> void reset (DB&, const std::shared_ptr<Y>&);
+ template <class DB, class Y> void reset (DB&, std::shared_ptr<Y>&&);
+
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /*= T*/>
+#endif
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ // Helpers.
+ //
+ public:
+ template <class Y> bool equal (const lazy_shared_ptr<Y>&) const;
+
+ private:
+ template <class Y> friend class lazy_shared_ptr;
+ template <class Y> friend class lazy_weak_ptr;
+
+ // For lazy_weak_ptr::lock().
+ //
+ lazy_shared_ptr (std::shared_ptr<T>&& p, const lazy_ptr_impl<T>& i)
+ : p_ (std::move (p)), i_ (i) {}
+
+ private:
+ mutable std::shared_ptr<T> p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+
+ template <class T> void swap (lazy_shared_ptr<T>&, lazy_shared_ptr<T>&) /*noexcept*/;
+
+ template <class D, class T>
+ D* get_deleter (const lazy_shared_ptr<T>&) /*noexcept*/;
+
+ // operator< and operator<< are not provided.
+ //
+ template <class T, class Y>
+ bool operator== (const lazy_shared_ptr<T>&, const lazy_shared_ptr<Y>&) /*noexcept*/;
+
+ template <class T, class Y>
+ bool operator!= (const lazy_shared_ptr<T>&, const lazy_shared_ptr<Y>&) /*noexcept*/;
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T>
+ bool operator== (const lazy_shared_ptr<T>&, std::nullptr_t) /*noexcept*/;
+
+ template <class T>
+ bool operator== (std::nullptr_t, const lazy_shared_ptr<T>&) /*noexcept*/;
+
+ template <class T>
+ bool operator!= (const lazy_shared_ptr<T>&, std::nullptr_t) /*noexcept*/;
+
+ template <class T>
+ bool operator!= (std::nullptr_t, const lazy_shared_ptr<T>&) /*noexcept*/;
+#endif
+
+ // C++11 std::weak_ptr lazy version.
+ //
+ template <class T>
+ class lazy_weak_ptr
+ {
+ // The standard weak_ptr interface.
+ //
+ public:
+ typedef T element_type;
+
+ /*constexpr*/ lazy_weak_ptr () /*noexcept*/;
+ template <class Y> lazy_weak_ptr (const lazy_shared_ptr<Y>&) /*noexcept*/;
+ lazy_weak_ptr (const lazy_weak_ptr&) /*noexcept*/;
+ template <class Y> lazy_weak_ptr (const lazy_weak_ptr<Y>&) /*noexcept*/;
+
+ ~lazy_weak_ptr ();
+
+ lazy_weak_ptr& operator= (const lazy_weak_ptr&) /*noexcept*/;
+ template <class Y> lazy_weak_ptr& operator= (const lazy_weak_ptr<Y>&) /*noexcept*/;
+ template <class Y> lazy_weak_ptr& operator= (const lazy_shared_ptr<Y>&) /*noexcept*/;
+
+ void swap (lazy_weak_ptr<T>&) /*noexcept*/;
+ void reset () /*noexcept*/;
+
+ long use_count () const /*noexcept*/;
+ bool expired () const /*noexcept*/;
+
+ lazy_shared_ptr<T> lock () const /*noexcept*/;
+
+ // owner_before () is not provded.
+
+ // Initialization/assignment from shared_ptr and weak_ptr.
+ //
+ public:
+ template <class Y> lazy_weak_ptr (const std::weak_ptr<Y>&);
+ template <class Y> lazy_weak_ptr (const std::shared_ptr<Y>&);
+
+ template <class Y> lazy_weak_ptr& operator= (const std::weak_ptr<Y>&);
+ template <class Y> lazy_weak_ptr& operator= (const std::shared_ptr<Y>&);
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // expired() loaded()
+ //
+ // true true expired pointer to transient object
+ // false true valid pointer to persistent object
+ // true false expired pointer to persistent object
+ // false false valid pointer to transient object
+ //
+ bool loaded () const;
+
+ // Performs both lock and load.
+ //
+ std::shared_ptr<T> load () const;
+
+ // Unload the pointer. For transient objects this function is
+ // equivalent to reset().
+ //
+ void unload () const;
+
+ // Get the underlying eager pointer. If this is an unloaded pointer
+ // to a persistent object, then the returned pointer will be NULL.
+ //
+ std::weak_ptr<T> get_eager () const;
+
+ template <class DB, class ID> lazy_weak_ptr (DB&, const ID&);
+ template <class DB, class Y> lazy_weak_ptr (DB&, const std::shared_ptr<Y>&);
+ template <class DB, class Y> lazy_weak_ptr (DB&, const std::weak_ptr<Y>&);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB, class Y> void reset (DB&, const std::shared_ptr<Y>&);
+ template <class DB, class Y> void reset (DB&, const std::weak_ptr<Y>&);
+
+ // The object_id() function can only be called when the object is
+ // persistent, or: expired() XOR loaded() (can use != for XOR).
+ //
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /*= T*/>
+#endif
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ private:
+ template <class Y> friend class lazy_shared_ptr;
+ template <class Y> friend class lazy_weak_ptr;
+
+ mutable std::weak_ptr<T> p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+
+ // operator< is not provided.
+ //
+ template <class T> void swap (lazy_weak_ptr<T>&, lazy_weak_ptr<T>&);
+
+#endif // ODB_CXX11
+
+ namespace common
+ {
+ using odb::lazy_ptr;
+
+#ifndef ODB_CXX11
+ using odb::lazy_auto_ptr;
+#endif
+
+#ifdef ODB_CXX11
+ using odb::lazy_unique_ptr;
+ using odb::lazy_shared_ptr;
+ using odb::lazy_weak_ptr;
+#endif
+ }
+}
+
+#include <odb/lazy-ptr.ixx>
+#include <odb/lazy-ptr.txx>
+
+#include <odb/lazy-pointer-traits.hxx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_LAZY_PTR_HXX
diff --git a/libodb/odb/lazy-ptr.ixx b/libodb/odb/lazy-ptr.ixx
new file mode 100644
index 0000000..a2d72f5
--- /dev/null
+++ b/libodb/odb/lazy-ptr.ixx
@@ -0,0 +1,1681 @@
+// file : odb/lazy-ptr.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // lazy_ptr
+ //
+
+ template <class T>
+ inline lazy_ptr<T>::
+ lazy_ptr (): p_ (0) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_ptr<T>::
+ lazy_ptr (Y* p): p_ (p) {}
+
+ template <class T>
+ inline lazy_ptr<T>::
+ lazy_ptr (const lazy_ptr& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_ptr<T>::
+ lazy_ptr (const lazy_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_ptr<T>& lazy_ptr<T>::
+ operator= (const lazy_ptr& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_ptr<T>& lazy_ptr<T>::
+ operator= (Y* r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_ptr<T>& lazy_ptr<T>::
+ operator= (const lazy_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ inline void lazy_ptr<T>::
+ swap (lazy_ptr& b)
+ {
+ T* p (p_);
+ p_ = b.p_;
+ b.p_ = p;
+ i_.swap (b.i_);
+ }
+
+ template <class T>
+ inline void lazy_ptr<T>::
+ reset ()
+ {
+ p_ = 0;
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline void lazy_ptr<T>::
+ reset (Y* p)
+ {
+ p_ = p;
+ i_.reset ();
+ }
+
+ template <class T>
+ inline T& lazy_ptr<T>::
+ operator* () const
+ {
+ return *p_;
+ }
+
+ template <class T>
+ inline T* lazy_ptr<T>::
+ operator-> () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ inline T* lazy_ptr<T>::
+ get () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ inline bool lazy_ptr<T>::
+ loaded () const
+ {
+ bool i (i_);
+ return (p_ == 0) != i; // !p_ XOR i
+ }
+
+ template <class T>
+ inline T* lazy_ptr<T>::
+ load () const
+ {
+ if (p_ == 0 && i_)
+ p_ = i_.template load<T> (true); // Reset id.
+
+ return p_;
+ }
+
+ template <class T>
+ inline void lazy_ptr<T>::
+ unload () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (p_)
+ {
+ if (i_.database () != 0)
+ i_.reset_id (object_traits<object_type>::id (*p_));
+
+ p_ = 0;
+ }
+ }
+
+ template <class T>
+ inline T* lazy_ptr<T>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline lazy_ptr<T>::
+ lazy_ptr (DB& db, const ID& id): p_ (0), i_ (db, id) {}
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_ptr<T>::
+ lazy_ptr (DB& db, Y* r)
+ : p_ (r)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline void lazy_ptr<T>::
+ reset (DB& db, const ID& id)
+ {
+ p_ = 0;
+ i_.reset (db, id);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_ptr<T>::
+ reset (DB& db, Y* r)
+ {
+ p_ = r;
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_ptr<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ return p_
+ ? object_traits<object_type>::id (*p_)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T>
+ inline typename lazy_ptr<T>::database_type& lazy_ptr<T>::
+ database () const
+ {
+ return *i_.database ();
+ }
+
+ template <class T, class Y>
+ inline bool
+ operator== (const lazy_ptr<T>& a, const lazy_ptr<Y>& b)
+ {
+ return a.equal (b);
+ }
+
+ template <class T, class Y>
+ inline bool
+ operator!= (const lazy_ptr<T>& a, const lazy_ptr<Y>& b)
+ {
+ return !a.equal (b);
+ }
+
+ template <class T>
+ inline void
+ swap (lazy_ptr<T>& a, lazy_ptr<T>& b)
+ {
+ a.swap (b);
+ }
+
+ //
+ // lazy_auto_ptr_ref
+ //
+#ifndef ODB_CXX11
+
+ template <class T>
+ inline lazy_auto_ptr_ref<T>::
+ lazy_auto_ptr_ref (T* p, const lazy_ptr_impl_ref& i): p_ (p), i_ (i) {}
+
+ //
+ // lazy_auto_ptr
+ //
+
+ template <class T>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (T* p): p_ (p) {}
+
+ template <class T>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (lazy_auto_ptr& r)
+ : p_ (r.p_), i_ (static_cast<lazy_ptr_impl_ref> (r.i_))
+ {
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (lazy_auto_ptr<Y>& r)
+ : p_ (r.p_), i_ (static_cast<lazy_ptr_impl_ref> (r.i_))
+ {
+ }
+
+ template <class T>
+ inline lazy_auto_ptr<T>& lazy_auto_ptr<T>::
+ operator= (lazy_auto_ptr& r)
+ {
+ p_ = r.p_;
+ i_ = static_cast<lazy_ptr_impl_ref> (r.i_);
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_auto_ptr<T>& lazy_auto_ptr<T>::
+ operator= (lazy_auto_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = static_cast<lazy_ptr_impl_ref> (r.i_);
+ return *this;
+ }
+
+ template <class T>
+ inline T& lazy_auto_ptr<T>::
+ operator* () const
+ {
+ return *p_;
+ }
+
+ template <class T>
+ inline T* lazy_auto_ptr<T>::
+ operator-> () const
+ {
+ return p_.operator-> ();
+ }
+
+ template <class T>
+ inline T* lazy_auto_ptr<T>::
+ get () const
+ {
+ return p_.get ();
+ }
+
+ template <class T>
+ inline T* lazy_auto_ptr<T>::
+ release ()
+ {
+ i_.reset ();
+ return p_.release ();
+ }
+
+ template <class T>
+ inline void lazy_auto_ptr<T>::
+ reset (T* p)
+ {
+ i_.reset ();
+ p_.reset (p);
+ }
+
+ template <class T>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (const lazy_auto_ptr_ref<T>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_auto_ptr<T>& lazy_auto_ptr<T>::
+ operator= (const lazy_auto_ptr_ref<T>& r)
+ {
+ if (p_.get () != r.p_)
+ p_.reset (r.p_);
+
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_auto_ptr<T>::
+ operator lazy_auto_ptr_ref<Y> ()
+ {
+ return lazy_auto_ptr_ref<Y> (p_.release (), i_);
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_auto_ptr<T>::
+ operator lazy_auto_ptr<Y> ()
+ {
+ return lazy_auto_ptr<Y> (*this);
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (std::auto_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (std::auto_ptr_ref<T> r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_auto_ptr<T>& lazy_auto_ptr<T>::
+ operator= (std::auto_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline lazy_auto_ptr<T>& lazy_auto_ptr<T>::
+ operator= (std::auto_ptr_ref<T> r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline bool lazy_auto_ptr<T>::
+ loaded () const
+ {
+ bool i (i_);
+ return (p_.get () == 0) != i; // XOR
+ }
+
+ template <class T>
+ inline std::auto_ptr<T>& lazy_auto_ptr<T>::
+ load () const
+ {
+ if (p_.get () == 0 && i_)
+ {
+ std::auto_ptr<T> tmp (i_.template load<T> (true)); // Reset id.
+ p_ = tmp;
+ }
+
+ return p_;
+ }
+
+ template <class T>
+ inline void lazy_auto_ptr<T>::
+ unload () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (p_.get () != 0)
+ {
+ if (i_.database () != 0)
+ i_.reset_id (object_traits<object_type>::id (*p_));
+
+ p_.reset ();
+ }
+ }
+
+ template <class T>
+ inline std::auto_ptr<T>& lazy_auto_ptr<T>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (DB& db, const ID& id): i_ (db, id) {}
+
+ template <class T>
+ template <class DB>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (DB& db, T* p)
+ : p_ (p)
+ {
+ if (p)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_auto_ptr<T>::
+ lazy_auto_ptr (DB& db, std::auto_ptr<Y>& p)
+ : p_ (p)
+ {
+ if (p_.get () != 0)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline void lazy_auto_ptr<T>::
+ reset (DB& db, const ID& id)
+ {
+ p_.reset ();
+ i_.reset (db, id);
+ }
+
+ template <class T>
+ template <class DB>
+ inline void lazy_auto_ptr<T>::
+ reset (DB& db, T* p)
+ {
+ p_.reset (p);
+
+ if (p)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_auto_ptr<T>::
+ reset (DB& db, std::auto_ptr<Y>& p)
+ {
+ p_ = p;
+
+ if (p_.get () != 0)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_auto_ptr<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ return p_.get () != 0
+ ? object_traits<object_type>::id (*p_)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T>
+ inline typename lazy_auto_ptr<T>::database_type& lazy_auto_ptr<T>::
+ database () const
+ {
+ return *i_.database ();
+ }
+#endif
+
+#ifdef ODB_CXX11
+
+ //
+ // lazy_unique_ptr
+ //
+
+ template <class T, class D>
+ lazy_unique_ptr<T, D>::
+ lazy_unique_ptr () {}
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T, class D>
+ lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (std::nullptr_t) {}
+#endif
+
+ template <class T, class D>
+ lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (pointer p): p_ (p) {}
+
+ template <class T, class D>
+ lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (pointer p, const deleter_type& d): p_ (p, d) {}
+
+ template <class T, class D>
+ lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (pointer p, deleter_type&& d): p_ (p, std::move (d)) {}
+
+ template <class T, class D>
+ lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (lazy_unique_ptr&& r) noexcept
+ : p_ (std::move (r.p_)), i_ (std::move (r.i_)) {}
+
+ template <class T, class D>
+ template <class T1, class D1>
+ lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (lazy_unique_ptr<T1, D1>&& r)
+ : p_ (std::move (r.p_)), i_ (std::move (r.i_)) {}
+
+ // template <class T, class D>
+ // template <class T1>
+ // lazy_unique_ptr<T, D>::
+ // lazy_unique_ptr (std::auto_ptr<T1>&& r): p_ (std::move (r)) {}
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T, class D>
+ lazy_unique_ptr<T, D>& lazy_unique_ptr<T, D>::
+ operator= (std::nullptr_t)
+ {
+ reset ();
+ return *this;
+ }
+#endif
+
+ template <class T, class D>
+ lazy_unique_ptr<T, D>& lazy_unique_ptr<T, D>::
+ operator= (lazy_unique_ptr&& r) noexcept
+ {
+ p_ = std::move (r.p_);
+ i_ = std::move (r.i_);
+ return *this;
+ }
+
+ template <class T, class D>
+ template <class T1, class D1>
+ lazy_unique_ptr<T, D>& lazy_unique_ptr<T, D>::
+ operator= (lazy_unique_ptr<T1, D1>&& r)
+ {
+ p_ = std::move (r.p_);
+ i_ = std::move (r.i_);
+ return *this;
+ }
+
+ template <class T, class D>
+ T& lazy_unique_ptr<T, D>::
+ operator* () const
+ {
+ return *p_;
+ }
+
+ template <class T, class D>
+ typename lazy_unique_ptr<T, D>::pointer lazy_unique_ptr<T, D>::
+ operator-> () const
+ {
+ return p_.operator-> ();
+ }
+
+ template <class T, class D>
+ typename lazy_unique_ptr<T, D>::pointer lazy_unique_ptr<T, D>::
+ get () const
+ {
+ return p_.get ();
+ }
+
+#ifdef ODB_CXX11_EXPLICIT_CONVERSION_OPERATOR
+ template <class T, class D>
+ lazy_unique_ptr<T, D>::
+ operator bool() const
+ {
+ return p_ || i_;
+ }
+#endif
+
+ template <class T, class D>
+ typename lazy_unique_ptr<T, D>::pointer lazy_unique_ptr<T, D>::
+ release ()
+ {
+ i_.reset ();
+ return p_.release ();
+ }
+
+ template <class T, class D>
+ void lazy_unique_ptr<T, D>::
+ reset (pointer p)
+ {
+ p_.reset (p);
+ i_.reset ();
+ }
+
+ template <class T, class D>
+ void lazy_unique_ptr<T, D>::
+ swap (lazy_unique_ptr& b)
+ {
+ p_.swap (b.p_);
+ i_.swap (b.i_);
+ }
+
+ template <class T, class D>
+ typename lazy_unique_ptr<T, D>::deleter_type& lazy_unique_ptr<T, D>::
+ get_deleter ()
+ {
+ return p_.get_deleter ();
+ }
+
+ template <class T, class D>
+ const typename lazy_unique_ptr<T, D>::deleter_type& lazy_unique_ptr<T, D>::
+ get_deleter () const
+ {
+ return p_.get_deleter ();
+ }
+
+ template <class T, class D>
+ template <class T1, class D1>
+ inline lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (std::unique_ptr<T1, D1>&& p)
+ : p_ (std::move (p))
+ {
+ }
+
+ template <class T, class D>
+ template <class T1, class D1>
+ inline lazy_unique_ptr<T, D>& lazy_unique_ptr<T, D>::
+ operator= (std::unique_ptr<T1, D1>&& p)
+ {
+ p_ = std::move (p);
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T, class D>
+ inline bool lazy_unique_ptr<T, D>::
+ loaded () const
+ {
+ bool i (i_);
+ return !p_ != i; // !p_ XOR i_
+ }
+
+ template <class T, class D>
+ inline std::unique_ptr<T, D>& lazy_unique_ptr<T, D>::
+ load () const
+ {
+ if (!p_ && i_)
+ p_ = std::unique_ptr<T, D> (i_.template load<T> (true)); // Reset id.
+
+ return p_;
+ }
+
+ template <class T, class D>
+ inline void lazy_unique_ptr<T, D>::
+ unload () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (p_)
+ {
+ if (i_.database () != 0)
+ i_.reset_id (object_traits<object_type>::id (*p_));
+
+ p_.reset ();
+ }
+ }
+
+ template <class T, class D>
+ inline std::unique_ptr<T, D>& lazy_unique_ptr<T, D>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T, class D>
+ template <class DB, class ID>
+ inline lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (DB& db, const ID& id): i_ (db, id) {}
+
+ template <class T, class D>
+ template <class DB>
+ inline lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (DB& db, T* p)
+ : p_ (p)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T, class D>
+ template <class DB>
+ inline lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (DB& db, T* p, const deleter_type& d)
+ : p_ (p, d)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T, class D>
+ template <class DB>
+ inline lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (DB& db, T* p, deleter_type&& d)
+ : p_ (p, std::move (d))
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T, class D>
+ template <class DB, class T1, class D1>
+ inline lazy_unique_ptr<T, D>::
+ lazy_unique_ptr (DB& db, std::unique_ptr<T1, D1>&& p)
+ : p_ (std::move (p))
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ // template <class T, class D>
+ // template <class DB, class T1>
+ // inline lazy_unique_ptr<T, D>::
+ // lazy_unique_ptr (DB& db, std::auto_ptr<T1>&& p)
+ // : p_ (std::move (p))
+ // {
+ // if (p_)
+ // i_.reset_db (db);
+ // }
+
+ template <class T, class D>
+ template <class DB, class ID>
+ inline void lazy_unique_ptr<T, D>::
+ reset (DB& db, const ID& id)
+ {
+ p_.reset ();
+ i_.reset (db, id);
+ }
+
+ template <class T, class D>
+ template <class DB>
+ inline void lazy_unique_ptr<T, D>::
+ reset (DB& db, T* p)
+ {
+ p_.reset (p);
+
+ if (p)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T, class D>
+ template <class DB, class T1, class D1>
+ inline void lazy_unique_ptr<T, D>::
+ reset (DB& db, std::unique_ptr<T1, D1>&& p)
+ {
+ p_ = std::move (p);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ // template <class T, class D>
+ // template <class DB, class T1>
+ // inline void lazy_unique_ptr<T, D>::
+ // reset (DB& db, std::auto_ptr<T1>&& p)
+ // {
+ // p_ = std::unique_ptr<T, D> (std::move (p));
+ //
+ // if (p_)
+ // i_.reset_db (db);
+ // else
+ // i_.reset ();
+ // }
+
+ template <class T, class D>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_unique_ptr<T, D>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ return p_
+ ? object_traits<object_type>::id (*p_)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T, class D>
+ inline typename lazy_unique_ptr<T, D>::database_type& lazy_unique_ptr<T, D>::
+ database () const
+ {
+ return *i_.database ();
+ }
+
+ template <class T>
+ inline void
+ swap (lazy_unique_ptr<T>& a, lazy_unique_ptr<T>& b)
+ {
+ a.swap (b);
+ }
+
+ template <class T1, class D1, class T2, class D2>
+ inline bool
+ operator== (const lazy_unique_ptr<T1, D1>& a,
+ const lazy_unique_ptr<T2, D2>& b)
+ {
+ return a.equal (b);
+ }
+
+ template <class T1, class D1, class T2, class D2>
+ inline bool
+ operator!= (const lazy_unique_ptr<T1, D1>& a,
+ const lazy_unique_ptr<T2, D2>& b)
+ {
+ return !a.equal (b);
+ }
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T, class D>
+ inline bool
+ operator== (const lazy_unique_ptr<T, D>& a, std::nullptr_t)
+ {
+ return !a;
+ }
+
+ template <class T, class D>
+ inline bool
+ operator== (std::nullptr_t, const lazy_unique_ptr<T, D>& b)
+ {
+ return !b;
+ }
+
+ template <class T, class D>
+ inline bool
+ operator!= (const lazy_unique_ptr<T, D>& a, std::nullptr_t)
+ {
+ return bool (a); // Explicit to-bool conversion.
+ }
+
+ template <class T, class D>
+ inline bool
+ operator!= (std::nullptr_t, const lazy_unique_ptr<T, D>& b)
+ {
+ return bool (b); // Explicit to-bool conversion.
+ }
+#endif
+
+ //
+ // lazy_shared_ptr
+ //
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr () {}
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (std::nullptr_t) {}
+#endif
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (Y* p): p_ (p) {}
+
+ template <class T>
+ template <class Y, class D>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (Y* p, D d): p_ (p, d) {}
+
+ template <class T>
+ template <class Y, class D, class A>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (Y* p, D d, A a): p_ (p, d, a) {}
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T>
+ template <class D>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (std::nullptr_t p, D d): p_ (p, d) {}
+
+ template <class T>
+ template <class D, class A>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (std::nullptr_t p, D d, A a): p_ (p, d, a) {}
+#endif
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_shared_ptr<Y>& r, T* p)
+ // r.p_ has to be loaded
+ : p_ (r.p_, p) {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_shared_ptr& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_shared_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (lazy_shared_ptr&& r) noexcept
+ : p_ (std::move (r.p_)), i_ (std::move (r.i_)) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (lazy_shared_ptr<Y>&& r)
+ : p_ (std::move (r.p_)), i_ (std::move (r.i_)) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_weak_ptr<Y>& r): i_ (r.i_)
+ {
+ // If the pointer has expired but can be re-loaded, then don't throw.
+ //
+ p_ = r.lock ().get_eager ();
+
+ if (!p_ && !i_)
+ throw std::bad_weak_ptr ();
+ }
+
+ // template <class T>
+ // template <class Y>
+ // inline lazy_shared_ptr<T>::
+ // lazy_shared_ptr (std::auto_ptr<Y>&& r): p_ (std::move (r)) {}
+
+ template <class T>
+ template <class Y, class D>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (std::unique_ptr<Y, D>&& r): p_ (std::move (r)) {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ ~lazy_shared_ptr () {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (const lazy_shared_ptr& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (const lazy_shared_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (lazy_shared_ptr&& r) noexcept
+ {
+ p_ = std::move (r.p_);
+ i_ = std::move (r.i_);
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (lazy_shared_ptr<Y>&& r)
+ {
+ p_ = std::move (r.p_);
+ i_ = std::move (r.i_);
+ return *this;
+ }
+
+ // template <class T>
+ // template <class Y>
+ // inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ // operator= (std::auto_ptr<Y>&& r)
+ // {
+ // p_ = std::move (r);
+ // i_.reset ();
+ // return *this;
+ // }
+
+ template <class T>
+ template <class Y, class D>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (std::unique_ptr<Y, D>&& r)
+ {
+ p_ = std::move (r);
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline void lazy_shared_ptr<T>::
+ swap (lazy_shared_ptr& b)
+ {
+ p_.swap (b.p_);
+ i_.swap (b.i_);
+ }
+
+ template <class T>
+ inline void lazy_shared_ptr<T>::
+ reset ()
+ {
+ p_.reset ();
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (Y* p)
+ {
+ p_.reset (p);
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class Y, class D>
+ inline void lazy_shared_ptr<T>::
+ reset (Y* p, D d)
+ {
+ p_.reset (p, d);
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class Y, class D, class A>
+ inline void lazy_shared_ptr<T>::
+ reset (Y* p, D d, A a)
+ {
+ p_.reset (p, d, a);
+ i_.reset ();
+ }
+
+ template <class T>
+ inline T& lazy_shared_ptr<T>::
+ operator* () const
+ {
+ return *p_;
+ }
+
+ template <class T>
+ inline T* lazy_shared_ptr<T>::
+ operator-> () const
+ {
+ return p_.operator-> ();
+ }
+
+ template <class T>
+ inline T* lazy_shared_ptr<T>::
+ get () const
+ {
+ return p_.get ();
+ }
+
+ template <class T>
+ inline bool lazy_shared_ptr<T>::
+ unique () const
+ {
+ return p_.unique ();
+ }
+
+ template <class T>
+ inline long lazy_shared_ptr<T>::
+ use_count () const
+ {
+ return p_.use_count ();
+ }
+
+#ifdef ODB_CXX11_EXPLICIT_CONVERSION_OPERATOR
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ operator bool () const
+ {
+ return p_ || i_;
+ }
+#endif
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const std::shared_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (std::shared_ptr<Y>&& r): p_ (std::move (r)) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const std::weak_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (const std::shared_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (std::shared_ptr<Y>&& r)
+ {
+ p_ = std::move (r);
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline bool lazy_shared_ptr<T>::
+ loaded () const
+ {
+ bool i (i_);
+ return !p_ != i; // !p_ XOR i_
+ }
+
+ template <class T>
+ inline std::shared_ptr<T> lazy_shared_ptr<T>::
+ load () const
+ {
+ if (!p_ && i_)
+ p_ = i_.template load<T> (true); // Reset id.
+
+ return p_;
+ }
+
+ template <class T>
+ inline void lazy_shared_ptr<T>::
+ unload () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (p_)
+ {
+ if (i_.database () != 0)
+ i_.reset_id (object_traits<object_type>::id (*p_));
+
+ p_.reset ();
+ }
+ }
+
+ template <class T>
+ inline std::shared_ptr<T> lazy_shared_ptr<T>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, const ID& id): i_ (db, id) {}
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, Y* p)
+ : p_ (p)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y, class D>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, Y* p, D d)
+ : p_ (p, d)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y, class D, class A>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, Y* p, D d, A a)
+ : p_ (p, d, a)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ // template <class T>
+ // template <class DB, class Y>
+ // inline lazy_shared_ptr<T>::
+ // lazy_shared_ptr (DB& db, std::auto_ptr<Y>&& r)
+ // : p_ (std::move (r))
+ // {
+ // if (p_)
+ // i_.reset_db (db);
+ // }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, const std::shared_ptr<Y>& r)
+ : p_ (r)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, std::shared_ptr<Y>&& r)
+ : p_ (std::move (r))
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, const std::weak_ptr<Y>& r)
+ : p_ (r)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, const ID& id)
+ {
+ p_.reset ();
+ i_.reset (db, id);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, Y* p)
+ {
+ p_.reset (p);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y, class D>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, Y* p, D d)
+ {
+ p_.reset (p, d);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y, class D, class A>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, Y* p, D d, A a)
+ {
+ p_.reset (p, d, a);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ // template <class T>
+ // template <class DB, class Y>
+ // inline void lazy_shared_ptr<T>::
+ // reset (DB& db, std::auto_ptr<Y>&& r)
+ // {
+ // p_ = std::move (r);
+ //
+ // if (p_)
+ // i_.reset_db (db);
+ // else
+ // i_.reset ();
+ // }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, const std::shared_ptr<Y>& r)
+ {
+ p_ = r;
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, std::shared_ptr<Y>&& r)
+ {
+ p_ = std::move (r);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_shared_ptr<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ return p_
+ ? object_traits<object_type>::id (*p_)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T>
+ inline typename lazy_shared_ptr<T>::database_type& lazy_shared_ptr<T>::
+ database () const
+ {
+ return *i_.database ();
+ }
+
+ template <class T, class Y>
+ inline bool
+ operator== (const lazy_shared_ptr<T>& a, const lazy_shared_ptr<Y>& b)
+ {
+ return a.equal (b);
+ }
+
+ template <class T, class Y>
+ inline bool
+ operator!= (const lazy_shared_ptr<T>& a, const lazy_shared_ptr<Y>& b)
+ {
+ return !a.equal (b);
+ }
+
+#ifdef ODB_CXX11_NULLPTR
+ template <class T>
+ inline bool
+ operator== (const lazy_shared_ptr<T>& p, std::nullptr_t)
+ {
+ return !p;
+ }
+
+ template <class T>
+ inline bool
+ operator== (std::nullptr_t, const lazy_shared_ptr<T>& p)
+ {
+ return !p;
+ }
+
+ template <class T>
+ inline bool
+ operator!= (const lazy_shared_ptr<T>& p, std::nullptr_t)
+ {
+ return bool (p); // Explicit to-bool conversion.
+ }
+
+ template <class T>
+ inline bool
+ operator!= (std::nullptr_t, const lazy_shared_ptr<T>& p)
+ {
+ return bool (p); // Explicit to-bool conversion.
+ }
+#endif
+
+ template <class T>
+ inline void
+ swap (lazy_shared_ptr<T>& a, lazy_shared_ptr<T>& b)
+ {
+ a.swap (b);
+ }
+
+ template <class D, class T>
+ inline D*
+ get_deleter (const lazy_shared_ptr<T>& p)
+ {
+ return std::get_deleter<D> (p.p_);
+ }
+
+ //
+ // lazy_weak_ptr
+ //
+
+ template <class T>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr () {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const lazy_shared_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const lazy_weak_ptr& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const lazy_weak_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_weak_ptr<T>::
+ ~lazy_weak_ptr () {}
+
+ template <class T>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const lazy_weak_ptr& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const lazy_weak_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const lazy_shared_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ inline void lazy_weak_ptr<T>::
+ swap (lazy_weak_ptr<T>& r)
+ {
+ p_.swap (r.p_);
+ i_.swap (r.i_);
+ }
+
+ template <class T>
+ inline void lazy_weak_ptr<T>::
+ reset ()
+ {
+ p_.reset ();
+ i_.reset ();
+ }
+
+ template <class T>
+ inline long lazy_weak_ptr<T>::
+ use_count () const
+ {
+ return p_.use_count ();
+ }
+
+ template <class T>
+ inline bool lazy_weak_ptr<T>::
+ expired () const
+ {
+ return p_.expired ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const std::weak_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const std::shared_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const std::weak_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const std::shared_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline bool lazy_weak_ptr<T>::
+ loaded () const
+ {
+ bool i (i_);
+ return expired () != i; // expired () XOR i_
+ }
+
+ template <class T>
+ inline lazy_shared_ptr<T> lazy_weak_ptr<T>::
+ lock () const
+ {
+ return lazy_shared_ptr<T> (p_.lock (), i_);
+ }
+
+ template <class T>
+ inline std::shared_ptr<T> lazy_weak_ptr<T>::
+ load () const
+ {
+ std::shared_ptr<T> r (p_.lock ());
+
+ if (!r && i_)
+ {
+ r = i_.template load<T> (false); // Keep id.
+ p_ = r;
+ }
+
+ return r;
+ }
+
+ template <class T>
+ inline void lazy_weak_ptr<T>::
+ unload () const
+ {
+ // With weak pointer we always keep i_ up to date.
+ //
+ p_.reset ();
+ }
+
+ template <class T>
+ inline std::weak_ptr<T> lazy_weak_ptr<T>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (DB& db, const ID& id): i_ (db, id) {}
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (DB& db, const std::shared_ptr<Y>& r)
+ : p_ (r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (r)
+ i_.reset (db, object_traits<object_type>::id (*r));
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (DB& db, const std::weak_ptr<Y>& r)
+ : p_ (r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ std::shared_ptr<T> sp (p_.lock ());
+
+ if (sp)
+ i_.reset (db, object_traits<object_type>::id (*sp));
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline void lazy_weak_ptr<T>::
+ reset (DB& db, const ID& id)
+ {
+ p_.reset ();
+ i_.reset (db, id);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_weak_ptr<T>::
+ reset (DB& db, const std::shared_ptr<Y>& r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ p_ = r;
+
+ if (r)
+ i_.reset (db, object_traits<object_type>::id (*r));
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_weak_ptr<T>::
+ reset (DB& db, const std::weak_ptr<Y>& r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ p_ = r;
+ std::shared_ptr<T> sp (p_.lock ());
+
+ if (sp)
+ i_.reset (db, object_traits<object_type>::id (*sp));
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_weak_ptr<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ std::shared_ptr<T> sp (p_.lock ());
+ return sp
+ ? object_traits<object_type>::id (*sp)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T>
+ inline typename lazy_weak_ptr<T>::database_type& lazy_weak_ptr<T>::
+ database () const
+ {
+ return *i_.database ();
+ }
+
+ template <class T>
+ inline void
+ swap (lazy_weak_ptr<T>& a, lazy_weak_ptr<T>& b)
+ {
+ a.swap (b);
+ }
+
+#endif // ODB_CXX11
+
+}
diff --git a/libodb/odb/lazy-ptr.txx b/libodb/odb/lazy-ptr.txx
new file mode 100644
index 0000000..17a7405
--- /dev/null
+++ b/libodb/odb/lazy-ptr.txx
@@ -0,0 +1,114 @@
+// file : odb/lazy-ptr.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // lazy_ptr
+ //
+
+ template <class T>
+ template <class Y>
+ bool lazy_ptr<T>::
+ equal (const lazy_ptr<Y>& r) const
+ {
+ bool t1 ((p_ == 0) == loaded ());
+ bool t2 ((r.p_ == 0) == r.loaded ());
+
+ // If both are transient, then compare the underlying pointers.
+ //
+ if (t1 && t2)
+ return p_ == r.p_;
+
+ // If one is transient and the other is persistent, then compare
+ // the underlying pointers but only if they are non NULL. Note
+ // that an unloaded persistent object is always unequal to a
+ // transient object.
+ //
+ if (t1 || t2)
+ return p_ == r.p_ && p_ != 0;
+
+ // If both objects are persistent, then we compare databases and
+ // object ids.
+ //
+ typedef typename object_traits<T>::object_type object_type1;
+ typedef typename object_traits<Y>::object_type object_type2;
+
+ return i_.database () == r.i_.database () &&
+ object_id<object_type1> () == r.template object_id<object_type2> ();
+ }
+
+#ifdef ODB_CXX11
+
+ //
+ // lazy_unique_ptr
+ //
+
+ template <class T, class D>
+ template <class T1, class D1>
+ bool lazy_unique_ptr<T, D>::
+ equal (const lazy_unique_ptr<T1, D1>& r) const
+ {
+ bool t1 (!p_ == loaded ());
+ bool t2 (!r.p_ == r.loaded ());
+
+ // If both are transient, then compare the underlying pointers.
+ //
+ if (t1 && t2)
+ return p_ == r.p_;
+
+ // If one is transient and the other is persistent, then compare
+ // the underlying pointers but only if they are non NULL. Note
+ // that an unloaded persistent object is always unequal to a
+ // transient object.
+ //
+ if (t1 || t2)
+ return p_ == r.p_ && p_;
+
+ // If both objects are persistent, then we compare databases and
+ // object ids.
+ //
+ typedef typename object_traits<T>::object_type object_type1;
+ typedef typename object_traits<T1>::object_type object_type2;
+
+ return i_.database () == r.i_.database () &&
+ object_id<object_type1> () == r.template object_id<object_type2> ();
+ }
+
+ //
+ // lazy_shared_ptr
+ //
+
+ template <class T>
+ template <class Y>
+ bool lazy_shared_ptr<T>::
+ equal (const lazy_shared_ptr<Y>& r) const
+ {
+ bool t1 (!p_ == loaded ());
+ bool t2 (!r.p_ == r.loaded ());
+
+ // If both are transient, then compare the underlying pointers.
+ //
+ if (t1 && t2)
+ return p_ == r.p_;
+
+ // If one is transient and the other is persistent, then compare
+ // the underlying pointers but only if they are non NULL. Note
+ // that an unloaded persistent object is always unequal to a
+ // transient object.
+ //
+ if (t1 || t2)
+ return p_ == r.p_ && p_;
+
+ // If both objects are persistent, then we compare databases and
+ // object ids.
+ //
+ typedef typename object_traits<T>::object_type object_type1;
+ typedef typename object_traits<Y>::object_type object_type2;
+
+ return i_.database () == r.i_.database () &&
+ object_id<object_type1> () == r.template object_id<object_type2> ();
+ }
+#endif // ODB_CXX11
+
+}
diff --git a/libodb/odb/nested-container.hxx b/libodb/odb/nested-container.hxx
new file mode 100644
index 0000000..d7e4ec1
--- /dev/null
+++ b/libodb/odb/nested-container.hxx
@@ -0,0 +1,218 @@
+// file : odb/nested-container.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_NESTED_CONTAINER_HXX
+#define ODB_NESTED_CONTAINER_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // size_t
+
+#include <odb/forward.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#ifndef ODB_CXX11
+# error nested container support is only available in C++11
+#endif
+
+namespace odb
+{
+ // Nested container emulation support for ODB.
+ //
+ // In a nutshell, the idea is to represent a nested container, for example,
+ // vector<vector<V>>, as map<nested_key, V> where nested_key is a composite
+ // key consisting of the outer and inner container indexes.
+ //
+ // Note that with this approach the empty trailing entries of the outer
+ // container will not be added on load. It is assumed that the user handles
+ // that on their own, for example, by pre-loading the outer container entry
+ // members if there are any.
+ //
+ // Also note that the outer key in the inner container should strictly
+ // speaking be a foreign key pointing to the key of the outer container. The
+ // only way to achieve this currently is to manually add the constraint via
+ // ALTER TABLE ADD CONSTRAINT. Note, however, that as long as we only modify
+ // these tables via the ODB container interface, not having the foreign key
+ // (and not having ON DELETE CASCADE) should be harmless (since we have a
+ // foreign key pointing to the object id).
+
+ // Map key that is used to emulate 1-level nested container mapping (for
+ // example, vector<vector<V>>). Template parameter IC is a tag that allows
+ // us to distinguish keys for unrelated containers in order to assign column
+ // names, etc. Use the inner container type (for example, vector<V>) for IC.
+ //
+ template <typename IC,
+ typename O = std::size_t,
+ typename I = std::size_t>
+ struct nested_key
+ {
+ using outer_type = O;
+ using inner_type = I;
+
+ outer_type outer;
+ inner_type inner;
+
+ nested_key () = default;
+ nested_key (outer_type o, inner_type i): outer (o), inner (i) {}
+
+ bool
+ operator< (const nested_key& v) const
+ {
+ return outer < v.outer || (outer == v.outer && inner < v.inner);
+ }
+ };
+
+ // Map key that is used to emulate 2-level nested container mapping (for
+ // example, vector<vector<vector<V>>>>). Use the middle container type for
+ // MC (for example, vector<vector<V>>).
+ //
+ template <typename MC,
+ typename O = std::size_t,
+ typename M = std::size_t,
+ typename I = std::size_t>
+ struct nested2_key
+ {
+ using outer_type = O;
+ using middle_type = M;
+ using inner_type = I;
+
+ outer_type outer;
+ middle_type middle;
+ inner_type inner;
+
+ nested2_key () = default;
+ nested2_key (outer_type o, middle_type m, inner_type i)
+ : outer (o), middle (m), inner (i) {}
+
+ bool
+ operator< (const nested2_key& v) const
+ {
+ return outer != v.outer ? outer < v.outer :
+ middle != v.middle ? middle < v.middle :
+ inner < v.inner ;
+ }
+ };
+}
+
+#include <map>
+#include <cstddef> // size_t
+#include <utility> // move(), declval()
+#include <cassert>
+#include <type_traits> // remove_reference
+
+namespace odb
+{
+ template <typename C>
+ struct nested1_type:
+ std::remove_reference<decltype (std::declval<C> ()[0])> {};
+
+ template <typename C>
+ struct nested2_type:
+ std::remove_reference<decltype (std::declval<C> ()[0][0])> {};
+
+ template <typename C>
+ struct nested3_type:
+ std::remove_reference<decltype (std::declval<C> ()[0][0][0])> {};
+
+ // 1-level nesting.
+ //
+ template <typename OC> // For example, OC = vector<vector<V>>.
+ std::map<nested_key<typename nested1_type<OC>::type>,
+ typename nested2_type<OC>::type>
+ nested_get (const OC& oc)
+ {
+ using namespace std;
+
+ using IC = typename nested1_type<OC>::type;
+ using V = typename nested2_type<OC>::type;
+ using K = nested_key<IC>;
+
+ map<K, V> r;
+ for (size_t o (0); o != oc.size (); ++o)
+ {
+ const IC& ic (oc[o]);
+ for (size_t i (0); i != ic.size (); ++i)
+ r.emplace (K (o, i), ic[i]);
+ }
+ return r;
+ }
+
+ template <typename K, typename V, typename OC>
+ void
+ nested_set (OC& oc, std::map<K, V>&& r)
+ {
+ using namespace std;
+
+ for (auto& p: r)
+ {
+ size_t o (p.first.outer);
+ size_t i (p.first.inner);
+ V& v (p.second);
+
+ if (o >= oc.size ())
+ oc.resize (o + 1);
+
+ assert (i == oc[o].size ());
+
+ oc[o].push_back (move (v));
+ }
+ }
+
+ // 2-level nesting.
+ //
+ template <typename OC> // For example, OC = vector<vector<vector<V>>>.
+ std::map<nested2_key<typename nested1_type<OC>::type>,
+ typename nested3_type<OC>::type>
+ nested2_get (const OC& oc)
+ {
+ using namespace std;
+
+ using MC = typename nested1_type<OC>::type;
+ using V = typename nested3_type<OC>::type;
+ using K = nested2_key<MC>;
+
+ map<K, V> r;
+ for (size_t o (0); o != oc.size (); ++o)
+ {
+ const auto& mc (oc[o]);
+ for (size_t m (0); m != mc.size (); ++m)
+ {
+ const auto& ic (mc[m]);
+ for (size_t i (0); i != ic.size (); ++i)
+ r.emplace (K (o, m, i), ic[i]);
+ }
+ }
+ return r;
+ }
+
+ template <typename K, typename V, typename OC>
+ void
+ nested2_set (OC& oc, std::map<K, V>&& r)
+ {
+ using namespace std;
+
+ for (auto& p: r)
+ {
+ size_t o (p.first.outer);
+ size_t m (p.first.middle);
+ size_t i (p.first.inner);
+ V& v (p.second);
+
+ if (o >= oc.size ())
+ oc.resize (o + 1);
+
+ auto& mc (oc[o]);
+
+ if (m >= mc.size ())
+ mc.resize (m + 1);
+
+ assert (i == mc[m].size ());
+
+ mc[m].push_back (move (v));
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_NESTED_CONTAINER_HXX
diff --git a/libodb/odb/no-id-object-result.hxx b/libodb/odb/no-id-object-result.hxx
new file mode 100644
index 0000000..556065b
--- /dev/null
+++ b/libodb/odb/no-id-object-result.hxx
@@ -0,0 +1,182 @@
+// file : odb/no-id-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_NO_ID_OBJECT_RESULT_HXX
+#define ODB_NO_ID_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+#include <utility> // std::move
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+#include <odb/result.hxx>
+#include <odb/object-result.hxx>
+#include <odb/pointer-traits.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ // Implementation for objects without object id (always non-polymorphic).
+ //
+ template <typename T>
+ class no_id_object_result_impl: public result_impl
+ {
+ protected:
+ // In result_impl, T is always non-const and the same as object_type.
+ //
+ typedef T object_type;
+ typedef odb::object_traits<object_type> object_traits;
+
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef odb::pointer_traits<pointer_type> pointer_traits;
+
+ friend class result<T>;
+ friend class result<const T>;
+ friend class result_iterator<T, class_object>;
+ friend class result_iterator<const T, class_object>;
+ friend class object_result_iterator<T, void, false>;
+ friend class object_result_iterator<const T, void, false>;
+
+ protected:
+ no_id_object_result_impl (odb::connection& conn)
+ : result_impl (conn), begin_ (true), end_ (false), current_ ()
+ {
+ }
+
+ // To make this work with all kinds of pointers (raw, std::auto_ptr,
+ // shared), we need to make sure we don't make any copies of the
+ // pointer on the return path.
+ //
+ pointer_type&
+ current ()
+ {
+ if (pointer_traits::null_ptr (current_) && !end_)
+ load ();
+
+ return current_;
+ }
+
+ void
+ release ()
+ {
+ current_ = pointer_type ();
+ guard_.release ();
+ }
+
+ void
+ begin ()
+ {
+ if (begin_)
+ {
+ next ();
+ begin_ = false;
+ }
+ }
+
+ bool
+ end () const
+ {
+ return end_;
+ }
+
+ protected:
+ virtual void
+ load (object_type&) = 0;
+
+ virtual void
+ next () = 0;
+
+ virtual void
+ cache () = 0;
+
+ virtual std::size_t
+ size () = 0;
+
+ protected:
+#ifdef ODB_CXX11
+ void
+ current (pointer_type& p)
+ {
+ current_ = std::move (p);
+ guard_.reset (current_);
+ }
+
+ void
+ current (pointer_type&& p)
+ {
+ current (p);
+ }
+#else
+ void
+ current (pointer_type p)
+ {
+ current_ = p;
+ guard_.reset (current_);
+ }
+#endif
+
+ bool begin_;
+ bool end_;
+
+ private:
+ void
+ load ();
+
+ private:
+ pointer_type current_;
+ typename pointer_traits::guard guard_;
+ };
+
+ template <typename T>
+ class object_result_iterator<T, void, false>
+ {
+ public:
+ // T can be const T while object_type is always non-const.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+
+ typedef no_id_object_result_impl<object_type> result_impl_type;
+
+ public:
+ object_result_iterator (result_impl_type* res)
+ : res_ (res)
+ {
+ }
+
+ public:
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ pointer_type
+ load ()
+ {
+#ifdef ODB_CXX11
+ pointer_type r (std::move (res_->current ()));
+#else
+ pointer_type r (res_->current ());
+#endif
+ res_->release ();
+ return r;
+ }
+
+ void
+ load (object_type& obj)
+ {
+ // Objects without ids are not stored in session cache.
+ //
+ if (!res_->end ())
+ res_->load (obj);
+ }
+
+ protected:
+ result_impl_type* res_;
+ };
+}
+
+#include <odb/no-id-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_NO_ID_OBJECT_RESULT_HXX
diff --git a/libodb/odb/no-id-object-result.txx b/libodb/odb/no-id-object-result.txx
new file mode 100644
index 0000000..886fe4b
--- /dev/null
+++ b/libodb/odb/no-id-object-result.txx
@@ -0,0 +1,21 @@
+// file : odb/no-id-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // object_result_impl
+ //
+
+ template <typename T>
+ void no_id_object_result_impl<T>::
+ load ()
+ {
+ // Objects without ids are not stored in session cache.
+ //
+ pointer_type p (object_traits::create ());
+ object_type& obj (pointer_traits::get_ref (p));
+ current (p);
+ load (obj);
+ }
+}
diff --git a/libodb/odb/no-op-cache-traits.hxx b/libodb/odb/no-op-cache-traits.hxx
new file mode 100644
index 0000000..b6d0518
--- /dev/null
+++ b/libodb/odb/no-op-cache-traits.hxx
@@ -0,0 +1,236 @@
+// file : odb/no-op-cache-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_NO_OP_CACHE_TRAITS_HXX
+#define ODB_NO_OP_CACHE_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/traits.hxx>
+#include <odb/forward.hxx>
+#include <odb/pointer-traits.hxx>
+
+namespace odb
+{
+ // pointer_cache_type
+ //
+ // Used to convert an object pointer to the canonical form (non-const),
+ // suitable for insertion into the cache.
+ //
+ template <typename P,
+ typename E = typename pointer_traits<P>::element_type,
+ typename O = typename object_traits<E>::object_type,
+ pointer_kind pk = pointer_traits<P>::kind>
+ struct pointer_cache_type
+ {
+ typedef typename object_traits<O>::pointer_type pointer_type;
+
+ static pointer_type
+ convert (const P& p)
+ {
+ return pointer_traits<P>::const_pointer_cast (p);
+ }
+ };
+
+ template <typename P, typename T, pointer_kind pk>
+ struct pointer_cache_type<P, T, T, pk>
+ {
+ // If element_type and object_type are the same, then it is already
+ // the canonical pointer.
+ //
+ static const P&
+ convert (const P& p) {return p;}
+ };
+
+ template <typename P, typename E, typename O>
+ struct pointer_cache_type<P, E, O, pk_unique>
+ {
+ // If the pointer is unique, then casting it can transfer ownership.
+ // In this case we return NULL void*, which will be ignored down the
+ // chain.
+ //
+ static void*
+ convert (const P&) {return 0;}
+ };
+
+ template <typename P, typename T>
+ struct pointer_cache_type<P, T, T, pk_unique>
+ {
+ static void*
+ convert (const P&) {return 0;}
+ };
+
+ // reference_cache_type
+ //
+ // Used to convert an object reference to the canonical form (non-const),
+ // suitable for insertion into the cache.
+ //
+ template <typename T,
+ typename O = typename object_traits<T>::object_type>
+ struct reference_cache_type
+ {
+ static O&
+ convert (T& r)
+ {
+ return const_cast<O&> (r);
+ }
+ };
+
+ template <typename T>
+ struct reference_cache_type<T, T>
+ {
+ // If the types are the same, then it is already the canonical form.
+ //
+ static T&
+ convert (T& r) {return r;}
+ };
+
+ // pointer_cache_traits
+ //
+ // Caching traits for objects passed by pointer. P should be the canonical
+ // pointer (non-const).
+ //
+ template <typename P>
+ struct no_op_pointer_cache_traits
+ {
+ typedef P pointer_type;
+ typedef typename pointer_traits<pointer_type>::element_type object_type;
+ typedef typename object_traits<object_type>::id_type id_type;
+ struct position_type {};
+
+ struct insert_guard
+ {
+ insert_guard () {}
+ insert_guard (const position_type&) {}
+
+ position_type
+ position () const {return position_type ();}
+
+ void
+ release () {}
+
+ void
+ reset (const position_type&) {}
+ };
+
+ // Cache management.
+ //
+ static position_type
+ insert (odb::database&, const id_type&, const pointer_type&)
+ {
+ return position_type ();
+ }
+
+ static position_type
+ insert (odb::database&, const pointer_type&) {return position_type ();}
+
+ // Special signature for unique pointers.
+ //
+ static position_type
+ insert (odb::database&, void*) {return position_type ();}
+
+ static pointer_type
+ find (odb::database&, const id_type&) {return pointer_type ();}
+
+ static void
+ erase (const position_type&) {}
+
+ // Notifications.
+ //
+ static void
+ persist (const position_type&) {}
+
+ static void
+ load (const position_type&) {}
+
+ static void
+ update (odb::database&, const object_type&) {}
+
+ static void
+ erase (odb::database&, const id_type&) {}
+ };
+
+ template <typename P>
+ struct no_id_pointer_cache_traits
+ {
+ typedef P pointer_type;
+ struct position_type {};
+
+ static position_type
+ insert (odb::database&, const pointer_type&) {return position_type ();}
+
+ // Special signature for unique pointers.
+ //
+ static position_type
+ insert (odb::database&, void*) {return position_type ();}
+
+ static void
+ persist (const position_type&) {}
+
+ static void
+ load (const position_type&) {}
+ };
+
+ // reference_cache_traits
+ //
+ // Caching traits for objects passed by reference. T should be the
+ // canonical object type (non-const).
+ //
+ template <typename T>
+ struct no_op_reference_cache_traits
+ {
+ typedef T object_type;
+ typedef typename object_traits<object_type>::id_type id_type;
+ struct position_type {};
+
+ struct insert_guard
+ {
+ insert_guard () {}
+ insert_guard (const position_type&) {}
+
+ position_type
+ position () const {return position_type ();}
+
+ void
+ release () {}
+
+ void
+ reset () {}
+ };
+
+ static position_type
+ insert (odb::database&, const id_type&, object_type&)
+ {
+ return position_type ();
+ }
+
+ static position_type
+ insert (odb::database&, object_type&) {return position_type ();}
+
+ static void
+ persist (const position_type&) {}
+
+ static void
+ load (const position_type&) {}
+ };
+
+ template <typename T>
+ struct no_id_reference_cache_traits
+ {
+ typedef T object_type;
+ struct position_type {};
+
+ static position_type
+ insert (odb::database&, object_type&) {return position_type ();}
+
+ static void
+ persist (const position_type&) {}
+
+ static void
+ load (const position_type&) {}
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_NO_OP_CACHE_TRAITS_HXX
diff --git a/libodb/odb/nullable.hxx b/libodb/odb/nullable.hxx
new file mode 100644
index 0000000..d13f0c9
--- /dev/null
+++ b/libodb/odb/nullable.hxx
@@ -0,0 +1,229 @@
+// file : odb/nullable.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_NULLABLE_HXX
+#define ODB_NULLABLE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx> // odb::core
+
+namespace odb
+{
+ template <typename T>
+ class nullable
+ {
+ public:
+ typedef T value_type;
+
+ nullable ();
+ nullable (const T&);
+ nullable (const nullable&);
+ template <typename Y> explicit nullable (const nullable<Y>&);
+
+ nullable& operator= (const T&);
+ nullable& operator= (const nullable&);
+ template <typename Y> nullable& operator= (const nullable<Y>&);
+
+ void swap (nullable&);
+
+ bool null () const;
+
+ T& get ();
+ const T& get () const;
+
+ T* operator-> ();
+ const T* operator-> () const;
+
+ T& operator* ();
+ const T& operator* () const;
+
+ typedef void (nullable::*bool_convertible) ();
+ operator bool_convertible () const
+ {
+ return null_ ? 0 : &nullable<T>::true_value;
+ }
+
+ void reset ();
+
+ private:
+ void true_value () {};
+
+ T value_;
+ bool null_;
+ };
+
+ namespace common
+ {
+ using odb::nullable;
+ }
+
+ template <typename T>
+ inline bool
+ operator== (const nullable<T>& x, const nullable<T>& y)
+ {
+ return x.null () == y.null () && (x.null () || *x == *y);
+ }
+
+ template <typename T>
+ inline bool
+ operator!= (const nullable<T>& x, const nullable<T>& y) {return !(x == y);}
+
+ template <typename T>
+ inline bool
+ operator< (const nullable<T>& x, const nullable<T>& y)
+ {
+ return x.null () > y.null () || (!x.null () && !y.null () && *x < *y);
+ }
+
+ template <typename T>
+ inline bool
+ operator> (const nullable<T>& x, const nullable<T>& y)
+ {
+ return x.null () < y.null () || (!x.null () && !y.null () && *x > *y);
+ }
+
+ template <typename T>
+ inline bool
+ operator<= (const nullable<T>& x, const nullable<T>& y) {return !(x > y);}
+
+ template <typename T>
+ inline bool
+ operator>= (const nullable<T>& x, const nullable<T>& y) {return !(x < y);}
+
+ template <typename T>
+ inline nullable<T>::
+ nullable ()
+ : null_ (true)
+ {
+ }
+
+ template <typename T>
+ inline nullable<T>::
+ nullable (const T& v)
+ : value_ (v), null_ (false)
+ {
+ }
+
+ template <typename T>
+ inline nullable<T>::
+ nullable (const nullable& y)
+ : value_ (y.value_), null_ (y.null_)
+ {
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline nullable<T>::
+ nullable (const nullable<Y>& y)
+ : value_ (y.value_), null_ (y.null_)
+ {
+ }
+
+ template <typename T>
+ inline nullable<T>& nullable<T>::
+ operator= (const T& v)
+ {
+ value_ = v;
+ null_ = false;
+ return *this;
+ }
+
+ template <typename T>
+ inline nullable<T>& nullable<T>::
+ operator= (const nullable& y)
+ {
+ if (this != &y)
+ {
+ value_ = y.value_;
+ null_ = y.null_;
+ }
+
+ return *this;
+ }
+
+ template <typename T>
+ template <typename Y>
+ inline nullable<T>& nullable<T>::
+ operator= (const nullable<Y>& y)
+ {
+ value_ = y.value_;
+ null_ = y.null_;
+ return *this;
+ }
+
+ template <typename T>
+ inline void nullable<T>::
+ swap (nullable& y)
+ {
+ T v (value_);
+ bool n (null_);
+
+ value_ = y.value_;
+ null_ = y.null_;
+
+ y.value_ = v;
+ y.null_ = n;
+ }
+
+ template <typename T>
+ inline bool nullable<T>::
+ null () const
+ {
+ return null_;
+ }
+
+ template <typename T>
+ inline T& nullable<T>::
+ get ()
+ {
+ return value_;
+ }
+
+ template <typename T>
+ inline const T& nullable<T>::
+ get () const
+ {
+ return value_;
+ }
+
+ template <typename T>
+ inline T* nullable<T>::
+ operator-> ()
+ {
+ return null_ ? 0 : &value_;
+ }
+
+ template <typename T>
+ inline const T* nullable<T>::
+ operator-> () const
+ {
+ return null_ ? 0 : &value_;
+ }
+
+ template <typename T>
+ inline T& nullable<T>::
+ operator* ()
+ {
+ return value_;
+ }
+
+ template <typename T>
+ inline const T& nullable<T>::
+ operator* () const
+ {
+ return value_;
+ }
+
+ template <typename T>
+ inline void nullable<T>::
+ reset ()
+ {
+ value_ = T ();
+ null_ = true;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_NULLABLE_HXX
diff --git a/libodb/odb/object-result.hxx b/libodb/odb/object-result.hxx
new file mode 100644
index 0000000..056d518
--- /dev/null
+++ b/libodb/odb/object-result.hxx
@@ -0,0 +1,164 @@
+// file : odb/object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_OBJECT_RESULT_HXX
+#define ODB_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::ptrdiff_t
+#include <iterator> // iterator categories
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+#include <odb/result.hxx>
+#include <odb/pointer-traits.hxx>
+
+namespace odb
+{
+ //
+ // object_result_impl
+ //
+ template <typename T>
+ class object_result_impl;
+
+ template <typename T>
+ class polymorphic_object_result_impl;
+
+ template <typename T>
+ class no_id_object_result_impl;
+
+ //
+ // object_result_impl_selector
+ //
+ template <typename T,
+ typename ID = typename object_traits<T>::id_type,
+ bool polymorphic = object_traits<T>::polymorphic>
+ struct object_result_impl_selector;
+
+ template <typename T, typename ID>
+ struct object_result_impl_selector<T, ID, false>
+ {
+ typedef object_result_impl<T> type;
+ };
+
+ template <typename T, typename ID>
+ struct object_result_impl_selector<T, ID, true>
+ {
+ typedef polymorphic_object_result_impl<T> type;
+ };
+
+ template <typename T>
+ struct object_result_impl_selector<T, void, false>
+ {
+ typedef no_id_object_result_impl<T> type;
+ };
+
+ //
+ // result_iterator
+ //
+
+ template <typename T, typename ID, bool polymorphic>
+ class object_result_iterator;
+
+ template <typename T>
+ class result_iterator<T, class_object>: public object_result_iterator<
+ T,
+ typename object_traits<T>::id_type,
+ object_traits<T>::polymorphic>
+ {
+ public:
+ typedef T value_type;
+ typedef value_type& reference;
+ typedef value_type* pointer;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::input_iterator_tag iterator_category;
+
+ // T can be const T while object_type is always non-const.
+ //
+ typedef
+ object_result_iterator<T,
+ typename object_traits<T>::id_type,
+ object_traits<T>::polymorphic> base_type;
+
+ public:
+ explicit
+ result_iterator (typename base_type::result_impl_type* res = 0)
+ : base_type (res)
+ {
+ }
+
+ // Input iterator requirements.
+ //
+ public:
+ reference
+ operator* () const
+ {
+ return pointer_traits::get_ref (this->res_->current ());
+ }
+
+ // Our value_type is already a pointer so return it instead of
+ // a pointer to it (operator-> will just have to go one deeper
+ // in the latter case).
+ //
+ pointer
+ operator-> () const
+ {
+ return pointer_traits::get_ptr (this->res_->current ());
+ }
+
+ result_iterator&
+ operator++ ()
+ {
+ this->res_->next ();
+ return *this;
+ }
+
+ result_iterator
+ operator++ (int)
+ {
+ // All non-end iterators for a result object move together.
+ //
+ this->res_->next ();
+ return *this;
+ }
+
+ public:
+ bool
+ equal (result_iterator j) const
+ {
+ return (this->res_ ? this->res_->end () : true) ==
+ (j.res_ ? j.res_->end () : true);
+ }
+
+ private:
+ // Use unrestricted pointer traits since that's what is returned by
+ // result_impl.
+ //
+ typedef
+ odb::pointer_traits<
+ typename object_traits<
+ typename base_type::object_type>::pointer_type>
+ pointer_traits;
+ };
+
+ //
+ //
+ template <typename T>
+ class result_base<T, class_object>
+ {
+ public:
+ typedef typename object_traits<T>::pointer_type value_type;
+
+ // T can be const T while object_type is always non-const.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef
+ typename object_result_impl_selector<object_type>::type
+ result_impl_type;
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_OBJECT_RESULT_HXX
diff --git a/libodb/odb/pointer-traits.hxx b/libodb/odb/pointer-traits.hxx
new file mode 100644
index 0000000..a0baa21
--- /dev/null
+++ b/libodb/odb/pointer-traits.hxx
@@ -0,0 +1,405 @@
+// file : odb/pointer-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_POINTER_TRAITS_HXX
+#define ODB_POINTER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <new> // operators new/delete
+#include <memory> // std::auto_ptr, std::unique_ptr, std::shared_ptr/weak_ptr
+#include <cstddef> // std::size_t
+
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/meta/remove-const.hxx>
+
+namespace odb
+{
+ enum pointer_kind
+ {
+ pk_raw, // Raw pointer or equivalent (i.e., unmanaged).
+ pk_unique, // Smart pointer that doesn't support sharing.
+ pk_shared, // Smart pointer that supports sharing.
+ pk_weak // Weak counterpart for shared pointer.
+ };
+
+ template <typename P>
+ class pointer_traits;
+
+ //
+ // Standard pointer guards.
+ //
+
+ // Raw pointer guard.
+ //
+ template <typename P>
+ class raw_ptr_guard
+ {
+ public:
+ ~raw_ptr_guard () {delete p_;}
+ raw_ptr_guard (): p_ (0) {}
+
+ explicit
+ raw_ptr_guard (P p): p_ (p) {}
+
+ void
+ release () {p_ = 0;}
+
+ void
+ reset (P p = 0) {delete p_; p_ = p;}
+
+ private:
+ P p_;
+ };
+
+ // No-op pointer guard for smart pointers.
+ //
+ template <typename P>
+ class smart_ptr_guard
+ {
+ public:
+ smart_ptr_guard () {}
+
+ explicit
+ smart_ptr_guard (const P&) {}
+
+ void
+ release () {}
+
+ void
+ reset () {}
+
+ void
+ reset (const P&) {}
+ };
+
+ // Specialization for raw pointers.
+ //
+ template <typename T>
+ class pointer_traits<T*>
+ {
+ public:
+ static const pointer_kind kind = pk_raw;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef T* pointer_type;
+ typedef const T* const_pointer_type;
+ typedef typename odb::details::meta::remove_const<T>::result*
+ unrestricted_pointer_type;
+ typedef raw_ptr_guard<pointer_type> guard;
+
+ // Return raw pointer to the pointed-to element, including NULL.
+ //
+ static element_type*
+ get_ptr (pointer_type p)
+ {
+ return p;
+ }
+
+ // Return reference to the pointed-to element.
+ //
+ static element_type&
+ get_ref (pointer_type p)
+ {
+ return *p;
+ }
+
+ // Return true if the pointer is NULL.
+ //
+ static bool
+ null_ptr (pointer_type p)
+ {
+ return p == 0;
+ }
+
+ // Casts.
+ //
+ static unrestricted_pointer_type
+ const_pointer_cast (pointer_type p)
+ {
+ return const_cast<unrestricted_pointer_type> (p);
+ }
+
+ template <typename T1>
+ static T1*
+ static_pointer_cast (pointer_type p)
+ {
+ return static_cast<T1*> (p);
+ }
+
+ template <typename T1>
+ static T1*
+ dynamic_pointer_cast (pointer_type p)
+ {
+ return dynamic_cast<T1*> (p);
+ }
+
+ public:
+ // Allocate memory for an element that will be managed by this
+ // pointer.
+ //
+ static void*
+ allocate (std::size_t n)
+ {
+ return operator new (n);
+ }
+
+ // Free memory allocated for an element. This functions is only
+ // called if the constructor of the element being created fails.
+ // Otherwise, the pointer (or guard) is used to delete the object
+ // and free the memory. This behavior is identical to the one
+ // used by operator delete overloading.
+ //
+ static void
+ free (void* p)
+ {
+ operator delete (p);
+ }
+ };
+
+ // Specialization for std::auto_ptr.
+ //
+#ifndef ODB_CXX11
+ template <typename T>
+ class pointer_traits< std::auto_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_unique;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef std::auto_ptr<element_type> pointer_type;
+ typedef std::auto_ptr<const element_type> const_pointer_type;
+ typedef smart_ptr_guard<pointer_type> guard;
+
+ static element_type*
+ get_ptr (const pointer_type& p)
+ {
+ return p.get ();
+ }
+
+ static element_type&
+ get_ref (const pointer_type& p)
+ {
+ return *p;
+ }
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return p.get () == 0;
+ }
+
+ // const_pointer_cast() is not provided.
+ //
+
+ // Note: transfers ownership.
+ //
+ template <typename T1>
+ static std::auto_ptr<T1>
+ static_pointer_cast (pointer_type& p)
+ {
+ return std::auto_ptr<T1> (static_cast<T1*> (p.release ()));
+ }
+
+ // Note: transfers ownership if successful.
+ //
+ template <typename T1>
+ static std::auto_ptr<T1>
+ dynamic_pointer_cast (pointer_type& p)
+ {
+ T1* p1 (dynamic_cast<T1*> (p.get ()));
+
+ if (p1 != 0)
+ p.release ();
+
+ return std::auto_ptr<T1> (p1);
+ }
+
+ public:
+ static void*
+ allocate (std::size_t n)
+ {
+ return operator new (n);
+ }
+
+ static void
+ free (void* p)
+ {
+ operator delete (p);
+ }
+ };
+#endif
+
+#ifdef ODB_CXX11
+
+ // Specialization for C++11 std::unique_ptr.
+ //
+ template <typename T, template <typename> class D>
+ class pointer_traits<std::unique_ptr<T, D<T>>>
+ {
+ public:
+ static const pointer_kind kind = pk_unique;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef std::unique_ptr<T, D<T>> pointer_type;
+ typedef std::unique_ptr<const T, D<const T>> const_pointer_type;
+ typedef smart_ptr_guard<pointer_type> guard;
+
+ static element_type*
+ get_ptr (const pointer_type& p)
+ {
+ return p.get ();
+ }
+
+ static element_type&
+ get_ref (const pointer_type& p)
+ {
+ return *p;
+ }
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ // const_pointer_cast() is not provided.
+ //
+
+ // Note: transfers ownership.
+ //
+ template <typename T1>
+ static std::unique_ptr<T1, D<T1>>
+ static_pointer_cast (pointer_type& p)
+ {
+ return std::unique_ptr<T1, D<T1>> (static_cast<T1*> (p.release ()));
+ }
+
+ // Note: transfers ownership if successful.
+ //
+ template <typename T1>
+ static std::unique_ptr<T1, D<T1>>
+ dynamic_pointer_cast (pointer_type& p)
+ {
+ T1* p1 (dynamic_cast<T1*> (p.get ()));
+
+ if (p1 != 0)
+ p.release ();
+
+ return std::unique_ptr<T1, D<T1>> (p1);
+ }
+
+ public:
+ static void*
+ allocate (std::size_t n)
+ {
+ return operator new (n);
+ }
+
+ static void
+ free (void* p)
+ {
+ operator delete (p);
+ }
+ };
+
+ // Specialization for C++11 std::shared_ptr.
+ //
+ template <typename T>
+ class pointer_traits<std::shared_ptr<T>>
+ {
+ public:
+ static const pointer_kind kind = pk_shared;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef std::shared_ptr<element_type> pointer_type;
+ typedef std::shared_ptr<const element_type> const_pointer_type;
+ typedef typename odb::details::meta::remove_const<element_type>::result
+ unrestricted_element_type;
+ typedef std::shared_ptr<unrestricted_element_type>
+ unrestricted_pointer_type;
+ typedef smart_ptr_guard<pointer_type> guard;
+
+ static element_type*
+ get_ptr (const pointer_type& p)
+ {
+ return p.get ();
+ }
+
+ static element_type&
+ get_ref (const pointer_type& p)
+ {
+ return *p;
+ }
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ static unrestricted_pointer_type
+ const_pointer_cast (const pointer_type& p)
+ {
+ return std::const_pointer_cast<unrestricted_element_type> (p);
+ }
+
+ template <typename T1>
+ static std::shared_ptr<T1>
+ static_pointer_cast (const pointer_type& p)
+ {
+ return std::static_pointer_cast<T1> (p);
+ }
+
+ template <typename T1>
+ static std::shared_ptr<T1>
+ dynamic_pointer_cast (const pointer_type& p)
+ {
+ return std::dynamic_pointer_cast<T1> (p);
+ }
+
+ public:
+ static void*
+ allocate (std::size_t n)
+ {
+ return operator new (n);
+ }
+
+ static void
+ free (void* p)
+ {
+ operator delete (p);
+ }
+ };
+
+ // Specialization for C++11 std::weak_ptr.
+ //
+ template <typename T>
+ class pointer_traits<std::weak_ptr<T>>
+ {
+ public:
+ static const pointer_kind kind = pk_weak;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef std::weak_ptr<element_type> pointer_type;
+ typedef std::shared_ptr<element_type> strong_pointer_type;
+
+ static strong_pointer_type
+ lock (const pointer_type& p)
+ {
+ return p.lock ();
+ }
+ };
+
+#endif // ODB_CXX11
+
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_POINTER_TRAITS_HXX
diff --git a/libodb/odb/polymorphic-info.hxx b/libodb/odb/polymorphic-info.hxx
new file mode 100644
index 0000000..0f410d5
--- /dev/null
+++ b/libodb/odb/polymorphic-info.hxx
@@ -0,0 +1,188 @@
+// file : odb/polymorphic-info.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_POLYMORPHIC_INFO_HXX
+#define ODB_POLYMORPHIC_INFO_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+#include <typeinfo>
+
+#include <odb/forward.hxx> // database, connection
+#include <odb/schema-version.hxx>
+#include <odb/traits.hxx>
+
+namespace odb
+{
+ template <typename R>
+ struct polymorphic_abstract_info
+ {
+ typedef void (*section_load) (odb::connection&, R&, bool top);
+ typedef void (*section_update) (odb::connection&, const R&);
+
+ struct section_functions
+ {
+ section_load load;
+ section_update update;
+ };
+
+ struct section_list
+ {
+ std::size_t count;
+ const section_functions* functions;
+ };
+
+ public:
+ polymorphic_abstract_info (const std::type_info& t,
+ const polymorphic_abstract_info* b,
+ const section_list* s)
+ : type (t), base (b), sections (s) {}
+
+ bool
+ derived (const polymorphic_abstract_info& b) const
+ {
+ for (const polymorphic_abstract_info* p (base); p != 0; p = p->base)
+ if (&b == p)
+ return true;
+
+ return false;
+ }
+
+ // Find the "most overridden" section functions.
+ //
+ section_load
+ find_section_load (std::size_t index) const
+ {
+ for (const polymorphic_abstract_info* b (this); b != 0; b = b->base)
+ if (b->sections != 0 &&
+ index < b->sections->count &&
+ b->sections->functions[index].load != 0)
+ return b->sections->functions[index].load;
+
+ return 0;
+ }
+
+ section_update
+ find_section_update (std::size_t index) const
+ {
+ for (const polymorphic_abstract_info* b (this); b != 0; b = b->base)
+ if (b->sections != 0 &&
+ index < b->sections->count &&
+ b->sections->functions[index].update != 0)
+ return b->sections->functions[index].update;
+
+ return 0;
+ }
+
+ bool
+ final_section_update (const polymorphic_abstract_info& i,
+ std::size_t index) const
+ {
+ return i.sections != 0 &&
+ index < i.sections->count &&
+ i.sections->functions[index].update != 0 &&
+ i.sections->functions[index].update == find_section_update (index);
+ }
+
+ public:
+ const std::type_info& type;
+ const polymorphic_abstract_info* base;
+
+ // Sections.
+ //
+ // There could be "concrete" (i.e., not overridden) section in an
+ // abstract class. Which means the section table has to be in
+ // abstract_info.
+ //
+ const section_list* sections;
+ };
+
+ template <typename R>
+ struct polymorphic_concrete_info: polymorphic_abstract_info<R>
+ {
+ // Have to use access::object_traits directly because of VC10.
+ //
+ typedef R root_type;
+ typedef access::object_traits<root_type> root_traits;
+ typedef typename root_traits::id_type id_type;
+ typedef typename root_traits::pointer_type pointer_type;
+ typedef typename root_traits::discriminator_type discriminator_type;
+
+ typedef typename polymorphic_abstract_info<R>::section_list section_list;
+
+ enum call_type
+ {
+ call_callback, // arg points to callback event.
+ call_persist, // arg is not used.
+ call_update, // arg is not used.
+ call_find, // arg points to object id.
+ call_reload, // arg is not used.
+ call_load, // arg points to depth.
+ call_erase // arg points to object id.
+ };
+
+ typedef pointer_type (*create_function) ();
+ typedef bool (*dispatch_function) (
+ call_type, odb::database&, const root_type*, const void* arg);
+ typedef void (*delayed_loader_function) (
+ odb::database&,
+ const id_type&,
+ root_type&,
+ const schema_version_migration*);
+
+ public:
+ polymorphic_concrete_info (const std::type_info& t,
+ const polymorphic_abstract_info<R>* b,
+ const section_list* s,
+ const discriminator_type& d,
+ create_function cf,
+ dispatch_function df,
+ delayed_loader_function dlf)
+ : polymorphic_abstract_info<R> (t, b, s),
+ discriminator (d),
+ create (cf), dispatch (df), delayed_loader (dlf)
+ {
+ }
+
+ public:
+ discriminator_type discriminator;
+ create_function create;
+ dispatch_function dispatch;
+ delayed_loader_function delayed_loader;
+ };
+
+ // Register concrete type T in the root's map.
+ //
+ template <typename T, database_id DB>
+ struct polymorphic_entry
+ {
+ typedef T object_type;
+ typedef object_traits_impl<object_type, DB> object_traits;
+ typedef typename object_traits::root_type root_type;
+
+ polymorphic_entry ();
+ ~polymorphic_entry ();
+ };
+
+ // Helper functions that either return the concrete info or NULL
+ // depending on what kind of info we pass (used in query support).
+ //
+ template <typename R>
+ inline const polymorphic_concrete_info<R>*
+ polymorphic_info (const polymorphic_concrete_info<R>& i)
+ {
+ return &i;
+ }
+
+ template <typename R>
+ inline const polymorphic_concrete_info<R>*
+ polymorphic_info (const polymorphic_abstract_info<R>&)
+ {
+ return 0;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_POLYMORPHIC_INFO_HXX
diff --git a/libodb/odb/polymorphic-map.hxx b/libodb/odb/polymorphic-map.hxx
new file mode 100644
index 0000000..2e61314
--- /dev/null
+++ b/libodb/odb/polymorphic-map.hxx
@@ -0,0 +1,276 @@
+// file : odb/polymorphic-map.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_POLYMORPHIC_MAP_HXX
+#define ODB_POLYMORPHIC_MAP_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <utility> // std::move
+#include <cstddef> // std::size_t
+#include <cassert>
+#include <typeinfo>
+
+#include <odb/callback.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/type-info.hxx>
+
+#include <odb/polymorphic-info.hxx>
+
+namespace odb
+{
+ template <typename R>
+ struct polymorphic_map
+ {
+ typedef R root_type;
+ typedef polymorphic_concrete_info<root_type> info_type;
+ typedef typename info_type::discriminator_type discriminator_type;
+
+ polymorphic_map (): ref_count_ (1) {}
+
+ const info_type&
+ find (const std::type_info& t) const;
+
+ const info_type&
+ find (const discriminator_type& d) const;
+
+ public:
+ typedef
+ std::map<const std::type_info*,
+ const info_type*,
+ odb::details::type_info_comparator> // VC bug.
+ type_map;
+
+ struct discriminator_comparator
+ {
+ bool
+ operator() (const discriminator_type* x,
+ const discriminator_type* y) const
+ {
+ return *x < *y;
+ }
+ };
+
+ typedef
+ std::map<const discriminator_type*,
+ const info_type*,
+ discriminator_comparator>
+ discriminator_map;
+
+ public:
+ std::size_t ref_count_;
+ type_map type_map_;
+ discriminator_map discriminator_map_;
+ };
+
+ template <typename R, database_id DB>
+ struct polymorphic_entry_impl
+ {
+ typedef R root_type;
+ typedef object_traits_impl<root_type, DB> root_traits;
+ typedef polymorphic_concrete_info<root_type> info_type;
+
+ static void
+ insert (const info_type&);
+
+ static void
+ erase (const info_type&);
+ };
+
+ template <typename T>
+ typename object_traits<typename object_traits<T>::root_type>::pointer_type
+ create_impl ()
+ {
+ typedef object_traits<T> derived_traits;
+ typedef object_traits<typename derived_traits::root_type> root_traits;
+
+ typedef typename derived_traits::pointer_type derived_pointer_type;
+ typedef typename root_traits::pointer_type root_pointer_type;
+
+ derived_pointer_type p (
+ access::object_factory<T, derived_pointer_type>::create ());
+
+ // Implicit downcast.
+ //
+#ifdef ODB_CXX11
+ root_pointer_type r (std::move (p));
+#else
+ root_pointer_type r (p);
+#endif
+ return r;
+ }
+
+ template <typename T, database_id DB, typename R>
+ struct dispatch_load
+ {
+ static void
+ call (database& db, T& obj, std::size_t d)
+ {
+ object_traits_impl<T, DB>::load_ (db, obj, d);
+ }
+ };
+
+ template <typename R, database_id DB>
+ struct dispatch_load<R, DB, R>
+ {
+ static void
+ call (database&, R&, std::size_t)
+ {
+ assert (false);
+ }
+ };
+
+ template <typename T, database_id DB, bool auto_id>
+ struct dispatch_persist
+ {
+ static void
+ call (database& db, const T& obj)
+ {
+ // Top-level call, no dynamic type checking.
+ //
+ object_traits_impl<T, DB>::persist (db, obj, true, false);
+ }
+ };
+
+ template <typename T, database_id DB>
+ struct dispatch_persist<T, DB, true>
+ {
+ static void
+ call (database& db, const T& obj)
+ {
+ // Top-level call, no dynamic type checking.
+ //
+ object_traits_impl<T, DB>::persist (
+ db, const_cast<T&> (obj), true, false);
+ }
+ };
+
+ template <typename T, database_id DB>
+ bool
+ dispatch_impl (
+ typename polymorphic_concrete_info<
+ typename object_traits<T>::root_type>::call_type c,
+ database& db,
+ const typename object_traits<T>::root_type* pobj,
+ const void* arg)
+ {
+ typedef object_traits_impl<T, DB> derived_traits;
+ typedef typename derived_traits::root_type root_type;
+ typedef object_traits_impl<root_type, DB> root_traits;
+ typedef typename root_traits::id_type id_type;
+ typedef polymorphic_concrete_info<root_type> info_type;
+
+ bool r (false);
+
+ switch (c)
+ {
+ case info_type::call_callback:
+ {
+ derived_traits::callback (
+ db,
+ *const_cast<T*> (static_cast<const T*> (pobj)),
+ *static_cast<const callback_event*> (arg));
+ break;
+ }
+ case info_type::call_persist:
+ {
+ dispatch_persist<T, DB, root_traits::auto_id>::call (
+ db,
+ *static_cast<const T*> (pobj));
+ break;
+ }
+ case info_type::call_update:
+ {
+ derived_traits::update (
+ db,
+ *static_cast<const T*> (pobj),
+ true, // Top-level call.
+ false); // No dynamic type checking.
+ break;
+ }
+ case info_type::call_find:
+ {
+ r = derived_traits::find (
+ db,
+ *static_cast<const id_type*> (arg),
+ *const_cast<T*> (static_cast<const T*> (pobj)),
+ false); // No dynamic type checking.
+ break;
+ }
+ case info_type::call_reload:
+ {
+ r = derived_traits::reload (
+ db,
+ *const_cast<T*> (static_cast<const T*> (pobj)),
+ false); // No dynamic type checking.
+ break;
+ }
+ case info_type::call_load:
+ {
+ dispatch_load<T, DB, root_type>::call (
+ db,
+ *const_cast<T*> (static_cast<const T*> (pobj)),
+ *static_cast<const std::size_t*> (arg));
+ break;
+ }
+ case info_type::call_erase:
+ {
+ if (pobj != 0)
+ derived_traits::erase (
+ db,
+ *static_cast<const T*> (pobj),
+ true, // Top-level call.
+ false); // No dynamic type checking.
+ else
+ derived_traits::erase (
+ db,
+ *static_cast<const id_type*> (arg),
+ true, // Top-level call.
+ false); // No dynamic type checking.
+ break;
+ }
+ }
+
+ return r;
+ }
+
+ template <typename T, database_id DB, typename ST>
+ void
+ section_load_impl (odb::connection& conn,
+ typename object_traits<T>::root_type& obj,
+ bool top)
+ {
+ typedef object_traits_impl<T, DB> derived_traits;
+ typedef typename derived_traits::statements_type statements_type;
+ typedef typename statements_type::connection_type connection_type;
+
+ connection_type& c (static_cast<connection_type&> (conn));
+ statements_type& sts (c.statement_cache ().template find_object<T> ());
+
+ ST::load (sts.extra_statement_cache (), static_cast<T&> (obj), top);
+ }
+
+ template <typename T, database_id DB, typename ST>
+ void
+ section_update_impl (odb::connection& conn,
+ const typename object_traits<T>::root_type& obj)
+ {
+ typedef object_traits_impl<T, DB> derived_traits;
+ typedef typename derived_traits::statements_type statements_type;
+ typedef typename statements_type::connection_type connection_type;
+
+ connection_type& c (static_cast<connection_type&> (conn));
+ statements_type& sts (c.statement_cache ().template find_object<T> ());
+
+ ST::update (sts.extra_statement_cache (), static_cast<const T&> (obj));
+ }
+}
+
+#include <odb/polymorphic-map.ixx>
+#include <odb/polymorphic-map.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_POLYMORPHIC_MAP_HXX
diff --git a/libodb/odb/polymorphic-map.ixx b/libodb/odb/polymorphic-map.ixx
new file mode 100644
index 0000000..4e00c46
--- /dev/null
+++ b/libodb/odb/polymorphic-map.ixx
@@ -0,0 +1,19 @@
+// file : odb/polymorphic-map.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ template <typename T, database_id DB>
+ inline polymorphic_entry<T, DB>::
+ polymorphic_entry ()
+ {
+ polymorphic_entry_impl<root_type, DB>::insert (object_traits::info);
+ }
+
+ template <typename T, database_id DB>
+ inline polymorphic_entry<T, DB>::
+ ~polymorphic_entry ()
+ {
+ polymorphic_entry_impl<root_type, DB>::erase (object_traits::info);
+ }
+}
diff --git a/libodb/odb/polymorphic-map.txx b/libodb/odb/polymorphic-map.txx
new file mode 100644
index 0000000..9e0c0f0
--- /dev/null
+++ b/libodb/odb/polymorphic-map.txx
@@ -0,0 +1,75 @@
+// file : odb/polymorphic-map.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/exceptions.hxx> // no_type_info
+
+namespace odb
+{
+ //
+ // polymorphic_map
+ //
+
+ template <typename R>
+ const typename polymorphic_map<R>::info_type& polymorphic_map<R>::
+ find (const std::type_info& t) const
+ {
+ typename type_map::const_iterator i (type_map_.find (&t));
+
+ if (i != type_map_.end ())
+ return *i->second;
+ else
+ throw no_type_info ();
+ }
+
+ template <typename R>
+ const typename polymorphic_map<R>::info_type& polymorphic_map<R>::
+ find (const discriminator_type& d) const
+ {
+ typename discriminator_map::const_iterator i (
+ discriminator_map_.find (&d));
+
+ if (i != discriminator_map_.end ())
+ return *i->second;
+ else
+ throw no_type_info ();
+ }
+
+ //
+ // polymorphic_entry_impl
+ //
+
+ template <typename R, database_id DB>
+ void polymorphic_entry_impl<R, DB>::
+ insert (const info_type& i)
+ {
+ // VC10 cannot grok constructor call syntax here.
+ //
+ polymorphic_map<root_type>*& pm = root_traits::map;
+
+ if (pm == 0)
+ pm = new polymorphic_map<root_type>;
+ else
+ pm->ref_count_++;
+
+ pm->type_map_[&i.type] = &i;
+ pm->discriminator_map_[&i.discriminator] = &i;
+ }
+
+ template <typename R, database_id DB>
+ void polymorphic_entry_impl<R, DB>::
+ erase (const info_type& i)
+ {
+ // VC10 cannot grok constructor call syntax here.
+ //
+ polymorphic_map<root_type>*& pm = root_traits::map;
+
+ pm->discriminator_map_.erase (&i.discriminator);
+ pm->type_map_.erase (&i.type);
+
+ if (--pm->ref_count_ == 0)
+ {
+ delete pm;
+ pm = 0;
+ }
+ }
+}
diff --git a/libodb/odb/polymorphic-object-result.hxx b/libodb/odb/polymorphic-object-result.hxx
new file mode 100644
index 0000000..5498856
--- /dev/null
+++ b/libodb/odb/polymorphic-object-result.hxx
@@ -0,0 +1,224 @@
+// file : odb/polymorphic-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_POLYMORPHIC_OBJECT_RESULT_HXX
+#define ODB_POLYMORPHIC_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+#include <utility> // std::move
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+#include <odb/result.hxx>
+#include <odb/object-result.hxx>
+#include <odb/pointer-traits.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ // Implementation for polymorphic objects with object id.
+ //
+ template <typename T>
+ class polymorphic_object_result_impl: public result_impl
+ {
+ protected:
+ // In result_impl, T is always non-const and the same as object_type.
+ //
+ typedef T object_type;
+ typedef odb::object_traits<object_type> object_traits;
+ typedef typename object_traits::id_type id_type;
+
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef odb::pointer_traits<pointer_type> pointer_traits;
+
+ typedef typename object_traits::root_type root_type;
+ typedef odb::object_traits<root_type> root_traits;
+ typedef typename root_traits::discriminator_type discriminator_type;
+
+ friend class result<T>;
+ friend class result<const T>;
+ friend class result_iterator<T, class_object>;
+ friend class result_iterator<const T, class_object>;
+ friend class object_result_iterator<T, id_type, true>;
+ friend class object_result_iterator<const T, id_type, true>;
+
+ protected:
+ polymorphic_object_result_impl (odb::connection& conn)
+ : result_impl (conn), begin_ (true), end_ (false), current_ ()
+ {
+ }
+
+ // To make this work with all kinds of pointers (raw, std::auto_ptr,
+ // shared), we need to make sure we don't make any copies of the
+ // pointer on the return path.
+ //
+ pointer_type&
+ current ()
+ {
+ if (pointer_traits::null_ptr (current_) && !end_)
+ load ();
+
+ return current_;
+ }
+
+ void
+ release ()
+ {
+ current_ = pointer_type ();
+ guard_.release ();
+ }
+
+ void
+ begin ()
+ {
+ if (begin_)
+ {
+ next ();
+ begin_ = false;
+ }
+ }
+
+ bool
+ end () const
+ {
+ return end_;
+ }
+
+ protected:
+ // The fetch argument is a hint to the implementation. If it is
+ // false then it means load_id() was already called (and presumably
+ // fetched the data into the object image) and the object image
+ // is still valid (so the implementation doesn't need to fetch
+ // the data again).
+ //
+ // The load() signature differs from the non-polymorphic cases in
+ // that we pass a pointer to object instead of a reference. The
+ // object is only passed if the user requests loading into an
+ // existing instance. Otherwise, we pass NULL and load() is
+ // responsible for creating the object of a correct dynamic
+ // type and managing the object cache insertion.
+ //
+ virtual void
+ load (object_type*, bool fetch = true) = 0;
+
+ virtual id_type
+ load_id () = 0;
+
+ virtual discriminator_type
+ load_discriminator () = 0;
+
+ virtual void
+ next () = 0;
+
+ virtual void
+ cache () = 0;
+
+ virtual std::size_t
+ size () = 0;
+
+ protected:
+#ifdef ODB_CXX11
+ void
+ current (pointer_type& p, bool guard = true)
+ {
+ current_ = std::move (p);
+
+ if (guard)
+ guard_.reset (current_);
+ else
+ guard_.reset ();
+ }
+
+ void
+ current (pointer_type&& p, bool guard = true)
+ {
+ current (p, guard);
+ }
+#else
+ void
+ current (pointer_type p, bool guard = true)
+ {
+ current_ = p;
+
+ if (guard)
+ guard_.reset (current_);
+ else
+ guard_.reset ();
+ }
+#endif
+
+ bool begin_;
+ bool end_;
+
+ private:
+ void
+ load ();
+
+ private:
+ pointer_type current_;
+ typename pointer_traits::guard guard_;
+ };
+
+ template <typename T, typename ID>
+ class object_result_iterator<T, ID, true>
+ {
+ public:
+ // T can be const T while object_type is always non-const.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef typename object_traits<T>::id_type id_type;
+ typedef typename object_traits<T>::root_type root_type;
+ typedef typename object_traits<root_type>::discriminator_type
+ discriminator_type;
+
+ typedef polymorphic_object_result_impl<object_type> result_impl_type;
+
+ public:
+ object_result_iterator (result_impl_type* res)
+ : res_ (res)
+ {
+ }
+
+ public:
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ pointer_type
+ load ()
+ {
+#ifdef ODB_CXX11
+ pointer_type r (std::move (res_->current ()));
+#else
+ pointer_type r (res_->current ());
+#endif
+ res_->release ();
+ return r;
+ }
+
+ void
+ load (object_type&);
+
+ id_type
+ id ()
+ {
+ return res_->load_id ();
+ }
+
+ discriminator_type
+ discriminator ()
+ {
+ return res_->load_discriminator ();
+ }
+
+ protected:
+ result_impl_type* res_;
+ };
+}
+
+#include <odb/polymorphic-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_POLYMORPHIC_OBJECT_RESULT_HXX
diff --git a/libodb/odb/polymorphic-object-result.txx b/libodb/odb/polymorphic-object-result.txx
new file mode 100644
index 0000000..d9252e9
--- /dev/null
+++ b/libodb/odb/polymorphic-object-result.txx
@@ -0,0 +1,70 @@
+// file : odb/polymorphic-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/exceptions.hxx>
+
+namespace odb
+{
+ //
+ // polymorphic_object_result_impl
+ //
+
+ template <typename T>
+ void polymorphic_object_result_impl<T>::
+ load ()
+ {
+ typedef typename root_traits::pointer_type root_pointer_type;
+ typedef typename root_traits::pointer_traits root_pointer_traits;
+
+ // First check the session.
+ //
+ const id_type& id (load_id ());
+
+ root_pointer_type rp (
+ object_traits::pointer_cache_traits::find (this->db_, id));
+
+ if (!root_pointer_traits::null_ptr (rp))
+ {
+ // Check if the types match.
+ //
+ pointer_type p (
+ root_pointer_traits::template dynamic_pointer_cast<
+ object_type> (rp));
+
+ if (!pointer_traits::null_ptr (p))
+ current (p, false); // Pointer from cache should not be guarded.
+ else
+ // We have an object in session that has a different type
+ // compared to the one in the database.
+ //
+ throw object_not_persistent (); // @@ type_mismatch?
+ }
+ else
+ // load() is responsible for creating the object of a correct
+ // dynamic type and for object cache insertion.
+ //
+ load (0, false);
+ }
+
+ //
+ // object_result_iterator
+ //
+
+ template <typename T, typename ID>
+ void object_result_iterator<T, ID, true>::
+ load (object_type& obj)
+ {
+ if (res_->end ())
+ return;
+
+ typedef odb::object_traits<object_type> object_traits;
+
+ typename object_traits::reference_cache_traits::position_type p (
+ object_traits::reference_cache_traits::insert (
+ res_->db_, res_->load_id (), obj));
+ typename object_traits::reference_cache_traits::insert_guard ig (p);
+ res_->load (&obj, false);
+ object_traits::reference_cache_traits::load (p);
+ ig.release ();
+ }
+}
diff --git a/libodb/odb/post.hxx b/libodb/odb/post.hxx
new file mode 100644
index 0000000..3cdfd94
--- /dev/null
+++ b/libodb/odb/post.hxx
@@ -0,0 +1,6 @@
+// file : odb/post.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
diff --git a/libodb/odb/pre.hxx b/libodb/odb/pre.hxx
new file mode 100644
index 0000000..088f172
--- /dev/null
+++ b/libodb/odb/pre.hxx
@@ -0,0 +1,23 @@
+// file : odb/pre.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifdef _MSC_VER
+ // Push warning state.
+ //
+# pragma warning (push, 3)
+
+ // Disabled warnings.
+ //
+# pragma warning (disable:4068) // unknown pragma
+# pragma warning (disable:4251) // needs to have DLL-interface
+# pragma warning (disable:4290) // exception specification ignored
+# pragma warning (disable:4355) // passing 'this' to a member
+# pragma warning (disable:4800) // forcing value to bool
+# pragma warning (disable:4231) // non-standard extension (extern template)
+# pragma warning (disable:4275) // "C4251 is essentially noise and can be
+ // silenced" - Stephan T. Lavavej [And C4275
+ // is essentially the same thing.]
+ // Elevated warnings.
+ //
+# pragma warning (2:4239) // standard doesn't allow this conversion
+#endif
diff --git a/libodb/odb/prepared-query.cxx b/libodb/odb/prepared-query.cxx
new file mode 100644
index 0000000..70bcaa1
--- /dev/null
+++ b/libodb/odb/prepared-query.cxx
@@ -0,0 +1,47 @@
+// file : odb/prepared-query.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/connection.hxx>
+#include <odb/transaction.hxx>
+#include <odb/prepared-query.hxx>
+
+namespace odb
+{
+ prepared_query_impl::
+ ~prepared_query_impl ()
+ {
+ if (next_ != this)
+ list_remove ();
+ }
+
+ prepared_query_impl::
+ prepared_query_impl (connection& c)
+ : cached (false), conn (c), prev_ (0), next_ (this)
+ {
+ // Add to the list.
+ //
+ next_ = conn.prepared_queries_;
+ conn.prepared_queries_ = this;
+
+ if (next_ != 0)
+ next_->prev_ = this;
+ }
+
+ bool prepared_query_impl::
+ verify_connection (transaction& t)
+ {
+ return &t.connection () == &stmt->connection ();
+ }
+
+ void prepared_query_impl::
+ list_remove ()
+ {
+ (prev_ == 0 ? conn.prepared_queries_ : prev_->next_) = next_;
+
+ if (next_ != 0)
+ next_->prev_ = prev_;
+
+ prev_ = 0;
+ next_ = this;
+ }
+}
diff --git a/libodb/odb/prepared-query.hxx b/libodb/odb/prepared-query.hxx
new file mode 100644
index 0000000..7cac6da
--- /dev/null
+++ b/libodb/odb/prepared-query.hxx
@@ -0,0 +1,201 @@
+// file : odb/prepared-query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_PREPARED_QUERY_HXX
+#define ODB_PREPARED_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx> // odb::core
+#include <odb/traits.hxx>
+#include <odb/result.hxx>
+#include <odb/statement.hxx>
+
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+namespace odb
+{
+ class LIBODB_EXPORT prepared_query_impl: public details::shared_base
+ {
+ public:
+ virtual
+ ~prepared_query_impl ();
+
+ prepared_query_impl (connection&);
+
+ // Verify this prepared query and the specified transaction use the
+ // same connection.
+ //
+ virtual bool
+ verify_connection (transaction&);
+
+ bool cached;
+ connection& conn;
+ const char* name;
+ details::shared_ptr<statement> stmt;
+ details::shared_ptr<result_impl> (*execute) (prepared_query_impl&);
+
+ private:
+ prepared_query_impl (const prepared_query_impl&);
+ prepared_query_impl& operator= (const prepared_query_impl&);
+
+ // Doubly-linked list of results.
+ //
+ // prev_ == 0 means we are the first element.
+ // next_ == 0 means we are the last element.
+ // next_ == this means we are not on the list.
+ //
+ protected:
+ friend class connection;
+
+ void
+ list_remove ();
+
+ prepared_query_impl* prev_;
+ prepared_query_impl* next_;
+ };
+
+ template <typename T>
+ struct prepared_query
+ {
+ // Cached version.
+ //
+ explicit
+ prepared_query (prepared_query_impl* impl = 0): impl_ (impl) {}
+
+ // Uncached version.
+ //
+ explicit
+ prepared_query (const details::shared_ptr<prepared_query_impl>& impl)
+ : impl_ (impl.get ())
+ {
+ impl_->_inc_ref ();
+ }
+
+ result<T>
+ execute (bool cache = true)
+ {
+ typedef
+ typename result_base<T, class_traits<T>::kind>::result_impl_type
+ derived_type;
+
+ details::shared_ptr<result_impl> ri (impl_->execute (*impl_));
+ result<T> r (
+ details::shared_ptr<derived_type> (
+ static_cast<derived_type*> (ri.release ())));
+
+ if (cache)
+ r.cache ();
+
+ return r;
+ }
+
+ typename object_traits<T>::pointer_type
+ execute_one ()
+ {
+ return execute (false).one ();
+ }
+
+ bool
+ execute_one (T& object)
+ {
+ return execute (false).one (object);
+ }
+
+ T
+ execute_value ()
+ {
+ // Compiler error pointing here? The object must be default-
+ // constructible in order to use the return-by-value API.
+ //
+ T o;
+ execute (false).value (o);
+ return o;
+ }
+
+ const char*
+ name () const
+ {
+ return impl_->name;
+ }
+
+ typedef odb::statement statement_type;
+
+ statement_type&
+ statement () const
+ {
+ return *impl_->stmt;
+ }
+
+ typedef prepared_query_impl* prepared_query::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return impl_ ? &prepared_query::impl_ : 0;
+ }
+
+ public:
+ ~prepared_query ()
+ {
+ if (impl_ != 0 && !impl_->cached && impl_->_dec_ref ())
+ delete impl_;
+ }
+
+ prepared_query (const prepared_query& x)
+ : impl_ (x.impl_)
+ {
+ if (!impl_->cached)
+ impl_->_inc_ref ();
+ }
+
+ prepared_query&
+ operator= (const prepared_query& x)
+ {
+ if (impl_ != x.impl_)
+ {
+ if (impl_ != 0 && !impl_->cached && impl_->_dec_ref ())
+ delete impl_;
+
+ impl_ = x.impl_;
+
+ if (!impl_->cached)
+ impl_->_inc_ref ();
+ }
+
+ return *this;
+ }
+
+ private:
+ // Ideally, we would just use shared_ptr to manage the impl object.
+ // However, there is a problem if the prepared query is cached on
+ // the connection and the connection is released early when the
+ // transaction is committed or rolled back. In this case, the
+ // prepared_query object might still be around pointing to impl. If
+ // this connection and the prepared query are then used by another
+ // thread while we release the impl object, then we have a race
+ // condition.
+ //
+ // To work around this problem we will simply "reference" the impl
+ // object without counting if the prepared query is cached. For
+ // transition from pointer to reference, see cache_query_() in
+ // connection.cxx.
+ //
+ // You may also observe that in order to know whether this is a
+ // cached prepared query or not, we have to read the cached data
+ // member in the impl object. This does not cause a race because,
+ // unlike the reference count, this member is immutable once set
+ // to true.
+ //
+ friend class connection;
+ prepared_query_impl* impl_;
+ };
+
+ namespace common
+ {
+ using odb::prepared_query;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_PREPARED_QUERY_HXX
diff --git a/libodb/odb/query-dynamic.cxx b/libodb/odb/query-dynamic.cxx
new file mode 100644
index 0000000..bf9fd9c
--- /dev/null
+++ b/libodb/odb/query-dynamic.cxx
@@ -0,0 +1,200 @@
+// file : odb/query-dynamic.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/query-dynamic.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ // query_param
+ //
+ query_param::
+ ~query_param ()
+ {
+ }
+
+ // query_base
+ //
+ void query_base::
+ clear ()
+ {
+ for (clause_type::iterator i (clause_.begin ()); i != clause_.end (); ++i)
+ {
+ if (i->kind == clause_part::kind_param_val ||
+ i->kind == clause_part::kind_param_ref)
+ {
+ query_param* qp (reinterpret_cast<query_param*> (i->data));
+
+ if (qp != 0 && qp->_dec_ref ())
+ delete qp;
+ }
+ }
+
+ clause_.clear ();
+ strings_.clear ();
+ }
+
+ void query_base::
+ append (const string& native)
+ {
+ strings_.push_back (native);
+ clause_.push_back (clause_part ());
+ clause_.back ().kind = clause_part::kind_native;
+ clause_.back ().data = strings_.size () - 1;
+ }
+
+ void query_base::
+ append (const query_base& x)
+ {
+ size_t i (clause_.size ()), delta (i);
+ size_t n (i + x.clause_.size ());
+ clause_.resize (n);
+
+ for (size_t j (0); i < n; ++i, ++j)
+ {
+ const clause_part& s (x.clause_[j]);
+ clause_part& d (clause_[i]);
+
+ d = s;
+
+ // We need to increment the param references, update pointers
+ // to strings and update argument positions.
+ //
+ switch (s.kind)
+ {
+ case clause_part::kind_param_val:
+ case clause_part::kind_param_ref:
+ {
+ reinterpret_cast<query_param*> (d.data)->_inc_ref ();
+ break;
+ }
+ case clause_part::kind_native:
+ {
+ strings_.push_back (x.strings_[s.data]);
+ d.data = strings_.size () - 1;
+ break;
+ }
+ case clause_part::op_add:
+
+ case clause_part::op_and:
+ case clause_part::op_or:
+
+ case clause_part::op_eq:
+ case clause_part::op_ne:
+ case clause_part::op_lt:
+ case clause_part::op_gt:
+ case clause_part::op_le:
+ case clause_part::op_ge:
+ {
+ d.data += delta;
+ break;
+ }
+ // Do not use default here to remember to handle new op codes.
+ //
+ case clause_part::kind_column:
+ case clause_part::kind_true:
+ case clause_part::kind_false:
+ case clause_part::op_not:
+ case clause_part::op_null:
+ case clause_part::op_not_null:
+ case clause_part::op_in:
+ case clause_part::op_like:
+ case clause_part::op_like_escape:
+ break;
+ }
+ }
+ }
+
+ void query_base::
+ append_ref (const void* ref, const native_column_info* c)
+ {
+ clause_.push_back (clause_part ());
+ clause_part& p (clause_.back ());
+
+ p.kind = clause_part::kind_param_ref;
+ p.data = 0; // In case new below throws.
+ p.native_info = c;
+
+ p.data = reinterpret_cast<std::size_t> (
+ new (details::shared) query_param (ref));
+ }
+
+ query_base& query_base::
+ operator+= (const std::string& native)
+ {
+ if (!native.empty ())
+ {
+ size_t p (clause_.size ());
+ append (native);
+
+ if (p != 0)
+ append (clause_part::op_add, p - 1);
+ }
+
+ return *this;
+ }
+
+ query_base& query_base::
+ operator+= (const query_base& x)
+ {
+ if (!x.empty ())
+ {
+ size_t p (clause_.size ());
+ append (x);
+
+ if (p != 0)
+ append (clause_part::op_add, p - 1);
+ }
+
+ return *this;
+ }
+
+ 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 || x.empty ())
+ return y;
+
+ if (yt || y.empty ())
+ return x;
+
+ query_base r (x);
+ r.append (y);
+ r.append (query_base::clause_part::op_and, x.clause ().size () - 1);
+ return r;
+ }
+
+ query_base
+ operator|| (const query_base& x, const query_base& y)
+ {
+ if (x.empty ())
+ return y;
+
+ if (y.empty ())
+ return x;
+
+ query_base r (x);
+ r.append (y);
+ r.append (query_base::clause_part::op_or, x.clause ().size () - 1);
+ return r;
+ }
+
+ query_base
+ operator! (const query_base& x)
+ {
+ if (x.empty ())
+ return x;
+
+ query_base r (x);
+ r.append (query_base::clause_part::op_not, 0);
+ return r;
+ }
+}
diff --git a/libodb/odb/query-dynamic.hxx b/libodb/odb/query-dynamic.hxx
new file mode 100644
index 0000000..cd0ce05
--- /dev/null
+++ b/libodb/odb/query-dynamic.hxx
@@ -0,0 +1,1067 @@
+// file : odb/query-dynamic.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QUERY_DYNAMIC_HXX
+#define ODB_QUERY_DYNAMIC_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+#include <odb/query.hxx>
+
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+namespace odb
+{
+ struct native_column_info;
+
+ template <typename T>
+ struct val_bind
+ {
+ typedef const T& type;
+
+ explicit
+ val_bind (type v): val (v) {}
+
+ type val;
+ };
+
+ // Passing arrays by value in dynamic queries is not supported.
+ // Pass by reference instead.
+ //
+ template <typename T, std::size_t N>
+ struct val_bind<T[N]>;
+
+ 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;
+ };
+
+ //
+ //
+ struct LIBODB_EXPORT query_param: details::shared_base
+ {
+ virtual ~query_param ();
+ query_param (const void* v): value (v) {}
+
+ const void* value;
+ };
+
+ // For by-value parameters we have to make a copy since the original
+ // can be gone by the time we translate to native query.
+ //
+ template <typename T>
+ struct val_query_param: query_param
+ {
+ val_query_param (const T& v): query_param (&copy), copy (v) {}
+
+ T copy;
+ };
+
+ //
+ //
+ class LIBODB_EXPORT query_base
+ {
+ public:
+ // Internally the query clause is stored in a Reverse Polish Notation-
+ // like representation which also allows us to traverse it as a syntax
+ // tree.
+ //
+ // Let's keep this class POD so that std::vector can do more
+ // efficient copying, etc.
+ //
+ struct clause_part
+ {
+ // Note that the order of enumerators is important (used as indexes).
+ //
+ enum kind_type
+ {
+ kind_column, // native_info points to the native_column_info array.
+
+ kind_param_val, // data points to query_param while native_info points
+ kind_param_ref, // to the native_column_info array.
+
+ kind_native, // data is the index in the strings vector.
+
+ kind_true, // true literal.
+ kind_false, // false literal.
+
+ // Operators.
+ //
+ // For binary operators, data is the index of the last element
+ // belonging to the left hand side sub-expression.
+ //
+ op_add, // + (concatenation of two sub-expressions)
+
+ op_and, // &&
+ op_or, // ||
+ op_not, // !
+
+ op_null, // is_null ()
+ op_not_null, // is_not_null ()
+
+ op_in, // in(), data is the number of arguments
+ op_like, // like(pattern)
+ op_like_escape, // like(pattern, escape)
+
+ op_eq, // ==
+ op_ne, // !=
+ op_lt, // <
+ op_gt, // >
+ op_le, // <=
+ op_ge // >=
+ };
+
+ kind_type kind;
+ std::size_t data;
+ const native_column_info* native_info;
+ };
+
+ public:
+ ~query_base ()
+ {
+ clear ();
+ }
+
+ query_base () {}
+
+ // True or false literal.
+ //
+ explicit
+ query_base (bool v)
+ {
+ append (v ? clause_part::kind_true : clause_part::kind_false, 0);
+ }
+
+ explicit
+ query_base (const char* native)
+ {
+ append (native);
+ }
+
+ explicit
+ query_base (const std::string& native)
+ {
+ append (native);
+ }
+
+ query_base (const query_column<bool>&);
+
+ query_base (const query_base& x)
+ {
+ append (x);
+ }
+
+ query_base&
+ operator= (const query_base& x)
+ {
+ if (this != &x)
+ {
+ clear ();
+ append (x);
+ }
+
+ return *this;
+ }
+
+ public:
+ template <typename T>
+ static val_bind<T>
+ _val (const T& x)
+ {
+ return val_bind<T> (x);
+ }
+
+ template <typename T>
+ static ref_bind<T>
+ _ref (const T& x)
+ {
+ return ref_bind<T> (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 specialization of the above
+ // _ref() function (we don't need _val() since we don't support passing
+ // arrays by value; see val_bind definition).
+ //
+ template <typename T, std::size_t N>
+ static ref_bind<T[N]>
+ _ref (const T (&x) [N])
+ {
+ return ref_bind<T[N]> (x);
+ }
+
+ public:
+ query_base&
+ operator+= (const query_base&);
+
+ query_base&
+ operator+= (const std::string& native);
+
+ public:
+ bool
+ empty () const
+ {
+ return clause_.empty ();
+ }
+
+ bool
+ const_true () const
+ {
+ return clause_.size () == 1 &&
+ clause_.front ().kind == clause_part::kind_true;
+ }
+
+ // Implementation details.
+ //
+ public:
+ explicit
+ query_base (const native_column_info* c)
+ {
+ append (c);
+ }
+
+ // Native.
+ //
+ void
+ append (const std::string&);
+
+ // Query fragment.
+ //
+ void
+ append (const query_base&);
+
+ // Operator.
+ //
+ void
+ append (clause_part::kind_type k, std::size_t data)
+ {
+ clause_.push_back (clause_part ());
+ clause_.back ().kind = k;
+ clause_.back ().data = data;
+ }
+
+ // Column.
+ //
+ void
+ append (const native_column_info* c)
+ {
+ clause_.push_back (clause_part ());
+ clause_.back ().kind = clause_part::kind_column;
+ clause_.back ().native_info = c;
+ }
+
+ // Parameter.
+ //
+ void
+ append_ref (const void* ref, const native_column_info*);
+
+ template <typename T>
+ void
+ append_val (const T& val, const native_column_info*);
+
+ void
+ clear ();
+
+ public:
+ typedef std::vector<clause_part> clause_type;
+ typedef std::vector<std::string> strings_type;
+
+ const clause_type&
+ clause () const
+ {
+ return clause_;
+ }
+
+ const strings_type&
+ strings () const
+ {
+ return strings_;
+ }
+
+ private:
+ clause_type clause_;
+ strings_type strings_;
+ };
+
+ inline query_base
+ operator+ (const query_base& x, const query_base& y)
+ {
+ query_base r (x);
+ r += y;
+ 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;
+ }
+
+ LIBODB_EXPORT query_base
+ operator&& (const query_base&, const query_base&);
+
+ LIBODB_EXPORT query_base
+ operator|| (const query_base&, const query_base&);
+
+ LIBODB_EXPORT query_base
+ operator! (const query_base&);
+
+ //
+ //
+ struct native_column_info
+ {
+ const void* column;
+ void* param_factory;
+ };
+
+ template <typename T>
+ const T&
+ type_instance ();
+
+ // This class template has to remain POD since we rely on it being
+ // 0-initialized before any dynamic initialization takes place in
+ // any other translation unit.
+ //
+ template <typename T>
+ struct query_column
+ {
+ // Array of pointers to database-specific columns. It will be
+ // automatically zero-initialized since query_column instances
+ // are always static.
+ //
+ native_column_info native_info[database_count];
+
+ // is_null, is_not_null
+ //
+ public:
+ query_base
+ is_null () const
+ {
+ query_base q (native_info);
+ q.append (query_base::clause_part::op_null, 0);
+ return q;
+ }
+
+ query_base
+ is_not_null () const
+ {
+ query_base q (native_info);
+ q.append (query_base::clause_part::op_not_null, 0);
+ return q;
+ }
+
+ // in
+ //
+ public:
+ query_base
+ in (const T&, const T&) const;
+
+ query_base
+ in (const T&, const T&, const T&) const;
+
+ query_base
+ in (const T&, const T&, const T&, const T&) const;
+
+ query_base
+ in (const T&, const T&, const T&, const T&, const T&) const;
+
+ template <typename I>
+ query_base
+ in_range (I begin, I end) const;
+
+ // like
+ //
+ public:
+ query_base
+ like (const T& 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> (T (pattern.val)));
+ }
+
+ query_base
+ like (ref_bind<T> pattern) const;
+
+ query_base
+ like (const T& pattern, const T& escape) const
+ {
+ return like (val_bind<T> (pattern), escape);
+ }
+
+ query_base
+ like (val_bind<T> pattern, const T& escape) const;
+
+ template <typename T2>
+ query_base
+ like (val_bind<T2> pattern, const T& escape) const
+ {
+ return like (val_bind<T> (T (pattern.val)), escape);
+ }
+
+ query_base
+ like (ref_bind<T> pattern, const T& escape) const;
+
+ // ==
+ //
+ public:
+ query_base
+ equal (val_bind<T> v) const
+ {
+ query_base q (native_info);
+ q.append_val (v.val, native_info);
+ q.append (query_base::clause_part::op_eq, 0);
+ return q;
+ }
+
+ query_base
+ equal (ref_bind<T> r) const
+ {
+ query_base q (native_info);
+ q.append_ref (r.ptr (), native_info);
+ q.append (query_base::clause_part::op_eq, 0);
+ return q;
+ }
+
+ friend query_base
+ operator== (const query_column& c, const T& v)
+ {
+ return c.equal (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator== (const T& v, const query_column& c)
+ {
+ return c.equal (val_bind<T> (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 (val_bind<T> (T (v.val)));
+ }
+
+ template <typename T2>
+ friend query_base
+ operator== (val_bind<T2> v, const query_column& c)
+ {
+ return c.equal (val_bind<T> (T (v.val)));
+ }
+
+ 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 (val_bind<T> v) const
+ {
+ query_base q (native_info);
+ q.append_val (v.val, native_info);
+ q.append (query_base::clause_part::op_ne, 0);
+ return q;
+ }
+
+ query_base
+ unequal (ref_bind<T> r) const
+ {
+ query_base q (native_info);
+ q.append_ref (r.ptr (), native_info);
+ q.append (query_base::clause_part::op_ne, 0);
+ return q;
+ }
+
+ friend query_base
+ operator!= (const query_column& c, const T& v)
+ {
+ return c.unequal (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator!= (const T& v, const query_column& c)
+ {
+ return c.unequal (val_bind<T> (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 (val_bind<T> (T (v.val)));
+ }
+
+ template <typename T2>
+ friend query_base
+ operator!= (val_bind<T2> v, const query_column& c)
+ {
+ return c.unequal (val_bind<T> (T (v.val)));
+ }
+
+ 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 (val_bind<T> v) const
+ {
+ query_base q (native_info);
+ q.append_val (v.val, native_info);
+ q.append (query_base::clause_part::op_lt, 0);
+ return q;
+ }
+
+ query_base
+ less (ref_bind<T> r) const
+ {
+ query_base q (native_info);
+ q.append_ref (r.ptr (), native_info);
+ q.append (query_base::clause_part::op_lt, 0);
+ return q;
+ }
+
+ friend query_base
+ operator< (const query_column& c, const T& v)
+ {
+ return c.less (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator< (const T& v, const query_column& c)
+ {
+ return c.greater (val_bind<T> (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 (val_bind<T> (T (v.val)));
+ }
+
+ template <typename T2>
+ friend query_base
+ operator< (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater (val_bind<T> (T (v.val)));
+ }
+
+ 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 (val_bind<T> v) const
+ {
+ query_base q (native_info);
+ q.append_val (v.val, native_info);
+ q.append (query_base::clause_part::op_gt, 0);
+ return q;
+ }
+
+ query_base
+ greater (ref_bind<T> r) const
+ {
+ query_base q (native_info);
+ q.append_ref (r.ptr (), native_info);
+ q.append (query_base::clause_part::op_gt, 0);
+ return q;
+ }
+
+ friend query_base
+ operator> (const query_column& c, const T& v)
+ {
+ return c.greater (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator> (const T& v, const query_column& c)
+ {
+ return c.less (val_bind<T> (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 (val_bind<T> (T (v.val)));
+ }
+
+ template <typename T2>
+ friend query_base
+ operator> (val_bind<T2> v, const query_column& c)
+ {
+ return c.less (val_bind<T> (T (v.val)));
+ }
+
+ 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 (val_bind<T> v) const
+ {
+ query_base q (native_info);
+ q.append_val (v.val, native_info);
+ q.append (query_base::clause_part::op_le, 0);
+ return q;
+ }
+
+ query_base
+ less_equal (ref_bind<T> r) const
+ {
+ query_base q (native_info);
+ q.append_ref (r.ptr (), native_info);
+ q.append (query_base::clause_part::op_le, 0);
+ return q;
+ }
+
+ friend query_base
+ operator<= (const query_column& c, const T& v)
+ {
+ return c.less_equal (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator<= (const T& v, const query_column& c)
+ {
+ return c.greater_equal (val_bind<T> (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 (val_bind<T> (T (v.val)));
+ }
+
+ template <typename T2>
+ friend query_base
+ operator<= (val_bind<T2> v, const query_column& c)
+ {
+ return c.greater_equal (val_bind<T> (T (v.val)));
+ }
+
+ 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 (val_bind<T> v) const
+ {
+ query_base q (native_info);
+ q.append_val (v.val, native_info);
+ q.append (query_base::clause_part::op_ge, 0);
+ return q;
+ }
+
+ query_base
+ greater_equal (ref_bind<T> r) const
+ {
+ query_base q (native_info);
+ q.append_ref (r.ptr (), native_info);
+ q.append (query_base::clause_part::op_ge, 0);
+ return q;
+ }
+
+ friend query_base
+ operator>= (const query_column& c, const T& v)
+ {
+ return c.greater_equal (val_bind<T> (v));
+ }
+
+ friend query_base
+ operator>= (const T& v, const query_column& c)
+ {
+ return c.less_equal (val_bind<T> (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 (val_bind<T> (T (v.val)));
+ }
+
+ template <typename T2>
+ friend query_base
+ operator>= (val_bind<T2> v, const query_column& c)
+ {
+ return c.less_equal (val_bind<T> (T (v.val)));
+ }
+
+ 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>
+ query_base
+ operator== (const query_column<T2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (type_instance<T> () == type_instance<T2> ()));
+
+ query_base q (native_info);
+ q.append (c.native_info);
+ q.append (query_base::clause_part::op_eq, 0);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ operator!= (const query_column<T2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (type_instance<T> () != type_instance<T2> ()));
+
+ query_base q (native_info);
+ q.append (c.native_info);
+ q.append (query_base::clause_part::op_ne, 0);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ operator< (const query_column<T2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (type_instance<T> () < type_instance<T2> ()));
+
+ query_base q (native_info);
+ q.append (c.native_info);
+ q.append (query_base::clause_part::op_lt, 0);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ operator> (const query_column<T2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (type_instance<T> () > type_instance<T2> ()));
+
+ query_base q (native_info);
+ q.append (c.native_info);
+ q.append (query_base::clause_part::op_gt, 0);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ operator<= (const query_column<T2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (type_instance<T> () <= type_instance<T2> ()));
+
+ query_base q (native_info);
+ q.append (c.native_info);
+ q.append (query_base::clause_part::op_le, 0);
+ return q;
+ }
+
+ template <typename T2>
+ query_base
+ operator>= (const query_column<T2>& c) const
+ {
+ // We can compare columns only if we can compare their C++ types.
+ //
+ (void) (sizeof (type_instance<T> () >= type_instance<T2> ()));
+
+ query_base q (native_info);
+ q.append (c.native_info);
+ q.append (query_base::clause_part::op_ge, 0);
+ return q;
+ }
+ };
+
+ // Provide operator+() for using columns to construct native
+ // query fragments (e.g., ORDER BY).
+ //
+ template <typename T>
+ inline query_base
+ operator+ (const query_column<T>& c, const std::string& s)
+ {
+ query_base q (c.native_info);
+ q.append (s);
+ q.append (query_base::clause_part::op_add, 0);
+ return q;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const std::string& s, const query_column<T>& c)
+ {
+ query_base q (s);
+ q.append (c.native_info);
+ q.append (query_base::clause_part::op_add, 0);
+ return q;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_column<T>& c, const query_base& q)
+ {
+ query_base r (c.native_info);
+ r.append (q);
+ r.append (query_base::clause_part::op_add, 0);
+ return r;
+ }
+
+ template <typename T>
+ inline query_base
+ operator+ (const query_base& q, const query_column<T>& c)
+ {
+ query_base r (q);
+ r.append (c.native_info);
+ r.append (query_base::clause_part::op_add, q.clause ().size () - 1);
+ return r;
+ }
+
+ //
+ //
+ template <typename T>
+ class query<T, query_base>: public query_base,
+ public query_selector<T, id_common>::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)
+ {
+ }
+
+ query (const query_base& q)
+ : query_base (q)
+ {
+ }
+
+ query (const query_column<bool>& qc)
+ : query_base (qc)
+ {
+ }
+ };
+}
+
+#include <odb/query-dynamic.ixx>
+#include <odb/query-dynamic.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_QUERY_DYNAMIC_HXX
diff --git a/libodb/odb/query-dynamic.ixx b/libodb/odb/query-dynamic.ixx
new file mode 100644
index 0000000..8de7b8e
--- /dev/null
+++ b/libodb/odb/query-dynamic.ixx
@@ -0,0 +1,18 @@
+// file : odb/query-dynamic.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ // query_base
+ //
+ inline query_base::
+ query_base (const query_column<bool>& c)
+ {
+ // Some databases provide the IS TRUE operator. However, we cannot
+ // use it since the column type might now be SQL boolean type.
+ //
+ append (c.native_info);
+ append_val (true, c.native_info);
+ append (query_base::clause_part::op_eq, 0);
+ }
+}
diff --git a/libodb/odb/query-dynamic.txx b/libodb/odb/query-dynamic.txx
new file mode 100644
index 0000000..9ea16d2
--- /dev/null
+++ b/libodb/odb/query-dynamic.txx
@@ -0,0 +1,139 @@
+// file : odb/query-dynamic.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // query_base
+ //
+
+ template <typename T>
+ void query_base::
+ append_val (const T& val, const native_column_info* c)
+ {
+ clause_.push_back (clause_part ());
+ clause_part& p (clause_.back ());
+
+ p.kind = clause_part::kind_param_val;
+ p.data = 0; // In case new below throws.
+ p.native_info = c;
+
+ query_param* qp (new (details::shared) val_query_param<T> (val));
+ p.data = reinterpret_cast<std::size_t> (qp);
+ }
+
+ //
+ // query_column
+ //
+
+ // in
+ //
+ template <typename T>
+ query_base query_column<T>::
+ in (const T& v1, const T& v2) const
+ {
+ query_base q (native_info);
+ q.append_val (v1, native_info);
+ q.append_val (v2, native_info);
+ q.append (query_base::clause_part::op_in, 2);
+ return q;
+ }
+
+ template <typename T>
+ query_base query_column<T>::
+ in (const T& v1, const T& v2, const T& v3) const
+ {
+ query_base q (native_info);
+ q.append_val (v1, native_info);
+ q.append_val (v2, native_info);
+ q.append_val (v3, native_info);
+ q.append (query_base::clause_part::op_in, 3);
+ return q;
+ }
+
+ template <typename T>
+ query_base query_column<T>::
+ in (const T& v1, const T& v2, const T& v3, const T& v4) const
+ {
+ query_base q (native_info);
+ q.append_val (v1, native_info);
+ q.append_val (v2, native_info);
+ q.append_val (v3, native_info);
+ q.append_val (v4, native_info);
+ q.append (query_base::clause_part::op_in, 4);
+ return q;
+ }
+
+ template <typename T>
+ query_base query_column<T>::
+ in (const T& v1, const T& v2, const T& v3, const T& v4, const T& v5) const
+ {
+ query_base q (native_info);
+ q.append_val (v1, native_info);
+ q.append_val (v2, native_info);
+ q.append_val (v3, native_info);
+ q.append_val (v4, native_info);
+ q.append_val (v5, native_info);
+ q.append (query_base::clause_part::op_in, 5);
+ return q;
+ }
+
+ template <typename T>
+ template <typename I>
+ query_base query_column<T>::
+ in_range (I i, I end) const
+ {
+ query_base q (native_info);
+
+ std::size_t n (0);
+ for (; i != end; ++i, ++n)
+ q.append_val<T> (*i, native_info); // Force implicit conversion.
+
+ q.append (query_base::clause_part::op_in, n);
+ return q;
+ }
+
+ // like
+ //
+ template <typename T>
+ query_base query_column<T>::
+ like (val_bind<T> p) const
+ {
+ query_base q (native_info);
+ q.append_val (p.val, native_info);
+ q.append (query_base::clause_part::op_like, 0);
+ return q;
+ }
+
+ template <typename T>
+ query_base query_column<T>::
+ like (ref_bind<T> p) const
+ {
+ query_base q (native_info);
+ q.append_ref (p.ptr (), native_info);
+ q.append (query_base::clause_part::op_like, 0);
+ return q;
+ }
+
+ template <typename T>
+ query_base query_column<T>::
+ like (val_bind<T> p, const T& e) const
+ {
+ query_base q (native_info);
+ q.append_val (p.val, native_info);
+ q.append_val (e, native_info);
+ q.append (query_base::clause_part::op_like_escape, 0);
+ return q;
+ }
+
+ template <typename T>
+ query_base query_column<T>::
+ like (ref_bind<T> p, const T& e) const
+ {
+ query_base q (native_info);
+ q.append_ref (p.ptr (), native_info);
+ q.append_val (e, native_info);
+ q.append (query_base::clause_part::op_like_escape, 0);
+ return q;
+ }
+}
diff --git a/libodb/odb/query.hxx b/libodb/odb/query.hxx
new file mode 100644
index 0000000..9375738
--- /dev/null
+++ b/libodb/odb/query.hxx
@@ -0,0 +1,119 @@
+// file : odb/query.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_QUERY_HXX
+#define ODB_QUERY_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+namespace odb
+{
+ // Table alias for type T and tag Tag.
+ //
+ // The alias_traits interface consists of two things: the table_name
+ // static variable containing the name and, in case of a derived type
+ // in a polymorphic hierarchy, the base_traits typedef. Note that the
+ // same interface is exposed by object_traits, which is used when
+ // we need straight tables instead of aliases.
+ //
+ //
+ template <typename T, database_id DB, typename Tag>
+ struct alias_traits;
+
+ template <typename T, database_id DB>
+ struct query_columns_base;
+
+ template <typename T, database_id DB, typename A>
+ struct query_columns;
+
+ template <typename T, database_id DB, typename A>
+ struct pointer_query_columns;
+
+ // Object pointer syntax wrapper.
+ //
+ template <typename T>
+ struct query_pointer
+ {
+ query_pointer ()
+ {
+ // For some reason GCC needs this dummy c-tor if we make a static
+ // data member of this type const.
+ }
+
+ T*
+ operator-> () const
+ {
+ return 0; // All members in T are static.
+ }
+ };
+
+ // Query parameter decay traits.
+ //
+ template <typename T>
+ struct decay_traits
+ {
+ typedef const T& type;
+
+ static type
+ instance ();
+ };
+
+ template <typename T, std::size_t N>
+ struct decay_traits<T[N]>
+ {
+ typedef const T* type;
+
+ // Use the pointer comparability as a proxy for data comparability.
+ // Note that it is stricter than using element comparability (i.e.,
+ // one can compare int to char but not int* to char*).
+ //
+ static type
+ instance ();
+ };
+
+ // VC9 cannot handle certain cases of non-type arguments with default
+ // values in template functions (e.g., database::query()). As a result,
+ // we have to use the impl trick below instead of simply having kind
+ // as a second template argument with a default value.
+ //
+ template <typename T, database_id DB, class_kind kind>
+ struct query_selector_impl;
+
+ template <typename T, database_id DB>
+ struct query_selector_impl<T, DB, class_object>
+ {
+ typedef typename object_traits_impl<T, DB>::query_base_type base_type;
+
+ typedef
+ query_columns<T, DB, access::object_traits_impl<T, DB> >
+ columns_type;
+ };
+
+ template <typename T, database_id DB>
+ struct query_selector_impl<T, DB, class_view>
+ {
+ typedef typename view_traits_impl<T, DB>::query_base_type base_type;
+ typedef typename view_traits_impl<T, DB>::query_columns columns_type;
+ };
+
+ template <typename T, database_id DB>
+ struct query_selector: query_selector_impl<T, DB, class_traits<T>::kind>
+ {
+ };
+
+ template <typename T,
+ typename B = typename query_selector<T, id_common>::base_type>
+ class query;
+
+ namespace core
+ {
+ using odb::query;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_QUERY_HXX
diff --git a/libodb/odb/result.cxx b/libodb/odb/result.cxx
new file mode 100644
index 0000000..e9393ca
--- /dev/null
+++ b/libodb/odb/result.cxx
@@ -0,0 +1,40 @@
+// file : odb/result.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/result.hxx>
+#include <odb/connection.hxx>
+
+namespace odb
+{
+ result_impl::
+ ~result_impl ()
+ {
+ if (next_ != this)
+ list_remove ();
+ }
+
+ result_impl::
+ result_impl (connection& c)
+ : db_ (c.database ()), conn_ (c), prev_ (0), next_ (this)
+ {
+ // Add to the list.
+ //
+ next_ = conn_.results_;
+ conn_.results_ = this;
+
+ if (next_ != 0)
+ next_->prev_ = this;
+ }
+
+ void result_impl::
+ list_remove ()
+ {
+ (prev_ == 0 ? conn_.results_ : prev_->next_) = next_;
+
+ if (next_ != 0)
+ next_->prev_ = prev_;
+
+ prev_ = 0;
+ next_ = this;
+ }
+}
diff --git a/libodb/odb/result.hxx b/libodb/odb/result.hxx
new file mode 100644
index 0000000..87a6869
--- /dev/null
+++ b/libodb/odb/result.hxx
@@ -0,0 +1,247 @@
+// file : odb/result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_RESULT_HXX
+#define ODB_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::ptrdiff_t, std::size_t
+
+#include <odb/forward.hxx> // odb::core
+#include <odb/traits.hxx>
+
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+namespace odb
+{
+ class LIBODB_EXPORT result_impl: public details::shared_base
+ {
+ public:
+ virtual
+ ~result_impl ();
+
+ virtual void
+ invalidate () = 0;
+
+ protected:
+ result_impl (connection&);
+
+ protected:
+ database& db_;
+ connection& conn_;
+
+ // Doubly-linked list of results.
+ //
+ // prev_ == 0 means we are the first element.
+ // next_ == 0 means we are the last element.
+ // next_ == this means we are not on the list.
+ //
+ protected:
+ friend class connection;
+
+ void
+ list_remove ();
+
+ result_impl* prev_;
+ result_impl* next_;
+ };
+
+ template <typename T, class_kind kind>
+ class result_base;
+
+ template <typename T, class_kind kind = class_traits<T>::kind>
+ class result_iterator;
+
+ // Input iterator requirements.
+ //
+ template <typename T, class_kind kind>
+ inline bool
+ operator== (result_iterator<T, kind> i, result_iterator<T, kind> j)
+ {
+ return i.equal (j);
+ }
+
+ template <typename T, class_kind kind>
+ inline bool
+ operator!= (result_iterator<T, kind> i, result_iterator<T, kind> j)
+ {
+ return !i.equal (j);
+ }
+
+ template <typename T>
+ class result: result_base<T, class_traits<T>::kind>
+ {
+ public:
+ static const class_kind kind = class_traits<T>::kind;
+
+ typedef result_base<T, kind> base;
+
+ typedef typename base::value_type value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+
+ typedef result_iterator<T, kind> iterator;
+
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+
+ typedef typename base::result_impl_type result_impl_type;
+
+ public:
+ result ()
+ {
+ }
+
+ explicit
+ result (details::shared_ptr<result_impl_type> impl)
+ : impl_ (impl)
+ {
+ }
+
+ // Copying or assignment of a result instance leads to one instance
+ // being an alias for another. Think of copying a result as copying
+ // a file handle -- the file you access through either of them is
+ // still the same.
+ //
+ public:
+ result (const result& r)
+ : impl_ (r.impl_)
+ {
+ }
+
+ result&
+ operator= (const result& r)
+ {
+ if (impl_ != r.impl_)
+ impl_ = r.impl_;
+
+ return *this;
+ }
+
+ // Conversion from result<T> to result<const T>.
+ //
+ template <typename UT>
+ result (const result<UT>& r)
+ //
+ // If you get a compiler error pointing to the line below saying
+ // that the impl_ member is inaccessible, then you are most likely
+ // trying to perform an illegal result conversion, for example,
+ // from result<const obj> to result<obj>.
+ //
+ : impl_ (r.impl_)
+ {
+ }
+
+ template <typename UT>
+ result&
+ operator= (const result<UT>& r)
+ {
+ // If you get a compiler error pointing to the line below saying
+ // that the impl_ member is inaccessible, then you are most likely
+ // trying to perform an illegal result conversion, for example,
+ // from result<const obj> to result<obj>.
+ //
+ if (impl_ != r.impl_)
+ impl_ = r.impl_;
+
+ return *this;
+ }
+
+ void
+ swap (result& r)
+ {
+ // @@ add swap() to shared_ptr.
+ //
+ details::shared_ptr<result_impl_type> p (impl_);
+ impl_ = r.impl_;
+ r.impl_ = p;
+ }
+
+ public:
+ iterator
+ begin ()
+ {
+ if (impl_)
+ impl_->begin ();
+
+ return iterator (impl_.get ());
+ }
+
+ iterator
+ end ()
+ {
+ return iterator ();
+ }
+
+ // Cache the result instead of fetching the data from the database
+ // one row at a time. This is necessary if you plan on performing
+ // database operations while iterating over the result.
+ //
+ public:
+ void
+ cache ()
+ {
+ if (impl_)
+ impl_->cache ();
+ }
+
+ public:
+ bool
+ empty () const
+ {
+ if (impl_ == 0)
+ return true;
+
+ impl_->begin ();
+ return impl_->end ();
+ }
+
+ // Size is only known in cached results.
+ //
+ size_type
+ size () const
+ {
+ return impl_ ? impl_->size () : 0;
+ }
+
+ // query_one() and query_value() implementation details.
+ //
+ public:
+ typedef typename iterator::pointer_type pointer_type;
+
+ pointer_type
+ one ();
+
+ bool
+ one (T&);
+
+ // We cannot return by value here since result can be instantiated
+ // for an abstract type (polymorphic abstract base) and it seems
+ // the signature must be valid to the point being able to call the
+ // necessary constructors.
+ //
+ void
+ value (T&);
+
+ private:
+ friend class result<const T>;
+
+ details::shared_ptr<result_impl_type> impl_;
+ };
+
+ namespace common
+ {
+ using odb::result;
+ using odb::result_iterator;
+ }
+}
+
+#include <odb/result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_RESULT_HXX
diff --git a/libodb/odb/result.txx b/libodb/odb/result.txx
new file mode 100644
index 0000000..b69d92a
--- /dev/null
+++ b/libodb/odb/result.txx
@@ -0,0 +1,49 @@
+// file : odb/result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+namespace odb
+{
+ template <typename T>
+ typename result<T>::pointer_type result<T>::
+ one ()
+ {
+ iterator i (begin ());
+
+ if (i != end ())
+ {
+ pointer_type o (i.load ());
+ assert (++i == end ()); // More than one element in query_one() result.
+ return o;
+ }
+
+ return pointer_type ();
+ }
+
+ template <typename T>
+ bool result<T>::
+ one (T& o)
+ {
+ iterator i (begin ());
+
+ if (i != end ())
+ {
+ i.load (o);
+ assert (++i == end ()); // More than one element in query_one() result.
+ return true;
+ }
+
+ return false;
+ }
+
+ template <typename T>
+ void result<T>::
+ value (T& o)
+ {
+ iterator i (begin ());
+ assert (i != end ()); // Zero elements in query_value() result.
+ i.load (o);
+ assert (++i == end ()); // More than one element in query_value() result.
+ }
+}
diff --git a/libodb/odb/schema-catalog-impl.hxx b/libodb/odb/schema-catalog-impl.hxx
new file mode 100644
index 0000000..dd729db
--- /dev/null
+++ b/libodb/odb/schema-catalog-impl.hxx
@@ -0,0 +1,54 @@
+// file : odb/schema-catalog-impl.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SCHEMA_CATALOG_IMPL_HXX
+#define ODB_SCHEMA_CATALOG_IMPL_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef>
+
+#include <odb/forward.hxx> // schema_version
+
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ struct schema_catalog_impl;
+
+ // Translation unit initializer.
+ //
+ struct LIBODB_EXPORT schema_catalog_init
+ {
+ static schema_catalog_impl* catalog;
+ static std::size_t count;
+
+ schema_catalog_init ();
+ ~schema_catalog_init ();
+ };
+
+ static const schema_catalog_init schema_catalog_init_;
+
+ // Catalog entry registration.
+ //
+ struct LIBODB_EXPORT schema_catalog_create_entry
+ {
+ schema_catalog_create_entry (
+ database_id,
+ const char* name,
+ bool (*create_function) (database&, unsigned short pass, bool drop));
+ };
+
+ struct LIBODB_EXPORT schema_catalog_migrate_entry
+ {
+ schema_catalog_migrate_entry (
+ database_id,
+ const char* name,
+ schema_version,
+ bool (*migrate_function) (database&, unsigned short pass, bool pre));
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SCHEMA_CATALOG_IMPL_HXX
diff --git a/libodb/odb/schema-catalog.cxx b/libodb/odb/schema-catalog.cxx
new file mode 100644
index 0000000..1bdc112
--- /dev/null
+++ b/libodb/odb/schema-catalog.cxx
@@ -0,0 +1,387 @@
+// file : odb/schema-catalog.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <map>
+#include <vector>
+#include <cassert>
+
+#include <odb/exceptions.hxx>
+#include <odb/schema-catalog.hxx>
+#include <odb/schema-catalog-impl.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ // Schema.
+ //
+ typedef bool (*create_function) (database&, unsigned short pass, bool drop);
+ typedef bool (*migrate_function) (database&, unsigned short pass, bool pre);
+
+ typedef pair<database_id, string> key;
+ typedef vector<create_function> create_functions;
+ typedef vector<migrate_function> migrate_functions;
+ typedef map<schema_version, migrate_functions> version_map;
+
+ struct schema_functions
+ {
+ create_functions create;
+ version_map migrate;
+ };
+ typedef map<key, schema_functions> schema_map;
+
+ // Data. Normally the code would be database-independent, though there
+ // could be database-specific migration steps.
+ //
+ typedef pair<string, schema_version> data_key;
+
+ struct data_function
+ {
+ typedef schema_catalog::data_migration_function_wrapper
+ function_wrapper_type;
+
+ data_function () {}
+ data_function (database_id i, function_wrapper_type m)
+ : id (i), migrate (m) {}
+
+ database_id id;
+ function_wrapper_type migrate;
+ };
+ typedef vector<data_function> data_functions;
+ typedef map<data_key, data_functions> data_map;
+
+ struct schema_catalog_impl
+ {
+ schema_map schema;
+ data_map data;
+ };
+
+ // Static initialization.
+ //
+ schema_catalog_impl* schema_catalog_init::catalog = 0;
+ size_t schema_catalog_init::count = 0;
+
+ struct schema_catalog_init_extra
+ {
+ bool initialized;
+
+ schema_catalog_init_extra (): initialized (false) {}
+ ~schema_catalog_init_extra ()
+ {
+ if (initialized && --schema_catalog_init::count == 0)
+ delete schema_catalog_init::catalog;
+ }
+ };
+
+ static schema_catalog_init_extra schema_catalog_init_extra_;
+
+ bool schema_catalog::
+ exists (database_id id, const string& name)
+ {
+ const schema_catalog_impl& c (*schema_catalog_init::catalog);
+ return c.schema.find (key (id, name)) != c.schema.end ();
+ }
+
+ void schema_catalog::
+ create_schema (database& db, const string& name, bool drop)
+ {
+ const schema_catalog_impl& c (*schema_catalog_init::catalog);
+ schema_map::const_iterator i (c.schema.find (key (db.id (), name)));
+
+ if (i == c.schema.end ())
+ throw unknown_schema (name);
+
+ const create_functions& fs (i->second.create);
+
+ if (drop)
+ drop_schema (db, name);
+
+ // Run the passes until we ran them all or all the functions
+ // return false, which means no more passes necessary.
+ //
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ bool done (true);
+
+ for (create_functions::const_iterator j (fs.begin ()), e (fs.end ());
+ j != e; ++j)
+ {
+ if ((*j) (db, pass, false))
+ done = false;
+ }
+
+ if (done)
+ break;
+ }
+ }
+
+ void schema_catalog::
+ drop_schema (database& db, const string& name)
+ {
+ const schema_catalog_impl& c (*schema_catalog_init::catalog);
+ schema_map::const_iterator i (c.schema.find (key (db.id (), name)));
+
+ if (i == c.schema.end ())
+ throw unknown_schema (name);
+
+ const create_functions& fs (i->second.create);
+
+ // Run the passes until we ran them all or all the functions
+ // return false, which means no more passes necessary.
+ //
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ bool done (true);
+
+ for (create_functions::const_iterator j (fs.begin ()), e (fs.end ());
+ j != e; ++j)
+ {
+ if ((*j) (db, pass, true))
+ done = false;
+ }
+
+ if (done)
+ break;
+ }
+ }
+
+ void schema_catalog::
+ migrate_schema_impl (database& db,
+ schema_version v,
+ const string& name,
+ migrate_mode m)
+ {
+ const schema_catalog_impl& c (*schema_catalog_init::catalog);
+ schema_map::const_iterator i (c.schema.find (key (db.id (), name)));
+
+ if (i == c.schema.end ())
+ throw unknown_schema (name);
+
+ const version_map& vm (i->second.migrate);
+ version_map::const_iterator j (vm.find (v));
+
+ if (j == vm.end ())
+ throw unknown_schema_version (v);
+
+ const migrate_functions& fs (j->second);
+
+ // Run the passes until we ran them all or all the functions
+ // return false, which means no more passes necessary.
+ //
+ for (bool pre (m != migrate_post);; pre = false)
+ {
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ bool done (true);
+
+ for (migrate_functions::const_iterator i (fs.begin ()), e (fs.end ());
+ i != e; ++i)
+ {
+ if ((*i) (db, pass, pre))
+ done = false;
+ }
+
+ if (done)
+ break;
+ }
+
+ if (!pre || m != migrate_both)
+ break;
+ }
+
+ // Update the schema version on the database instance.
+ //
+ db.schema_version_migration (v, m == migrate_pre, name);
+ }
+
+ size_t schema_catalog::
+ migrate_data (database& db, schema_version v, const string& name)
+ {
+ if (v == 0)
+ {
+ if (!db.schema_migration ())
+ return 0;
+
+ v = db.schema_version ();
+ }
+
+ const schema_catalog_impl& c (*schema_catalog_init::catalog);
+ data_map::const_iterator i (c.data.find (data_key (name, v)));
+
+ if (i == c.data.end ())
+ return 0; // No data migration for this schema/version.
+
+ size_t r (0);
+
+ const data_functions& df (i->second);
+ for (data_functions::const_iterator i (df.begin ()), e (df.end ());
+ i != e; ++i)
+ {
+ if (i->id == id_common || i->id == db.id ())
+ {
+ const data_migration_function_wrapper &m = i->migrate;
+
+ if (m.std_function == 0)
+ m.function (db);
+ else
+ {
+ typedef void (*caller) (const void*, database&);
+ m.cast<caller> () (m.std_function, db);
+ }
+ r++;
+ }
+ }
+
+ return r;
+ }
+
+ void schema_catalog::
+ data_migration_function (database_id id,
+ schema_version v,
+ data_migration_function_wrapper f,
+ const string& name)
+ {
+ // This function can be called from a static initializer in which
+ // case the catalog might not have yet been created.
+ //
+ if (schema_catalog_init::count == 0)
+ {
+ schema_catalog_init::catalog = new schema_catalog_impl;
+ ++schema_catalog_init::count;
+ schema_catalog_init_extra_.initialized = true;
+ }
+
+ schema_catalog_impl& c (*schema_catalog_init::catalog);
+ c.data[data_key (name, v)].push_back (data_function (id, f));
+ }
+
+ void schema_catalog::
+ migrate (database& db, schema_version v, const string& name)
+ {
+ schema_version cur (current_version (db, name));
+
+ if (v == 0)
+ v = cur;
+ else if (v > cur)
+ throw unknown_schema_version (v);
+
+ schema_version i (db.schema_version (name));
+
+ if (i > v)
+ throw unknown_schema_version (i); // Database too new.
+
+ // If there is no schema, then "migrate" by creating it.
+ //
+ if (i == 0)
+ {
+ // Schema creation can only "migrate" straight to current.
+ //
+ if (v != cur)
+ throw unknown_schema_version (v);
+
+ create_schema (db, name, false);
+ return;
+ }
+
+ for (i = next_version (db, i, name);
+ i <= v;
+ i = next_version (db, i, name))
+ {
+ migrate_schema_pre (db, i, name);
+ migrate_data (db, i, name);
+ migrate_schema_post (db, i, name);
+ }
+ }
+
+ schema_version schema_catalog::
+ base_version (database_id id, const string& name)
+ {
+ const schema_catalog_impl& c (*schema_catalog_init::catalog);
+ schema_map::const_iterator i (c.schema.find (key (id, name)));
+
+ if (i == c.schema.end ())
+ throw unknown_schema (name);
+
+ const version_map& vm (i->second.migrate);
+ assert (!vm.empty ());
+ return vm.begin ()->first;
+ }
+
+ schema_version schema_catalog::
+ current_version (database_id id, const string& name)
+ {
+ const schema_catalog_impl& c (*schema_catalog_init::catalog);
+ schema_map::const_iterator i (c.schema.find (key (id, name)));
+
+ if (i == c.schema.end ())
+ throw unknown_schema (name);
+
+ const version_map& vm (i->second.migrate);
+ assert (!vm.empty ());
+ return vm.rbegin ()->first;
+ }
+
+ schema_version schema_catalog::
+ next_version (database_id id, schema_version v, const string& name)
+ {
+ const schema_catalog_impl& sc (*schema_catalog_init::catalog);
+ schema_map::const_iterator i (sc.schema.find (key (id, name)));
+
+ if (i == sc.schema.end ())
+ throw unknown_schema (name);
+
+ const version_map& vm (i->second.migrate); // Cannot be empty.
+
+ schema_version b (vm.begin ()->first);
+ schema_version c (vm.rbegin ()->first);
+
+ if (v == 0)
+ return c; // "Migration" to the current via schema creation.
+ else if (v < b)
+ throw unknown_schema_version (v); // Unsupported migration.
+
+ version_map::const_iterator j (vm.upper_bound (v));
+ return j != vm.end () ? j->first : c + 1;
+ }
+
+ // schema_catalog_init
+ //
+ schema_catalog_init::
+ schema_catalog_init ()
+ {
+ if (count == 0)
+ catalog = new schema_catalog_impl;
+
+ ++count;
+ }
+
+ schema_catalog_init::
+ ~schema_catalog_init ()
+ {
+ if (--count == 0)
+ delete catalog;
+ }
+
+ // schema_catalog_create_entry
+ //
+ schema_catalog_create_entry::
+ schema_catalog_create_entry (database_id id,
+ const char* name,
+ create_function cf)
+ {
+ schema_catalog_impl& c (*schema_catalog_init::catalog);
+ c.schema[key(id, name)].create.push_back (cf);
+ }
+
+ // schema_catalog_migrate_entry
+ //
+ schema_catalog_migrate_entry::
+ schema_catalog_migrate_entry (database_id id,
+ const char* name,
+ schema_version v,
+ migrate_function mf)
+ {
+ schema_catalog_impl& c (*schema_catalog_init::catalog);
+ c.schema[key(id, name)].migrate[v].push_back (mf);
+ }
+}
diff --git a/libodb/odb/schema-catalog.hxx b/libodb/odb/schema-catalog.hxx
new file mode 100644
index 0000000..c38cd4f
--- /dev/null
+++ b/libodb/odb/schema-catalog.hxx
@@ -0,0 +1,392 @@
+// file : odb/schema-catalog.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SCHEMA_CATALOG_HXX
+#define ODB_SCHEMA_CATALOG_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <string>
+#include <cstddef> // std::size_t
+
+#ifdef ODB_CXX11
+# include <utility> // std::move
+# include <functional> // std::function
+# include <type_traits> // std::enable_if, std::is_convertible
+#endif
+
+#include <odb/forward.hxx> // schema_version, odb::core
+#include <odb/database.hxx>
+
+#include <odb/details/export.hxx>
+#include <odb/details/unused.hxx>
+#include <odb/details/meta/static-assert.hxx>
+
+namespace odb
+{
+ class LIBODB_EXPORT schema_catalog
+ {
+ public:
+ // Schema creation.
+ //
+ static void
+ create_schema (database&, const std::string& name = "", bool drop = true);
+
+ static void
+ drop_schema (database&, const std::string& name = "");
+
+ // Schema migration.
+ //
+ public:
+ static void
+ migrate_schema_pre (database& db,
+ schema_version v,
+ const std::string& name = "")
+ {
+ migrate_schema_impl (db, v, name, migrate_pre);
+ }
+
+ static void
+ migrate_schema_post (database& db,
+ schema_version v,
+ const std::string& name = "")
+ {
+ migrate_schema_impl (db, v, name, migrate_post);
+ }
+
+ static void
+ migrate_schema (database& db,
+ schema_version v,
+ const std::string& name = "")
+ {
+ migrate_schema_impl (db, v, name, migrate_both);
+ }
+
+ // Data migration.
+ //
+ public:
+ // If version is 0, then use the current database version and also
+ // check whether we are in migration. Returns the number of calls made.
+ //
+ static std::size_t
+ migrate_data (database&,
+ schema_version = 0,
+ const std::string& name = "");
+
+ typedef void data_migration_function_type (database&);
+ typedef data_migration_function_type* data_migration_function_ptr;
+
+ typedef details::function_wrapper<data_migration_function_type>
+ data_migration_function_wrapper;
+
+ // The following three variants of the registration functions make
+ // sure that the version is greater that the base model version.
+ // This helps with identifying and removing data migration function
+ // that are no longer required.
+ //
+ // Data migration functions are called in the order of registration.
+ //
+#ifndef ODB_CXX11
+ template <schema_version v, schema_version base>
+ static void
+ data_migration_function (data_migration_function_ptr f,
+ const std::string& name = "")
+ {
+ data_migration_function<v, base> (id_common, f, name);
+ }
+
+#else
+ template <schema_version v, schema_version base, typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::value, void>::type
+ data_migration_function (F f, const std::string& name = "")
+ {
+ data_migration_function<v, base> (id_common, std::move (f), name);
+ }
+#endif
+
+ // Database-specific data migration.
+ //
+#ifndef ODB_CXX11
+ template <schema_version v, schema_version base>
+ static void
+ data_migration_function (database& db,
+ data_migration_function_ptr f,
+ const std::string& name = "")
+ {
+ data_migration_function<v, base> (db.id (), f, name);
+ }
+#else
+ template <schema_version v, schema_version base, typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::value, void>::type
+ data_migration_function (database& db, F f, const std::string& name = "")
+ {
+ data_migration_function<v, base> (db.id (), std::move (f), name);
+ }
+#endif
+
+#ifndef ODB_CXX11
+ template <schema_version v, schema_version base>
+ static void
+ data_migration_function (database_id id,
+ data_migration_function_ptr f,
+ const std::string& name = "")
+ {
+ // If the data migration version is below the base model version
+ // then it will never be called.
+ //
+
+ // Poor man's static_assert.
+ //
+ typedef details::meta::static_assert_test<(v > base || base == 0)>
+ data_migration_function_is_no_longer_necessary;
+
+ char sa [sizeof (data_migration_function_is_no_longer_necessary)];
+ ODB_POTENTIALLY_UNUSED (sa);
+
+ data_migration_function (id, v, f, name);
+ }
+#else
+ template <schema_version v, schema_version base, typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::value, void>::type
+ data_migration_function (database_id id, F f, const std::string& name = "")
+ {
+ // If the data migration version is below the base model version
+ // then it will never be called.
+ //
+ static_assert (v > base || base == 0,
+ "data migration function is no longer necessary");
+
+ data_migration_function (id, v, std::move (f), name);
+ }
+#endif
+
+ // The same as above but take the version as an argument and do
+ // not check whether it is greater than the base model version.
+ //
+#ifndef ODB_CXX11
+ static void
+ data_migration_function (schema_version v,
+ data_migration_function_ptr f,
+ const std::string& name = "")
+ {
+ data_migration_function (id_common, v, f, name);
+ }
+#else
+ template <typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::value, void>::type
+ data_migration_function (schema_version v,
+ F f,
+ const std::string& name = "")
+ {
+ data_migration_function (id_common, v, std::move (f), name);
+ }
+#endif
+
+#ifndef ODB_CXX11
+ static void
+ data_migration_function (database& db,
+ schema_version v,
+ data_migration_function_ptr f,
+ const std::string& name = "")
+ {
+ data_migration_function (db.id (), v, f, name);
+ }
+#else
+ template <typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::value, void>::type
+ data_migration_function (database& db,
+ schema_version v,
+ F f,
+ const std::string& name = "")
+ {
+ data_migration_function (db.id (), v, std::move (f), name);
+ }
+#endif
+
+#ifndef ODB_CXX11
+ static void
+ data_migration_function (database_id i,
+ schema_version v,
+ data_migration_function_ptr f,
+ const std::string& name = "")
+ {
+ data_migration_function (i,
+ v,
+ data_migration_function_wrapper (f),
+ name);
+ }
+#else
+ template <typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::value, void>::type
+ data_migration_function (database_id i,
+ schema_version v,
+ F f,
+ const std::string& name = "")
+ {
+ data_migration_function (
+ i,
+ v,
+ data_migration_function_wrapper (std::move (f)),
+ name);
+ }
+#endif
+
+ private:
+ static void
+ data_migration_function (database_id,
+ schema_version,
+ data_migration_function_wrapper,
+ const std::string& name);
+
+ // Combined schema and data migration.
+ //
+ public:
+ // Migrate both schema and data to the specified version. If version
+ // is not specified, then migrate to the current model version.
+ //
+ static void
+ migrate (database&, schema_version = 0, const std::string& name = "");
+
+ // Schema version information.
+ //
+ public:
+ // Return the base model version.
+ //
+ static schema_version
+ base_version (const database& db, const std::string& name = "")
+ {
+ return base_version (db.id (), name);
+ }
+
+ static schema_version
+ base_version (database_id, const std::string& name = "");
+
+ // Return the current model version.
+ //
+ static schema_version
+ current_version (const database& db, const std::string& name = "")
+ {
+ return current_version (db.id (), name);
+ }
+
+ static schema_version
+ current_version (database_id, const std::string& name = "");
+
+ // Return current model version + 1 (that is, one past current) if
+ // the passed version is equal to or greater than current. If the
+ // version is not specified, then use the current database version.
+ //
+ static schema_version
+ next_version (const database& db,
+ schema_version v = 0,
+ const std::string& name = "")
+ {
+ return next_version (db.id (), v == 0 ? db.schema_version () : v, name);
+ }
+
+ static schema_version
+ next_version (database_id,
+ schema_version,
+ const std::string& name = "");
+
+ // Schema existence.
+ //
+ public:
+ // Test for presence of a schema with a specific name.
+ //
+ static bool
+ exists (const database& db, const std::string& name = "")
+ {
+ return exists (db.id (), name);
+ }
+
+ static bool
+ exists (database_id, const std::string& name = "");
+
+ private:
+ enum migrate_mode
+ {
+ migrate_pre,
+ migrate_post,
+ migrate_both
+ };
+
+ static void
+ migrate_schema_impl (database&,
+ schema_version,
+ const std::string& name,
+ migrate_mode);
+ };
+
+ // Static data migration function registration.
+ //
+ template <schema_version v, schema_version base>
+ struct data_migration_entry
+ {
+ typedef schema_catalog::data_migration_function_type function_type;
+
+#ifndef ODB_CXX11
+ data_migration_entry (function_type* f, const std::string& name = "")
+ {
+ schema_catalog::data_migration_function<v, base> (f, name);
+ }
+#else
+ template <typename F>
+ data_migration_entry (F f,
+ const std::string& name = "",
+ typename std::enable_if<std::is_convertible<
+ F, std::function<function_type>>::value>
+ ::type* = 0)
+ {
+ schema_catalog::data_migration_function<v, base> (std::move (f), name);
+ }
+#endif
+
+#ifndef ODB_CXX11
+ data_migration_entry (database_id id,
+ function_type *f,
+ const std::string& name = "")
+ {
+ schema_catalog::data_migration_function<v, base> (id, v, f, name);
+ }
+#else
+ template <typename F>
+ data_migration_entry (database_id id,
+ F f,
+ const std::string& name = "",
+ typename std::enable_if<std::is_convertible<
+ F, std::function<function_type>>::value>
+ ::type* = 0)
+ {
+ schema_catalog::data_migration_function<v, base> (id,
+ v,
+ std::move (f),
+ name);
+ }
+#endif
+ };
+
+ namespace common
+ {
+ using odb::schema_catalog;
+ using odb::data_migration_entry;
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SCHEMA_CATALOG_HXX
diff --git a/libodb/odb/schema-version.hxx b/libodb/odb/schema-version.hxx
new file mode 100644
index 0000000..1f047a4
--- /dev/null
+++ b/libodb/odb/schema-version.hxx
@@ -0,0 +1,71 @@
+// file : odb/schema-version.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SCHEMA_VERSION_HXX
+#define ODB_SCHEMA_VERSION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx> // schema_version
+
+namespace odb
+{
+ struct schema_version_migration
+ {
+ schema_version_migration (schema_version v = 0, bool m = false)
+ : version (v), migration (m) {}
+
+ schema_version version;
+ bool migration;
+ };
+
+ // Version ordering is as follows: {1,f} < {2,t} < {2,f} < {3,t}
+ //
+ inline bool
+ operator== (const schema_version_migration& x,
+ const schema_version_migration& y)
+ {
+ return x.version == y.version && x.migration == y.migration;
+ }
+
+ inline bool
+ operator!= (const schema_version_migration& x,
+ const schema_version_migration& y)
+ {
+ return !(x == y);
+ }
+
+ inline bool
+ operator< (const schema_version_migration& x,
+ const schema_version_migration& y)
+ {
+ return x.version < y.version ||
+ (x.version == y.version && x.migration && !y.migration);
+ }
+
+ inline bool
+ operator> (const schema_version_migration& x,
+ const schema_version_migration& y)
+ {
+ return x.version > y.version ||
+ (x.version == y.version && !x.migration && y.migration);
+ }
+
+ inline bool
+ operator<= (const schema_version_migration& x,
+ const schema_version_migration& y)
+ {
+ return !(x > y);
+ }
+
+ inline bool
+ operator>= (const schema_version_migration& x,
+ const schema_version_migration& y)
+ {
+ return !(x < y);
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SCHEMA_VERSION_HXX
diff --git a/libodb/odb/section.cxx b/libodb/odb/section.cxx
new file mode 100644
index 0000000..0fe5211
--- /dev/null
+++ b/libodb/odb/section.cxx
@@ -0,0 +1,27 @@
+// file : odb/section.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/section.hxx>
+
+namespace odb
+{
+ void section::
+ disarm ()
+ {
+ transaction& t (transaction::current ());
+ t.callback_unregister (this);
+ state_.armed = 0;
+ }
+
+ void section::
+ transacion_callback (unsigned short event, void* key, unsigned long long)
+ {
+ section& s (*static_cast<section*> (key));
+
+ if (event == transaction::event_rollback && s.state_.restore)
+ s.state_.changed = 1;
+
+ s.state_.armed = 0;
+ s.state_.restore = 0;
+ }
+}
diff --git a/libodb/odb/section.hxx b/libodb/odb/section.hxx
new file mode 100644
index 0000000..98b98cf
--- /dev/null
+++ b/libodb/odb/section.hxx
@@ -0,0 +1,122 @@
+// file : odb/section.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SECTION_HXX
+#define ODB_SECTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/transaction.hxx>
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ class LIBODB_EXPORT section
+ {
+ public:
+ // Load state.
+ //
+ bool
+ loaded () const {return state_.loaded;}
+
+ // Mark a loaded section as not loaded. This, for example, can be
+ // useful if you don't want the section to be reloaded during the
+ // object reload.
+ //
+ void
+ unload ()
+ {
+ state_.loaded = 0;
+ state_.changed = 0;
+ state_.restore = 0;
+ }
+
+ // Mark an unloaded section as loaded. This, for example, can be useful if
+ // you don't want to load the old data before overwriting it with update().
+ //
+ void
+ load ()
+ {
+ state_.loaded = 1;
+ }
+
+ // Change state.
+ //
+ bool
+ changed () const {return state_.changed;}
+
+ // Mark the section as changed.
+ //
+ void
+ change ()
+ {
+ state_.changed = 1;
+ state_.restore = 0;
+ }
+
+ // User data. 4 bits of custom state.
+ //
+ unsigned char
+ user_data () const {return state_.user;}
+
+ void
+ user_data (unsigned char u) {state_.user = u;}
+
+ public:
+ section ()
+ {
+ state_.loaded = 0;
+ state_.changed = 0;
+ state_.armed = 0;
+ state_.restore = 0;
+ }
+
+ ~section ()
+ {
+ if (state_.armed)
+ disarm ();
+ }
+
+ // Implementation details.
+ //
+ public:
+ // Arm the callback and set the restore flag if transaction is not NULL.
+ //
+ void
+ reset (bool l = false, bool c = false, transaction* t = 0) const
+ {
+ state_.loaded = l;
+ state_.changed = c;
+
+ if (t != 0 && !state_.armed)
+ {
+ t->callback_register (&transacion_callback,
+ const_cast<section*> (this));
+ state_.armed = 1;
+ }
+
+ state_.restore = (t != 0);
+ }
+
+ private:
+ void
+ disarm ();
+
+ static void
+ transacion_callback (unsigned short, void* key, unsigned long long);
+
+ private:
+ mutable struct
+ {
+ unsigned char loaded : 1;
+ unsigned char changed : 1;
+ unsigned char armed : 1; // Transaction callback is armed.
+ unsigned char restore: 1; // Restore changed flag on rollback.
+ unsigned char user : 4; // User data.
+ } state_;
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_SECTION_HXX
diff --git a/libodb/odb/session.cxx b/libodb/odb/session.cxx
new file mode 100644
index 0000000..bc0e854
--- /dev/null
+++ b/libodb/odb/session.cxx
@@ -0,0 +1,66 @@
+// file : odb/session.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/exceptions.hxx>
+#include <odb/session.hxx>
+
+#include <odb/details/tls.hxx>
+
+namespace odb
+{
+ using namespace details;
+
+ static ODB_TLS_POINTER (session) current_session;
+
+ session::
+ session (bool make_current)
+ {
+ if (make_current)
+ {
+ if (has_current ())
+ throw already_in_session ();
+
+ current_pointer (this);
+ }
+ }
+
+ session::
+ ~session ()
+ {
+ // If we are the current thread's session, reset it.
+ //
+ if (current_pointer () == this)
+ reset_current ();
+ }
+
+ session* session::
+ current_pointer ()
+ {
+ return tls_get (current_session);
+ }
+
+ void session::
+ current_pointer (session* s)
+ {
+ tls_set (current_session, s);
+ }
+
+ session& session::
+ current ()
+ {
+ session* cur (tls_get (current_session));
+
+ if (cur == 0)
+ throw not_in_session ();
+
+ return *cur;
+ }
+
+ //
+ // object_map_base
+ //
+ session::object_map_base::
+ ~object_map_base ()
+ {
+ }
+}
diff --git a/libodb/odb/session.hxx b/libodb/odb/session.hxx
new file mode 100644
index 0000000..a14c42f
--- /dev/null
+++ b/libodb/odb/session.hxx
@@ -0,0 +1,217 @@
+// file : odb/session.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SESSION_HXX
+#define ODB_SESSION_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <typeinfo>
+
+#include <odb/traits.hxx>
+#include <odb/forward.hxx>
+
+#include <odb/details/shared-ptr.hxx>
+#include <odb/details/type-info.hxx>
+
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ class LIBODB_EXPORT session
+ {
+ public:
+ typedef odb::database database_type;
+
+ // If the make_current argument is true, then set the current thread's
+ // session to this session. If another session is already in effect,
+ // throw the already_in_session exception.
+ //
+ session (bool make_current = true);
+
+ // Reset the current thread's session if it is this session.
+ //
+ ~session ();
+
+ // Current session.
+ //
+ public:
+ // Return true if there is a session in effect in the current
+ // thread.
+ //
+ static bool
+ has_current () {return current_pointer () != 0;}
+
+ // Get current thread's session. Throw if no session is in effect.
+ //
+ static session&
+ current ();
+
+ // Set current thread's session.
+ //
+ static void
+ current (session& s) {current_pointer (&s);}
+
+ // Revert to the no session in effect state for the current thread.
+ //
+ static void
+ reset_current () {current_pointer (0);}
+
+ // Pointer versions.
+ //
+ static session*
+ current_pointer ();
+
+ static void
+ current_pointer (session*);
+
+ // Copying or assignment of sessions is not supported.
+ //
+ private:
+ session (const session&);
+ session& operator= (const session&);
+
+ public:
+ struct LIBODB_EXPORT object_map_base: details::shared_base
+ {
+ virtual
+ ~object_map_base ();
+ };
+
+ template <typename T>
+ struct object_map: object_map_base,
+ std::map<typename object_traits<T>::id_type,
+ typename object_traits<T>::pointer_type>
+ {
+ };
+
+ // Object cache.
+ //
+ public:
+ // Position in the cache of the inserted element.
+ //
+ template <typename T>
+ struct cache_position;
+
+ template <typename T>
+ cache_position<T>
+ cache_insert (database_type&,
+ const typename object_traits<T>::id_type&,
+ const typename object_traits<T>::pointer_type&);
+
+ template <typename T>
+ typename object_traits<T>::pointer_type
+ cache_find (database_type&,
+ const typename object_traits<T>::id_type&) const;
+
+ template <typename T>
+ void
+ cache_erase (const cache_position<T>&);
+
+ template <typename T>
+ void
+ cache_erase (database_type&, const typename object_traits<T>::id_type&);
+
+ // Low-level object cache access (iteration, etc).
+ //
+ public:
+ typedef std::map<const std::type_info*,
+ details::shared_ptr<object_map_base>,
+ details::type_info_comparator> type_map;
+
+ typedef std::map<database_type*, type_map> database_map;
+
+ database_map&
+ map () {return db_map_;}
+
+ const database_map&
+ map () const {return db_map_;}
+
+ // Static cache API as expected by the rest of ODB.
+ //
+ public:
+ static bool
+ _has_cache () {return has_current ();}
+
+ // Position in the cache of the inserted element. The requirements
+ // for this class template are: default and copy-constructible as
+ // well as copy-assignable. The default constructor creates an
+ // empty/NULL position.
+ //
+ template <typename T>
+ struct cache_position
+ {
+ typedef object_map<T> map;
+ typedef typename map::iterator iterator;
+
+ cache_position (): map_ (0) {}
+ cache_position (map& m, const iterator& p): map_ (&m), pos_ (p) {}
+
+ cache_position (const cache_position& p)
+ : map_ (p.map_), pos_ (p.pos_) {}
+
+ cache_position&
+ operator= (const cache_position& p)
+ {
+ // It might not be ok to use an uninitialized iterator on the rhs.
+ //
+ if (p.map_ != 0)
+ pos_ = p.pos_;
+ map_ = p.map_;
+ return *this;
+ }
+
+ map* map_;
+ iterator pos_;
+ };
+
+ // The following cache management functions are all static to
+ // allow for a custom notion of a current session. The erase()
+ // function is called to remove the object if the operation
+ // that caused it to be inserted (e.g., load) failed.
+ //
+ template <typename T>
+ static cache_position<T>
+ _cache_insert (database_type&,
+ const typename object_traits<T>::id_type&,
+ const typename object_traits<T>::pointer_type&);
+
+ template <typename T>
+ static typename object_traits<T>::pointer_type
+ _cache_find (database_type&, const typename object_traits<T>::id_type&);
+
+ template <typename T>
+ static void
+ _cache_erase (const cache_position<T>&);
+
+ // Notifications. These are called after per-object callbacks for
+ // post_{persist, load, update, erase} events.
+ //
+ template <typename T>
+ static void
+ _cache_persist (const cache_position<T>&) {}
+
+ template <typename T>
+ static void
+ _cache_load (const cache_position<T>&) {}
+
+ template <typename T>
+ static void
+ _cache_update (database_type&, const T&) {}
+
+ template <typename T>
+ static void
+ _cache_erase (database_type&, const typename object_traits<T>::id_type&);
+
+ protected:
+ database_map db_map_;
+ };
+}
+
+#include <odb/session.ixx>
+#include <odb/session.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SESSION_HXX
diff --git a/libodb/odb/session.ixx b/libodb/odb/session.ixx
new file mode 100644
index 0000000..4acdc1b
--- /dev/null
+++ b/libodb/odb/session.ixx
@@ -0,0 +1,60 @@
+// file : odb/session.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/exceptions.hxx>
+
+namespace odb
+{
+ template <typename T>
+ inline void session::
+ cache_erase (const cache_position<T>& p)
+ {
+ // @@ Empty maps are not cleaned up by this version of erase.
+ //
+ if (p.map_ != 0)
+ p.map_->erase (p.pos_);
+ }
+
+ template <typename T>
+ inline typename session::cache_position<T> session::
+ _cache_insert (database_type& db,
+ const typename object_traits<T>::id_type& id,
+ const typename object_traits<T>::pointer_type& obj)
+ {
+ if (session* s = current_pointer ())
+ return s->cache_insert<T> (db, id, obj);
+ else
+ return cache_position<T> ();
+ }
+
+ template <typename T>
+ inline typename object_traits<T>::pointer_type session::
+ _cache_find (database_type& db, const typename object_traits<T>::id_type& id)
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ if (const session* s = current_pointer ())
+ return s->cache_find<T> (db, id);
+ else
+ return pointer_type ();
+ }
+
+ template <typename T>
+ inline void session::
+ _cache_erase (const cache_position<T>& p)
+ {
+ // @@ Empty maps are not cleaned up by this version of erase.
+ //
+ if (p.map_ != 0)
+ p.map_->erase (p.pos_);
+ }
+
+ template <typename T>
+ inline void session::
+ _cache_erase (database_type& db,
+ const typename object_traits<T>::id_type& id)
+ {
+ if (session* s = current_pointer ())
+ s->cache_erase<T> (db, id);
+ }
+}
diff --git a/libodb/odb/session.txx b/libodb/odb/session.txx
new file mode 100644
index 0000000..d74fe0f
--- /dev/null
+++ b/libodb/odb/session.txx
@@ -0,0 +1,90 @@
+// file : odb/session.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ template <typename T>
+ typename session::cache_position<T> session::
+ cache_insert (database_type& db,
+ const typename object_traits<T>::id_type& id,
+ const typename object_traits<T>::pointer_type& obj)
+ {
+ type_map& tm (db_map_[&db]);
+ details::shared_ptr<object_map_base>& pom (tm[&typeid (T)]);
+
+ if (!pom)
+ pom.reset (new (details::shared) object_map<T>);
+
+ object_map<T>& om (static_cast<object_map<T>&> (*pom));
+
+ typename object_map<T>::value_type vt (id, obj);
+ std::pair<typename object_map<T>::iterator, bool> r (om.insert (vt));
+
+ // In what situation may we possibly attempt to reinsert the object?
+ // For example, when the user loads the same object in two different
+ // instances (i.e., load into a pre-allocated object). In this case
+ // we should probably update our entries accordingly.
+ //
+ if (!r.second)
+ r.first->second = obj;
+
+ return cache_position<T> (om, r.first);
+ }
+
+ template <typename T>
+ typename object_traits<T>::pointer_type session::
+ cache_find (database_type& db,
+ const typename object_traits<T>::id_type& id) const
+ {
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ database_map::const_iterator di (db_map_.find (&db));
+
+ if (di == db_map_.end ())
+ return pointer_type ();
+
+ const type_map& tm (di->second);
+ type_map::const_iterator ti (tm.find (&typeid (T)));
+
+ if (ti == tm.end ())
+ return pointer_type ();
+
+ const object_map<T>& om (static_cast<const object_map<T>&> (*ti->second));
+ typename object_map<T>::const_iterator oi (om.find (id));
+
+ if (oi == om.end ())
+ return pointer_type ();
+
+ return oi->second;
+ }
+
+ template <typename T>
+ void session::
+ cache_erase (database_type& db, const typename object_traits<T>::id_type& id)
+ {
+ database_map::iterator di (db_map_.find (&db));
+
+ if (di == db_map_.end ())
+ return;
+
+ type_map& tm (di->second);
+ type_map::iterator ti (tm.find (&typeid (T)));
+
+ if (ti == tm.end ())
+ return;
+
+ object_map<T>& om (static_cast<object_map<T>&> (*ti->second));
+ typename object_map<T>::iterator oi (om.find (id));
+
+ if (oi == om.end ())
+ return;
+
+ om.erase (oi);
+
+ if (om.empty ())
+ tm.erase (ti);
+
+ if (tm.empty ())
+ db_map_.erase (di);
+ }
+}
diff --git a/libodb/odb/simple-object-result.hxx b/libodb/odb/simple-object-result.hxx
new file mode 100644
index 0000000..53c0cb2
--- /dev/null
+++ b/libodb/odb/simple-object-result.hxx
@@ -0,0 +1,201 @@
+// file : odb/simple-object-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_SIMPLE_OBJECT_RESULT_HXX
+#define ODB_SIMPLE_OBJECT_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::size_t
+#include <utility> // std::move
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+#include <odb/result.hxx>
+#include <odb/object-result.hxx>
+#include <odb/pointer-traits.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ // Implementation for non-polymorphic objects with object id.
+ //
+ template <typename T>
+ class object_result_impl: public result_impl
+ {
+ protected:
+ // In result_impl, T is always non-const and the same as object_type.
+ //
+ typedef T object_type;
+ typedef odb::object_traits<object_type> object_traits;
+ typedef typename object_traits::id_type id_type;
+
+ typedef typename object_traits::pointer_type pointer_type;
+ typedef odb::pointer_traits<pointer_type> pointer_traits;
+
+ friend class result<T>;
+ friend class result<const T>;
+ friend class result_iterator<T, class_object>;
+ friend class result_iterator<const T, class_object>;
+ friend class object_result_iterator<T, id_type, false>;
+ friend class object_result_iterator<const T, id_type, false>;
+
+ protected:
+ object_result_impl (odb::connection& conn)
+ : result_impl (conn), begin_ (true), end_ (false), current_ ()
+ {
+ }
+
+ // To make this work with all kinds of pointers (raw, std::auto_ptr,
+ // shared), we need to make sure we don't make any copies of the
+ // pointer on the return path.
+ //
+ pointer_type&
+ current ()
+ {
+ if (pointer_traits::null_ptr (current_) && !end_)
+ load ();
+
+ return current_;
+ }
+
+ void
+ release ()
+ {
+ current_ = pointer_type ();
+ guard_.release ();
+ }
+
+ void
+ begin ()
+ {
+ if (begin_)
+ {
+ next ();
+ begin_ = false;
+ }
+ }
+
+ bool
+ end () const
+ {
+ return end_;
+ }
+
+ protected:
+ // The fetch argument is a hint to the implementation. If it is
+ // false then it means load_id() was already called (and presumably
+ // fetched the data into the object image) and the object image
+ // is still valid (so the implementation doesn't need to fetch
+ // the data again).
+ //
+ virtual void
+ load (object_type&, bool fetch = true) = 0;
+
+ virtual id_type
+ load_id () = 0;
+
+ virtual void
+ next () = 0;
+
+ virtual void
+ cache () = 0;
+
+ virtual std::size_t
+ size () = 0;
+
+ protected:
+#ifdef ODB_CXX11
+ void
+ current (pointer_type& p, bool guard = true)
+ {
+ current_ = std::move (p);
+
+ if (guard)
+ guard_.reset (current_);
+ else
+ guard_.reset ();
+ }
+
+ void
+ current (pointer_type&& p, bool guard = true)
+ {
+ current (p, guard);
+ }
+#else
+ void
+ current (pointer_type p, bool guard = true)
+ {
+ current_ = p;
+
+ if (guard)
+ guard_.reset (current_);
+ else
+ guard_.reset ();
+ }
+#endif
+
+ bool begin_;
+ bool end_;
+
+ private:
+ void
+ load ();
+
+ private:
+ pointer_type current_;
+ typename pointer_traits::guard guard_;
+ };
+
+ template <typename T, typename ID>
+ class object_result_iterator<T, ID, false>
+ {
+ public:
+ // T can be const T while object_type is always non-const.
+ //
+ typedef typename object_traits<T>::object_type object_type;
+ typedef typename object_traits<T>::id_type id_type;
+
+ typedef object_result_impl<object_type> result_impl_type;
+
+ public:
+ object_result_iterator (result_impl_type* res)
+ : res_ (res)
+ {
+ }
+
+ public:
+ typedef typename object_traits<T>::pointer_type pointer_type;
+
+ pointer_type
+ load ()
+ {
+#ifdef ODB_CXX11
+ pointer_type r (std::move (res_->current ()));
+#else
+ pointer_type r (res_->current ());
+#endif
+ res_->release ();
+ return r;
+ }
+
+ void
+ load (object_type&);
+
+ id_type
+ id ()
+ {
+ return res_->load_id ();
+ }
+
+ protected:
+ result_impl_type* res_;
+ };
+}
+
+#include <odb/simple-object-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_SIMPLE_OBJECT_RESULT_HXX
diff --git a/libodb/odb/simple-object-result.txx b/libodb/odb/simple-object-result.txx
new file mode 100644
index 0000000..00521f8
--- /dev/null
+++ b/libodb/odb/simple-object-result.txx
@@ -0,0 +1,58 @@
+// file : odb/simple-object-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // object_result_impl
+ //
+
+ template <typename T>
+ void object_result_impl<T>::
+ load ()
+ {
+ // First check the session.
+ //
+ const id_type& id (load_id ());
+
+ pointer_type p (object_traits::pointer_cache_traits::find (db_, id));
+
+ if (!pointer_traits::null_ptr (p))
+ current (p, false); // Pointer from cache should not be guarded.
+ else
+ {
+ p = object_traits::create ();
+
+ typename object_traits::pointer_cache_traits::insert_guard ig (
+ object_traits::pointer_cache_traits::insert (db_, id, p));
+
+ object_type& obj (pointer_traits::get_ref (p));
+ current (p);
+ load (obj, false);
+ object_traits::pointer_cache_traits::load (ig.position ());
+ ig.release ();
+ }
+ }
+
+ //
+ // object_result_iterator
+ //
+
+ template <typename T, typename ID>
+ void object_result_iterator<T, ID, false>::
+ load (object_type& obj)
+ {
+ if (res_->end ())
+ return;
+
+ typedef odb::object_traits<object_type> object_traits;
+
+ typename object_traits::reference_cache_traits::position_type p (
+ object_traits::reference_cache_traits::insert (
+ res_->db_, res_->load_id (), obj));
+ typename object_traits::reference_cache_traits::insert_guard ig (p);
+ res_->load (obj, false);
+ object_traits::reference_cache_traits::load (p);
+ ig.release ();
+ }
+}
diff --git a/libodb/odb/statement-processing-common.hxx b/libodb/odb/statement-processing-common.hxx
new file mode 100644
index 0000000..23661fc
--- /dev/null
+++ b/libodb/odb/statement-processing-common.hxx
@@ -0,0 +1,214 @@
+// file : odb/statement-processingc-common.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STATEMENT_PROCESSING_COMMON_HXX
+#define ODB_STATEMENT_PROCESSING_COMMON_HXX
+
+#include <odb/pre.hxx>
+
+//#define LIBODB_DEBUG_STATEMENT_PROCESSING 1
+//#define LIBODB_TRACE_STATEMENT_PROCESSING 1
+
+#include <string>
+#include <cstddef> // std::size_t
+
+namespace odb
+{
+ typedef std::char_traits<char> traits;
+
+ static inline const char*
+ find (const char* b, const char* e, char c)
+ {
+ return traits::find (b, e - b, c);
+ }
+
+ static inline const char*
+ rfind (const char* b, const char* e, char c)
+ {
+ for (--e; b != e; --e)
+ if (*e == c)
+ return e;
+
+ return 0;
+ }
+
+ static inline const char*
+ find (const char* b, const char* e, const char* s, std::size_t n)
+ {
+ for (; b != e; ++b)
+ {
+ if (*b == *s &&
+ static_cast<std::size_t> (e - b) >= n &&
+ traits::compare (b, s, n) == 0)
+ return b;
+ }
+
+ return 0;
+ }
+
+ // Iterate over INSERT column/value list, UPDATE SET expression list,
+ // or SELECT column/join list.
+ //
+ // for (const char* b (columns_begin), *e (begin (b, end));
+ // e != 0;
+ // next (b, e, end))
+ // {
+ // // b points to the beginning of the value (i.e., one past '(').
+ // // e points one past the end of the value (i.e., to ',', ')', or '\n').
+ // }
+ //
+ // // b points one past the last value.
+ //
+ static inline const char*
+ paren_begin (const char*& b, const char* end)
+ {
+ // Note that the list may not end with '\n'.
+
+ b++; // Skip '('.
+ const char* e (find (b, end, '\n'));
+ return (e != 0 ? e : end) - 1; // Skip ',' or ')'.
+ }
+
+ static inline void
+ paren_next (const char*& b, const char*& e, const char* end)
+ {
+ if (*e == ',')
+ {
+ b = e + 2; // Skip past '\n'.
+ e = find (b, end, '\n');
+ e = (e != 0 ? e : end) - 1; // Skip ',' or ')'.
+ }
+ else
+ {
+ b = (e + 1 != end ? e + 2 : end); // Skip past '\n'.
+ e = 0;
+ }
+ }
+
+ static inline const char*
+ comma_begin (const char* b, const char* end)
+ {
+ // Note that the list may not end with '\n'.
+
+ const char* e (find (b, end, '\n'));
+ return e != 0 ? e - (*(e - 1) == ',' ? 1 : 0) : end; // Skip ','.
+ }
+
+ static inline void
+ comma_next (const char*& b, const char*& e, const char* end)
+ {
+ if (*e == ',')
+ {
+ b = e + 2; // Skip past '\n'.
+ e = find (b, end, '\n');
+ e = (e != 0 ? e - (*(e - 1) == ',' ? 1 : 0) : end); // Skip ','.
+ }
+ else
+ {
+ b = (e != end ? e + 1 : end); // Skip past '\n'.
+ e = 0;
+ }
+ }
+
+ // Only allows A-Z and spaces before prefix (e.g., JOIN in LEFT OUTER JOIN).
+ //
+ static inline bool
+ fuzzy_prefix (const char* b,
+ const char* end,
+ const char* prefix,
+ std::size_t prefix_size)
+ {
+ for (; b != end; ++b)
+ {
+ char c (*b);
+
+ if ((c < 'A' || c > 'Z') && c != ' ')
+ break;
+
+ if (c == *prefix &&
+ static_cast<std::size_t> (end - b) > prefix_size &&
+ traits::compare (b, prefix, prefix_size) == 0)
+ return true;
+ }
+
+ return false;
+ }
+
+ static inline const char*
+ newline_begin (const char* b, const char* end)
+ {
+ // Note that the list may not end with '\n'.
+
+ const char* e (find (b, end, '\n'));
+ return e != 0 ? e : end;
+ }
+
+ static inline void
+ newline_next (const char*& b,
+ const char*& e,
+ const char* end,
+ const char* prefix,
+ std::size_t prefix_size,
+ bool prefix_fuzzy = false)
+ {
+ if (e != end)
+ e++; // Skip past '\n'.
+
+ b = e;
+
+ // Do we have another element?
+ //
+ if (static_cast<std::size_t> (end - b) > prefix_size &&
+ (prefix_fuzzy
+ ? fuzzy_prefix (b, end, prefix, prefix_size)
+ : traits::compare (b, prefix, prefix_size) == 0))
+ {
+ e = find (b, end, '\n');
+ if (e == 0)
+ e = end;
+ }
+ else
+ e = 0;
+ }
+
+ // Note that end must point to the beginning of the list.
+ //
+ static inline const char*
+ newline_rbegin (const char* e, const char* end)
+ {
+ const char* b (rfind (end, e - 1, '\n'));
+ return b != 0 ? b + 1 : end; // Skip past '\n'.
+ }
+
+ static inline void
+ newline_rnext (const char*& b, const char*& e, const char* end)
+ {
+ if (b != end)
+ {
+ e = b - 1; // Skip to previous '\n'.
+ b = rfind (end, e - 1, '\n');
+ b = (b != 0 ? b + 1 : end); // Skip past '\n'.
+ }
+ else
+ {
+ e = end - 1; // One before the first element.
+ b = 0;
+ }
+ }
+
+ // Fast path: just remove the "structure".
+ //
+ static inline void
+ process_fast (const char* s, std::string& r)
+ {
+ r = s;
+ for (std::size_t i (r.find ('\n'));
+ i != std::string::npos;
+ i = r.find ('\n', i))
+ r[i++] = ' ';
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STATEMENT_PROCESSING_COMMON_HXX
diff --git a/libodb/odb/statement-processing.cxx b/libodb/odb/statement-processing.cxx
new file mode 100644
index 0000000..708c9ab
--- /dev/null
+++ b/libodb/odb/statement-processing.cxx
@@ -0,0 +1,685 @@
+// file : odb/statement-processing.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/statement-processing-common.hxx>
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+# include <iostream>
+#endif
+
+#include <odb/statement.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ typedef const void* const* bind_type;
+
+ static inline const void*
+ bind_at (size_t i, bind_type bind, size_t bind_skip)
+ {
+ const char* b (reinterpret_cast<const char*> (bind));
+ return *reinterpret_cast<bind_type> (b + i * bind_skip);
+ }
+
+ void statement::
+ process_insert (string& r,
+ const char* s,
+ bind_type bind,
+ size_t bind_size,
+ size_t bind_skip,
+ char param_symbol,
+ char param_symbol2)
+ {
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ assert (bind_size != 0); // Cannot be versioned.
+#endif
+
+ bool fast (true); // Fast case (if all present).
+ for (size_t i (0); i != bind_size && fast; ++i)
+ {
+ if (bind_at (i, bind, bind_skip) == 0)
+ fast = false;
+ }
+
+ // Fast path: just remove the "structure".
+ //
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ if (fast)
+ {
+ process_fast (s, r);
+ return;
+ }
+#endif
+
+ // Scan the statement and store the positions of various parts.
+ //
+ size_t n (traits::length (s));
+ const char* e (s + n);
+
+ // Header.
+ //
+ const char* p (find (s, e, '\n'));
+ assert (p != 0);
+ size_t header_size (p - s);
+ p++;
+
+ // Column list.
+ //
+ const char* columns_begin (0);
+ if (*p == '(')
+ {
+ columns_begin = p;
+
+ // Find the end of the column list.
+ //
+ for (const char* ce (paren_begin (p, e)); ce != 0; paren_next (p, ce, e))
+ ;
+ }
+
+ // OUTPUT
+ //
+ const char* output_begin (0);
+ size_t output_size (0);
+ if (e - p > 7 && traits::compare (p, "OUTPUT ", 7) == 0)
+ {
+ output_begin = p;
+ p += 7;
+ p = find (p, e, '\n');
+ assert (p != 0);
+ output_size = p - output_begin;
+ p++;
+ }
+
+ // VALUES or DEFAULT VALUES
+ //
+ bool empty (true); // DEFAULT VALUES case (if none present).
+ const char* values_begin (0);
+ if (e - p > 7 && traits::compare (p, "VALUES\n", 7) == 0)
+ {
+ p += 7;
+ values_begin = p;
+
+ size_t bi (0);
+ for (const char* ve (paren_begin (p, e)); ve != 0; paren_next (p, ve, e))
+ {
+ // We cannot be empty if we have a non-parameterized value, e.g.,
+ // INSERT ... VALUES(1,?). We also cannot be empty if this value
+ // is present in the bind array.
+ //
+ if ((find (p, ve, param_symbol) == 0 &&
+ (param_symbol2 == '\0' || find (p, ve, param_symbol2) == 0)) ||
+ bind_at (bi++, bind, bind_skip) != 0)
+ empty = false;
+ }
+ }
+ else
+ {
+ // Must be DEFAULT VALUES.
+ //
+ assert (traits::compare (p, "DEFAULT VALUES", 14) == 0);
+ p += 14;
+
+ if (*p == '\n')
+ p++;
+ }
+
+ // Trailer.
+ //
+ const char* trailer_begin (0);
+ size_t trailer_size (0);
+ if (e - p != 0)
+ {
+ trailer_begin = p;
+ trailer_size = e - p;
+ }
+
+ // Empty case.
+ //
+ if (empty)
+ {
+ r.reserve (header_size +
+ (output_size == 0 ? 0 : output_size + 1) +
+ 15 + // For " DEFAULT VALUES"
+ (trailer_size == 0 ? 0 : trailer_size + 1));
+
+ r.assign (s, header_size);
+
+ if (output_size != 0)
+ {
+ r += ' ';
+ r.append (output_begin, output_size);
+ }
+
+ r += " DEFAULT VALUES";
+
+ if (trailer_size != 0)
+ {
+ r += ' ';
+ r.append (trailer_begin, trailer_size);
+ }
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (r.size () != n)
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+
+ return;
+ }
+
+ // Assume the same size as the original. It can only shrink, and in
+ // most cases only slightly. So this is a good approximation.
+ //
+ r.reserve (n);
+ r.assign (s, header_size);
+
+ // Column list.
+ //
+ {
+ r += ' ';
+
+ size_t i (0), bi (0);
+
+ for (const char *c (columns_begin), *ce (paren_begin (c, e)),
+ *v (values_begin), *ve (paren_begin (v, e));
+ ce != 0; paren_next (c, ce, e), paren_next (v, ve, e))
+ {
+ // See if the value contains the parameter symbol and, if so,
+ // whether it is present in the bind array.
+ //
+ if ((find (v, ve, param_symbol) != 0 ||
+ (param_symbol2 != '\0' && find (v, ve, param_symbol2) != 0)) &&
+ bind_at (bi++, bind, bind_skip) == 0)
+ continue;
+
+ // Append the column.
+ //
+ if (i++ == 0)
+ r += '(';
+ else
+ r += ", "; // Add the space for consistency with the fast path.
+
+ r.append (c, ce - c);
+ }
+
+ r += ')';
+ }
+
+ // OUTPUT
+ //
+ if (output_size != 0)
+ {
+ r += ' ';
+ r.append (output_begin, output_size);
+ }
+
+ // Value list.
+ //
+ {
+ r += " VALUES ";
+
+ size_t i (0), bi (0);
+
+ for (const char* v (values_begin), *ve (paren_begin (v, e));
+ ve != 0; paren_next (v, ve, e))
+ {
+ // See if the value contains the parameter symbol and, if so,
+ // whether it is present in the bind array.
+ //
+ if ((find (v, ve, param_symbol) != 0 ||
+ (param_symbol2 != '\0' && find (v, ve, param_symbol2) != 0)) &&
+ bind_at (bi++, bind, bind_skip) == 0)
+ continue;
+
+ // Append the value.
+ //
+ if (i++ == 0)
+ r += '(';
+ else
+ r += ", "; // Add the space for consistency with the fast path.
+
+ r.append (v, ve - v);
+ }
+
+ r += ')';
+ }
+
+ // Trailer.
+ //
+ if (trailer_size != 0)
+ {
+ r += ' ';
+ r.append (trailer_begin, trailer_size);
+ }
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (r.size () != n)
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+ }
+
+ void statement::
+ process_update (string& r,
+ const char* s,
+ bind_type bind,
+ size_t bind_size,
+ size_t bind_skip,
+ char param_symbol,
+ char param_symbol2)
+ {
+ bool fast (true); // Fast case (if all present).
+ for (size_t i (0); i != bind_size && fast; ++i)
+ {
+ if (bind_at (i, bind, bind_skip) == 0)
+ fast = false;
+ }
+
+ // Fast path: just remove the "structure".
+ //
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ if (fast)
+ {
+ process_fast (s, r);
+ return;
+ }
+#endif
+
+ // Scan the statement and store the positions of various parts.
+ //
+ size_t n (traits::length (s));
+ const char* e (s + n);
+
+ // Header.
+ //
+ const char* p (find (s, e, '\n'));
+ assert (p != 0);
+ size_t header_size (p - s);
+ p++;
+
+ // SET
+ //
+ bool empty (true); // Empty SET case.
+ const char* set_begin (0);
+ if (e - p > 4 && traits::compare (p, "SET\n", 4) == 0)
+ {
+ p += 4;
+ set_begin = p;
+
+ // Scan the SET list.
+ //
+ size_t bi (0);
+ for (const char* pe (comma_begin (p, e)); pe != 0; comma_next (p, pe, e))
+ {
+ if (empty)
+ {
+ // We cannot be empty if we have a non-parameterized set expression,
+ // e.g., UPDATE ... SET ver=ver+1 ... We also cannot be empty if
+ // this expression is present in the bind array.
+ //
+ if ((find (p, pe, param_symbol) == 0 &&
+ (param_symbol2 == '\0' || find (p, pe, param_symbol2) == 0)) ||
+ bind_at (bi++, bind, bind_skip) != 0)
+ empty = false;
+ }
+ }
+ }
+
+ // Empty case.
+ //
+ if (empty)
+ {
+ r.clear ();
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (n != 0)
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+
+ return;
+ }
+
+ // Trailer.
+ //
+ const char* trailer_begin (0);
+ size_t trailer_size (0);
+ if (e - p != 0)
+ {
+ trailer_begin = p;
+ trailer_size = e - p;
+ }
+
+ // Assume the same size as the original. It can only shrink, and in
+ // most cases only slightly. So this is a good approximation.
+ //
+ r.reserve (n);
+ r.assign (s, header_size);
+
+ // SET list.
+ //
+ {
+ r += " SET ";
+
+ size_t i (0), bi (0);
+
+ for (const char* p (set_begin), *pe (comma_begin (p, e));
+ pe != 0; comma_next (p, pe, e))
+ {
+ // See if the value contains the parameter symbol and, if so,
+ // whether it is present in the bind array.
+ //
+ if ((find (p, pe, param_symbol) != 0 ||
+ (param_symbol2 != '\0' && find (p, pe, param_symbol2) != 0)) &&
+ bind_at (bi++, bind, bind_skip) == 0)
+ continue;
+
+ // Append the expression.
+ //
+ if (i++ != 0)
+ r += ", "; // Add the space for consistency with the fast path.
+
+ r.append (p, pe - p);
+ }
+ }
+
+ // Trailer.
+ //
+ if (trailer_size != 0)
+ {
+ r += ' ';
+ r.append (trailer_begin, trailer_size);
+ }
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (r.size () != n)
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+ }
+
+ void statement::
+ process_select (string& r,
+ const char* s,
+ bind_type bind,
+ size_t bind_size,
+ size_t bind_skip,
+ char quote_open,
+ char quote_close,
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ bool optimize,
+#else
+ bool,
+#endif
+ bool as)
+ {
+ bool empty (true); // Empty case (if none present).
+ bool fast (true); // Fast case (if all present).
+ for (size_t i (0); i != bind_size && (empty || fast); ++i)
+ {
+ if (bind_at (i, bind, bind_skip) != 0)
+ empty = false;
+ else
+ fast = false;
+ }
+
+ // Empty.
+ //
+ if (empty)
+ {
+ r.clear ();
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (*s != '\0')
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+ return;
+ }
+
+ // Fast path: just remove the "structure".
+ //
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ if (fast && !optimize)
+ {
+ process_fast (s, r);
+ return;
+ }
+#endif
+
+ // Scan the statement and store the positions of various parts.
+ //
+ size_t n (traits::length (s));
+ const char* e (s + n);
+
+ // Header.
+ //
+ const char* p (find (s, e, '\n'));
+ assert (p != 0);
+ size_t header_size (p - s);
+ p++;
+
+ // Column list.
+ //
+ const char* columns_begin (p);
+ for (const char* ce (comma_begin (p, e)); ce != 0; comma_next (p, ce, e))
+ ;
+
+ // FROM.
+ assert (traits::compare (p, "FROM ", 5) == 0);
+ const char* from_begin (p);
+ p = find (p, e, '\n'); // May not end with '\n'.
+ if (p == 0)
+ p = e;
+ size_t from_size (p - from_begin);
+ if (p != e)
+ p++;
+
+ // JOIN list.
+ //
+ const char* joins_begin (0), *joins_end (0);
+ if (e - p > 5 && fuzzy_prefix (p, e, "JOIN ", 5))
+ {
+ joins_begin = p;
+
+ // Find the end of the JOIN list.
+ //
+ for (const char* je (newline_begin (p, e));
+ je != 0; newline_next (p, je, e, "JOIN ", 5, true))
+ ;
+
+ joins_end = (p != e ? p - 1 : p);
+ }
+
+#ifndef LIBODB_DEBUG_STATEMENT_PROCESSING
+ if (fast && joins_begin == 0)
+ {
+ // No JOINs to optimize so can still take the fast path.
+ //
+ process_fast (s, r);
+ return;
+ }
+#endif
+
+ // Trailer (WHERE, ORDER BY, etc).
+ //
+ const char* trailer_begin (0);
+ size_t trailer_size (0);
+ if (e - p != 0)
+ {
+ trailer_begin = p;
+ trailer_size = e - p;
+ }
+
+ // Assume the same size as the original. It can only shrink, and in
+ // most cases only slightly. So this is a good approximation.
+ //
+ r.reserve (n);
+ r.assign (s, header_size);
+
+ // Column list.
+ //
+ {
+ r += ' ';
+
+ size_t i (0), bi (0);
+
+ for (const char *c (columns_begin), *ce (comma_begin (c, e));
+ ce != 0; comma_next (c, ce, e))
+ {
+ // See if the column is present in the bind array.
+ //
+ if (bind_at (bi++, bind, bind_skip) == 0)
+ continue;
+
+ // Append the column.
+ //
+ if (i++ != 0)
+ r += ", "; // Add the space for consistency with the fast path.
+
+ r.append (c, ce - c);
+ }
+ }
+
+ // From.
+ //
+ r += ' ';
+ r.append (from_begin, from_size);
+
+ // JOIN list, pass 1.
+ //
+ size_t join_pos (0);
+ if (joins_begin != 0)
+ {
+ // Fill in the JOIN "area" with spaces.
+ //
+ r.resize (r.size () + joins_end - joins_begin + 1, ' ');
+ join_pos = r.size () + 1; // End of the last JOIN.
+ }
+
+ // Trailer.
+ //
+ if (trailer_size != 0)
+ {
+ r += ' ';
+ r.append (trailer_begin, trailer_size);
+ }
+
+ // JOIN list, pass 2.
+ //
+ if (joins_begin != 0)
+ {
+ // Splice the JOINs into the pre-allocated area.
+ //
+ for (const char* je (joins_end), *j (newline_rbegin (je, joins_begin));
+ j != 0; newline_rnext (j, je, joins_begin))
+ {
+ size_t n (je - j);
+
+ // Get the alias or, if none used, the table name.
+ //
+ p = find (j, je, "JOIN ", 5) + 5; // Skip past "JOIN ".
+ const char* table_begin (p);
+ p = find (p, je, ' '); // End of the table name.
+ const char* table_end (p);
+ p++; // Skip space.
+
+ // We may or may not have the AS keyword.
+ //
+ const char* alias_begin (0);
+ size_t alias_size (0);
+
+ if (p != je && // Not the end.
+ (je - p < 4 || traits::compare (p, "ON ", 3) != 0))
+ {
+ // Something other than "ON ", so got to be an alias.
+ //
+ if (as)
+ p += 3;
+
+ alias_begin = p;
+ p = find (p, je, ' '); // There might be no ON (CROSS JOIN).
+ alias_size = (p != 0 ? p : je) - alias_begin;
+ }
+ else
+ {
+ // Just the table.
+ //
+ alias_begin = table_begin;
+ alias_size = table_end - alias_begin;
+ }
+
+ // The alias must be quoted.
+ //
+ assert (*alias_begin == quote_open &&
+ *(alias_begin + alias_size - 1) == quote_close);
+
+ // We now need to see if the alias is used in either the SELECT
+ // list, the WHERE conditions, or the ON condition of any of the
+ // JOINs that we have already processed and decided to keep.
+ //
+ // Instead of re-parsing the whole thing again, we are going to
+ // take a shortcut and simply search for the alias in the statement
+ // we have constructed so far (that's why we have added the
+ // trailer before filling in the JOINs). To make it more robust,
+ // we are going to do a few extra sanity checks, specifically,
+ // that the alias is a top level identifier and is followed by
+ // only a single identifer (column). This will catch cases like
+ // [s].[t].[c] where [s] is also used as an alias or LEFT JOIN [t]
+ // where [t] is also used as an alias in another JOIN.
+ //
+ bool found (false);
+ for (size_t p (r.find (alias_begin, 0, alias_size));
+ p != string::npos;
+ p = r.find (alias_begin, p + alias_size, alias_size))
+ {
+ size_t e (p + alias_size);
+
+ // If we are not a top-level qualifier or not a bottom-level,
+ // then we are done (3 is for at least "[a]").
+ //
+ if ((p != 0 && r[p - 1] == '.') ||
+ (e + 3 >= r.size () || (r[e] != '.' || r[e + 1] != quote_open)))
+ continue;
+
+ // The only way to distinguish the [a].[c] from FROM [a].[c] or
+ // JOIN [a].[c] is by checking the prefix.
+ //
+ if ((p > 5 && r.compare (p - 5, 5, "FROM ") == 0) ||
+ (p > 5 && r.compare (p - 5, 5, "JOIN ") == 0))
+ continue;
+
+ // Check that we are followed by a single identifier.
+ //
+ e = r.find (quote_close, e + 2);
+ if (e == string::npos || (e + 1 != r.size () && r[e + 1] == '.'))
+ continue;
+
+ found = true;
+ break;
+ }
+
+ join_pos -= n + 1; // Extra one for space.
+ if (found)
+ r.replace (join_pos, n, j, n);
+ else
+ r.erase (join_pos - 1, n + 1); // Extra one for space.
+ }
+ }
+
+#ifdef LIBODB_TRACE_STATEMENT_PROCESSING
+ if (r.size () != n)
+ cerr << endl
+ << "old: '" << s << "'" << endl << endl
+ << "new: '" << r << "'" << endl << endl;
+#endif
+ }
+}
diff --git a/libodb/odb/statement.cxx b/libodb/odb/statement.cxx
new file mode 100644
index 0000000..35e7b77
--- /dev/null
+++ b/libodb/odb/statement.cxx
@@ -0,0 +1,12 @@
+// file : odb/statement.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/statement.hxx>
+
+namespace odb
+{
+ statement::
+ ~statement ()
+ {
+ }
+}
diff --git a/libodb/odb/statement.hxx b/libodb/odb/statement.hxx
new file mode 100644
index 0000000..83513c0
--- /dev/null
+++ b/libodb/odb/statement.hxx
@@ -0,0 +1,108 @@
+// file : odb/statement.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STATEMENT_HXX
+#define ODB_STATEMENT_HXX
+
+#include <odb/pre.hxx>
+
+#include <string>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx> // connection
+
+#include <odb/details/export.hxx>
+#include <odb/details/shared-ptr.hxx>
+
+namespace odb
+{
+ class LIBODB_EXPORT statement: public details::shared_base
+ {
+ private:
+ statement (const statement&);
+ statement& operator= (const statement&);
+
+ public:
+ typedef odb::connection connection_type;
+
+ virtual const char*
+ text () const = 0;
+
+ virtual connection_type&
+ connection () = 0;
+
+ virtual
+ ~statement () = 0;
+
+ protected:
+ statement () {}
+
+ // Statement processing. Kept public only for testing.
+ //
+ public:
+ // Expected statement structure:
+ //
+ // INSERT INTO table\n
+ // [(a,\n
+ // b)\n]
+ // [OUTPUT ...\n]
+ // [VALUES\n
+ // ($1,\n
+ // $2)[\n]]
+ // [DEFAULT VALUES[\n]]
+ // [RETURNING ...]
+ // [; SELECT ...]
+ //
+ static void
+ process_insert (std::string& result,
+ const char* statement,
+ const void* const* bind, // Array of bind buffer pointers.
+ std::size_t bind_size, // Number of bind elements.
+ std::size_t bind_skip, // Offset to the next bind.
+ char param_symbol, // $, ?, :, etc.
+ char param_symbol2 = '\0');
+
+ // Expected statement structure:
+ //
+ // UPDATE table\n
+ // SET\n
+ // a=$1,\n
+ // b=$2[\n]
+ // [OUTPUT ...]
+ // [WHERE ...]
+ //
+ static void
+ process_update (std::string& result,
+ const char* statement,
+ const void* const* bind, // Array of bind buffer pointers.
+ std::size_t bind_size, // Number of bind elements.
+ std::size_t bind_skip, // Offset to the next bind.
+ char param_symbol, // $, ?, :, etc.
+ char param_symbol2 = '\0');
+
+
+ // Expected statement structure:
+ //
+ // SELECT\n
+ // [schema.]table.a [AS b],\n
+ // alias.b\n
+ // FROM [schema.]table[\n]
+ // [{A-Z }* JOIN [schema.]table [AS alias][ ON ...][\n]]*
+ // [WHERE ...]
+ //
+ static void
+ process_select (std::string& result,
+ const char* statement,
+ const void* const* bind, // Array of bind buffer pointers.
+ std::size_t bind_size, // Number of bind elements.
+ std::size_t bind_skip, // Offset to the next bind.
+ char quote_open, // Identifier opening quote.
+ char quote_close, // Identifier closing quote.
+ bool optimize, // Remove unused JOINs.
+ bool as = true); // JOINs use AS keyword.
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STATEMENT_HXX
diff --git a/libodb/odb/std-array-traits.hxx b/libodb/odb/std-array-traits.hxx
new file mode 100644
index 0000000..1d0f1a1
--- /dev/null
+++ b/libodb/odb/std-array-traits.hxx
@@ -0,0 +1,72 @@
+// file : odb/std-array-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_ARRAY_TRAITS_HXX
+#define ODB_STD_ARRAY_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <array>
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename V, std::size_t N>
+ class access::container_traits<std::array<V, N>>
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef std::array<V, N> container_type;
+
+ typedef V value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (index_type i (0); i < N; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ index_type i (0);
+
+ for (; more && i < N; ++i)
+ {
+ index_type dummy;
+ more = f.select (dummy, c[i]);
+ }
+
+ assert (!more && i == N);
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (index_type i (0); i < N; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_ARRAY_TRAITS_HXX
diff --git a/libodb/odb/std-deque-traits.hxx b/libodb/odb/std-deque-traits.hxx
new file mode 100644
index 0000000..351c6db
--- /dev/null
+++ b/libodb/odb/std-deque-traits.hxx
@@ -0,0 +1,69 @@
+// file : odb/std-deque-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_DEQUE_TRAITS_HXX
+#define ODB_STD_DEQUE_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <deque>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename V, typename A>
+ class access::container_traits<std::deque<V, A> >
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef std::deque<V, A> container_type;
+
+ typedef V value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ index_type dummy;
+ c.push_back (value_type ());
+ more = f.select (dummy, c.back ());
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_DEQUE_TRAITS_HXX
diff --git a/libodb/odb/std-forward-list-traits.hxx b/libodb/odb/std-forward-list-traits.hxx
new file mode 100644
index 0000000..7978b1c
--- /dev/null
+++ b/libodb/odb/std-forward-list-traits.hxx
@@ -0,0 +1,73 @@
+// file : odb/std-forward-list-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_FORWARD_LIST_TRAITS_HXX
+#define ODB_STD_FORWARD_LIST_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <forward_list>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename V, typename A>
+ class access::container_traits<std::forward_list<V, A>>
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef std::forward_list<V, A> container_type;
+
+ typedef V value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ index_type i (0);
+ for (typename container_type::const_iterator j (c.begin ()),
+ e (c.end ()); j != e; ++j)
+ f.insert (i++, *j);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ for (typename container_type::iterator i (c.before_begin ()); more; )
+ {
+ index_type dummy;
+ i = c.insert_after (i, value_type ());
+ more = f.select (dummy, *i);
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ index_type i (0);
+ for (typename container_type::const_iterator j (c.begin ()),
+ e (c.end ()); j != e; ++j)
+ f.insert (i++, *j);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_FORWARD_LIST_TRAITS_HXX
diff --git a/libodb/odb/std-list-traits.hxx b/libodb/odb/std-list-traits.hxx
new file mode 100644
index 0000000..f349079
--- /dev/null
+++ b/libodb/odb/std-list-traits.hxx
@@ -0,0 +1,73 @@
+// file : odb/std-list-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_LIST_TRAITS_HXX
+#define ODB_STD_LIST_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <list>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename V, typename A>
+ class access::container_traits<std::list<V, A> >
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef std::list<V, A> container_type;
+
+ typedef V value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ index_type i (0);
+ for (typename container_type::const_iterator j (c.begin ()),
+ e (c.end ()); j != e; ++j)
+ f.insert (i++, *j);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ index_type dummy;
+ c.push_back (value_type ());
+ more = f.select (dummy, c.back ());
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ index_type i (0);
+ for (typename container_type::const_iterator j (c.begin ()),
+ e (c.end ()); j != e; ++j)
+ f.insert (i++, *j);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_LIST_TRAITS_HXX
diff --git a/libodb/odb/std-map-traits.hxx b/libodb/odb/std-map-traits.hxx
new file mode 100644
index 0000000..2b9bf7d
--- /dev/null
+++ b/libodb/odb/std-map-traits.hxx
@@ -0,0 +1,142 @@
+// file : odb/std-map-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_MAP_TRAITS_HXX
+#define ODB_STD_MAP_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <map>
+#include <utility> // std::move
+
+#include <odb/container-traits.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ template <typename K, typename V, typename C, typename A>
+ class access::container_traits<std::map<K, V, C, A> >
+ {
+ public:
+ static const container_kind kind = ck_map;
+ static const bool smart = false;
+
+ typedef std::map<K, V, C, A> container_type;
+
+ typedef K key_type;
+ typedef V value_type;
+ typedef typename container_type::value_type pair_type;
+
+ typedef map_functions<key_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ key_type k;
+ value_type v;
+ more = f.select (k, v);
+#ifdef ODB_CXX11
+ c.insert (pair_type (std::move (k), std::move (v)));
+#else
+ c.insert (pair_type (k, v));
+#endif
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // C++03 does not guarantee insertion order of equal values but C++11
+ // changes that. The current implementation in the generated code does
+ // not guarantee this either.
+ //
+ template <typename K, typename V, typename C, typename A>
+ class access::container_traits<std::multimap<K, V, C, A> >
+ {
+ public:
+ static const container_kind kind = ck_multimap;
+ static const bool smart = false;
+
+ typedef std::multimap<K, V, C, A> container_type;
+
+ typedef K key_type;
+ typedef V value_type;
+ typedef typename container_type::value_type pair_type;
+
+ typedef map_functions<key_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ key_type k;
+ value_type v;
+ more = f.select (k, v);
+#ifdef ODB_CXX11
+ c.insert (pair_type (std::move (k), std::move (v)));
+#else
+ c.insert (pair_type (k, v));
+#endif
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_MAP_TRAITS_HXX
diff --git a/libodb/odb/std-set-traits.hxx b/libodb/odb/std-set-traits.hxx
new file mode 100644
index 0000000..45a5dcc
--- /dev/null
+++ b/libodb/odb/std-set-traits.hxx
@@ -0,0 +1,134 @@
+// file : odb/std-set-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_SET_TRAITS_HXX
+#define ODB_STD_SET_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <set>
+#include <utility> // std::move
+
+#include <odb/container-traits.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ template <typename V, typename C, typename A>
+ class access::container_traits<std::set<V, C, A> >
+ {
+ public:
+ static const container_kind kind = ck_set;
+ static const bool smart = false;
+
+ typedef std::set<V, C, A> container_type;
+ typedef V value_type;
+
+ typedef set_functions<value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ value_type v;
+ more = f.select (v);
+#ifdef ODB_CXX11
+ c.insert (std::move (v));
+#else
+ c.insert (v);
+#endif
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // C++03 does not guarantee insertion order of equal values but C++11
+ // changes that. The current implementation in the generated code does
+ // not guarantee this either.
+ //
+ template <typename V, typename C, typename A>
+ class access::container_traits<std::multiset<V, C, A> >
+ {
+ public:
+ static const container_kind kind = ck_multiset;
+ static const bool smart = false;
+
+ typedef std::multiset<V, C, A> container_type;
+ typedef V value_type;
+
+ typedef set_functions<value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ value_type v;
+ more = f.select (v);
+#ifdef ODB_CXX11
+ c.insert (std::move (v));
+#else
+ c.insert (v);
+#endif
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_SET_TRAITS_HXX
diff --git a/libodb/odb/std-unordered-map-traits.hxx b/libodb/odb/std-unordered-map-traits.hxx
new file mode 100644
index 0000000..461eb06
--- /dev/null
+++ b/libodb/odb/std-unordered-map-traits.hxx
@@ -0,0 +1,133 @@
+// file : odb/std-unordered-map-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_UNORDERED_MAP_TRAITS_HXX
+#define ODB_STD_UNORDERED_MAP_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <utility> // std::move
+#include <unordered_map>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename K, typename V, typename H, typename P, typename A>
+ class access::container_traits<std::unordered_map<K, V, H, P, A>>
+ {
+ public:
+ static const container_kind kind = ck_map;
+ static const bool smart = false;
+
+ typedef std::unordered_map<K, V, H, P, A> container_type;
+
+ typedef K key_type;
+ typedef V value_type;
+ typedef typename container_type::value_type pair_type;
+
+ typedef map_functions<key_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ key_type k;
+ value_type v;
+ more = f.select (k, v);
+ c.insert (pair_type (std::move (k), std::move (v)));
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // @@ Does multimap preserve insertion order of equal elements? The
+ // current implementation in the generated code does not guarantee
+ // this.
+ //
+ template <typename K, typename V, typename H, typename P, typename A>
+ class access::container_traits<std::unordered_multimap<K, V, H, P, A>>
+ {
+ public:
+ static const container_kind kind = ck_multimap;
+ static const bool smart = false;
+
+ typedef std::unordered_multimap<K, V, H, P, A> container_type;
+
+ typedef K key_type;
+ typedef V value_type;
+ typedef typename container_type::value_type pair_type;
+
+ typedef map_functions<key_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ key_type k;
+ value_type v;
+ more = f.select (k, v);
+ c.insert (pair_type (std::move (k), std::move (v)));
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (i->first, i->second);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_UNORDERED_MAP_TRAITS_HXX
diff --git a/libodb/odb/std-unordered-set-traits.hxx b/libodb/odb/std-unordered-set-traits.hxx
new file mode 100644
index 0000000..f590665
--- /dev/null
+++ b/libodb/odb/std-unordered-set-traits.hxx
@@ -0,0 +1,125 @@
+// file : odb/std-unordered-set-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_UNORDERED_SET_TRAITS_HXX
+#define ODB_STD_UNORDERED_SET_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <utility> // std::move
+#include <unordered_set>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename V, typename H, typename P, typename A>
+ class access::container_traits<std::unordered_set<V, H, P, A>>
+ {
+ public:
+ static const container_kind kind = ck_set;
+ static const bool smart = false;
+
+ typedef std::unordered_set<V, H, P, A> container_type;
+ typedef V value_type;
+
+ typedef set_functions<value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ value_type v;
+ more = f.select (v);
+ c.insert (std::move (v));
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // @@ Does multiset preserve insertion order of equal elements? The
+ // current implementation in the generated code does not guarantee
+ // this.
+ //
+ template <typename V, typename H, typename P, typename A>
+ class access::container_traits<std::unordered_multiset<V, H, P, A>>
+ {
+ public:
+ static const container_kind kind = ck_multiset;
+ static const bool smart = false;
+
+ typedef std::unordered_multiset<V, H, P, A> container_type;
+ typedef V value_type;
+
+ typedef set_functions<value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ value_type v;
+ more = f.select (v);
+ c.insert (std::move (v));
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (typename container_type::const_iterator i (c.begin ()),
+ e (c.end ()); i != e; ++i)
+ f.insert (*i);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_UNORDERED_SET_TRAITS_HXX
diff --git a/libodb/odb/std-vector-traits.hxx b/libodb/odb/std-vector-traits.hxx
new file mode 100644
index 0000000..c6bb39e
--- /dev/null
+++ b/libodb/odb/std-vector-traits.hxx
@@ -0,0 +1,123 @@
+// file : odb/std-vector-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_STD_VECTOR_TRAITS_HXX
+#define ODB_STD_VECTOR_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+
+#include <odb/container-traits.hxx>
+
+namespace odb
+{
+ template <typename V, typename A>
+ class access::container_traits<std::vector<V, A> >
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef std::vector<V, A> container_type;
+
+ typedef V value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ index_type dummy;
+ c.push_back (value_type ());
+ more = f.select (dummy, c.back ());
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+
+ // std::vector<bool> is special.
+ //
+ template <typename A>
+ class access::container_traits<std::vector<bool, A> >
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = false;
+
+ typedef std::vector<bool, A> container_type;
+
+ typedef bool value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef ordered_functions<index_type, value_type> functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ index_type dummy;
+ value_type value;
+ more = f.select (dummy, value);
+ c.push_back (value);
+ }
+ }
+
+ static void
+ update (const container_type& c, const functions& f)
+ {
+ f.delete_ ();
+
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+ }
+
+ static void
+ erase (const functions& f)
+ {
+ f.delete_ ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_STD_VECTOR_TRAITS_HXX
diff --git a/libodb/odb/tr1/lazy-pointer-traits.hxx b/libodb/odb/tr1/lazy-pointer-traits.hxx
new file mode 100644
index 0000000..7adf957
--- /dev/null
+++ b/libodb/odb/tr1/lazy-pointer-traits.hxx
@@ -0,0 +1,61 @@
+// file : odb/tr1/lazy-pointer-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TR1_LAZY_POINTER_TRAITS_HXX
+#define ODB_TR1_LAZY_POINTER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/pointer-traits.hxx>
+#include <odb/tr1/lazy-ptr.hxx>
+
+namespace odb
+{
+ template <typename T>
+ class pointer_traits<tr1::lazy_shared_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_shared;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef tr1::lazy_shared_ptr<element_type> pointer_type;
+ typedef std::tr1::shared_ptr<element_type> eager_pointer_type;
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ template <class O /* = T */>
+ static typename object_traits<O>::id_type
+ object_id (const pointer_type& p)
+ {
+ return p.template object_id<O> ();
+ }
+ };
+
+ template <typename T>
+ class pointer_traits<tr1::lazy_weak_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_weak;
+ static const bool lazy = true;
+
+ typedef T element_type;
+ typedef tr1::lazy_weak_ptr<element_type> pointer_type;
+ typedef tr1::lazy_shared_ptr<element_type> strong_pointer_type;
+ typedef std::tr1::weak_ptr<element_type> eager_pointer_type;
+
+ static strong_pointer_type
+ lock (const pointer_type& p)
+ {
+ return p.lock ();
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_TR1_LAZY_POINTER_TRAITS_HXX
diff --git a/libodb/odb/tr1/lazy-ptr.hxx b/libodb/odb/tr1/lazy-ptr.hxx
new file mode 100644
index 0000000..b4946ec
--- /dev/null
+++ b/libodb/odb/tr1/lazy-ptr.hxx
@@ -0,0 +1,267 @@
+// file : odb/tr1/lazy-ptr.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TR1_LAZY_PTR_HXX
+#define ODB_TR1_LAZY_PTR_HXX
+
+#include <odb/pre.hxx>
+
+//
+// This header assumes that the necessary TR1 header has already
+// been included.
+//
+
+#include <memory> // std::auto_ptr
+
+#include <odb/forward.hxx> // odb::database
+#include <odb/traits.hxx>
+#include <odb/lazy-ptr-impl.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ namespace tr1
+ {
+ template <class T>
+ class lazy_weak_ptr;
+
+ //
+ //
+ template <class T>
+ class lazy_shared_ptr
+ {
+ // The standard shared_ptr interface.
+ //
+ public:
+ typedef T element_type;
+
+ lazy_shared_ptr ();
+ template <class Y> explicit lazy_shared_ptr (Y*);
+ template <class Y, class D> lazy_shared_ptr (Y*, D);
+
+ lazy_shared_ptr (const lazy_shared_ptr&);
+ template <class Y> lazy_shared_ptr (const lazy_shared_ptr<Y>&);
+ template <class Y> explicit lazy_shared_ptr (const lazy_weak_ptr<Y>&);
+ template <class Y> explicit lazy_shared_ptr (std::auto_ptr<Y>&);
+
+ ~lazy_shared_ptr ();
+
+ lazy_shared_ptr& operator= (const lazy_shared_ptr&);
+ template <class Y> lazy_shared_ptr& operator= (const lazy_shared_ptr<Y>&);
+ template <class Y> lazy_shared_ptr& operator= (std::auto_ptr<Y>&);
+
+ void swap (lazy_shared_ptr&);
+ void reset ();
+ template <class Y> void reset (Y*);
+ template <class Y, class D> void reset (Y*, D);
+
+ T& operator* () const;
+ T* operator-> () const;
+ T* get () const;
+
+ bool unique () const;
+ long use_count () const;
+
+ typedef std::tr1::shared_ptr<T> lazy_shared_ptr::*unspecified_bool_type;
+ operator unspecified_bool_type () const
+ {
+ return (p_ || i_) ? &lazy_shared_ptr::p_ : 0;
+ }
+
+ // Initialization/assignment from shared_ptr and weak_ptr.
+ //
+ public:
+ template <class Y> lazy_shared_ptr (const std::tr1::shared_ptr<Y>&);
+ template <class Y> explicit lazy_shared_ptr (const std::tr1::weak_ptr<Y>&);
+
+ template <class Y> lazy_shared_ptr& operator= (const std::tr1::shared_ptr<Y>&);
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // NULL loaded()
+ //
+ // true true NULL pointer to transient object
+ // false true valid pointer to persistent object
+ // true false unloaded pointer to persistent object
+ // false false valid pointer to transient object
+ //
+ bool loaded () const;
+
+ std::tr1::shared_ptr<T> load () const;
+
+ // Unload the pointer. For transient objects this function is
+ // equivalent to reset().
+ //
+ void unload () const;
+
+ // Get the underlying eager pointer. If this is an unloaded pointer
+ // to a persistent object, then the returned pointer will be NULL.
+ //
+ std::tr1::shared_ptr<T> get_eager () const;
+
+ template <class DB, class ID> lazy_shared_ptr (DB&, const ID&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, Y*);
+ template <class DB, class Y, class D> lazy_shared_ptr (DB&, Y*, D);
+ template <class DB, class Y> lazy_shared_ptr (DB&, std::auto_ptr<Y>&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, const std::tr1::shared_ptr<Y>&);
+ template <class DB, class Y> lazy_shared_ptr (DB&, const std::tr1::weak_ptr<Y>&);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB, class Y> void reset (DB&, Y*);
+ template <class DB, class Y, class D> void reset (DB&, Y*, D);
+ template <class DB, class Y> void reset (DB&, std::auto_ptr<Y>&);
+ template <class DB, class Y> void reset (DB&, const std::tr1::shared_ptr<Y>&);
+
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /*= T*/>
+#endif
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ // Helpers.
+ //
+ public:
+ template <class Y> bool equal (const lazy_shared_ptr<Y>&) const;
+
+ private:
+ template <class Y> friend class lazy_shared_ptr;
+ template <class Y> friend class lazy_weak_ptr;
+
+ // For lazy_weak_ptr::lock().
+ //
+ lazy_shared_ptr (const std::tr1::shared_ptr<T>& p,
+ const lazy_ptr_impl<T>& i)
+ : p_ (p), i_ (i) {}
+
+ private:
+ mutable std::tr1::shared_ptr<T> p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+
+ // operator< and operator<< are not provided.
+ //
+ template<class T, class Y>
+ bool operator== (const lazy_shared_ptr<T>&, const lazy_shared_ptr<Y>&);
+
+ template<class T, class Y>
+ bool operator!= (const lazy_shared_ptr<T>&, const lazy_shared_ptr<Y>&);
+
+ template<class T> void swap (lazy_shared_ptr<T>&, lazy_shared_ptr<T>&);
+
+ template<class D, class T>
+ D* get_deleter (const lazy_shared_ptr<T>&);
+
+ //
+ //
+ template <class T>
+ class lazy_weak_ptr
+ {
+ // The standard weak_ptr interface.
+ //
+ public:
+ typedef T element_type;
+
+ lazy_weak_ptr ();
+ template <class Y> lazy_weak_ptr (const lazy_shared_ptr<Y>&);
+ lazy_weak_ptr (const lazy_weak_ptr&);
+ template <class Y> lazy_weak_ptr (const lazy_weak_ptr<Y>&);
+
+ ~lazy_weak_ptr ();
+
+ lazy_weak_ptr& operator= (const lazy_weak_ptr&);
+ template <class Y> lazy_weak_ptr& operator= (const lazy_weak_ptr<Y>&);
+ template <class Y> lazy_weak_ptr& operator= (const lazy_shared_ptr<Y>&);
+
+ void swap (lazy_weak_ptr<T>&);
+ void reset ();
+
+ long use_count () const;
+ bool expired () const;
+
+ lazy_shared_ptr<T> lock () const;
+
+ // Initialization/assignment from shared_ptr and weak_ptr.
+ //
+ public:
+ template <class Y> lazy_weak_ptr (const std::tr1::weak_ptr<Y>&);
+ template <class Y> lazy_weak_ptr (const std::tr1::shared_ptr<Y>&);
+
+ template <class Y> lazy_weak_ptr& operator= (const std::tr1::weak_ptr<Y>&);
+ template <class Y> lazy_weak_ptr& operator= (const std::tr1::shared_ptr<Y>&);
+
+ // Lazy loading interface.
+ //
+ public:
+ typedef odb::database database_type;
+
+ // expired() loaded()
+ //
+ // true true expired pointer to transient object
+ // false true valid pointer to persistent object
+ // true false expired pointer to persistent object
+ // false false valid pointer to transient object
+ //
+ bool loaded () const;
+
+ // Performs both lock and load.
+ //
+ std::tr1::shared_ptr<T> load () const;
+
+ // Unload the pointer. For transient objects this function is
+ // equivalent to reset().
+ //
+ void unload () const;
+
+ // Get the underlying eager pointer. If this is an unloaded pointer
+ // to a persistent object, then the returned pointer will be NULL.
+ //
+ std::tr1::weak_ptr<T> get_eager () const;
+
+ template <class DB, class ID> lazy_weak_ptr (DB&, const ID&);
+ template <class DB, class Y> lazy_weak_ptr (DB&, const std::tr1::shared_ptr<Y>&);
+ template <class DB, class Y> lazy_weak_ptr (DB&, const std::tr1::weak_ptr<Y>&);
+
+ template <class DB, class ID> void reset (DB&, const ID&);
+ template <class DB, class Y> void reset (DB&, const std::tr1::shared_ptr<Y>&);
+ template <class DB, class Y> void reset (DB&, const std::tr1::weak_ptr<Y>&);
+
+ // The object_id() function can only be called when the object is
+ // persistent, or: expired() XOR loaded() (can use != for XOR).
+ //
+#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT
+ template <class O = T>
+#else
+ template <class O /* = T */>
+#endif
+ typename object_traits<O>::id_type object_id () const;
+
+ database_type& database () const;
+
+ private:
+ template <class Y> friend class lazy_shared_ptr;
+ template <class Y> friend class lazy_weak_ptr;
+
+ mutable std::tr1::weak_ptr<T> p_;
+ mutable lazy_ptr_impl<T> i_;
+ };
+
+ // operator< is not provided.
+ //
+ template<class T> void swap (lazy_weak_ptr<T>&, lazy_weak_ptr<T>&);
+ }
+}
+
+#include <odb/tr1/lazy-ptr.ixx>
+#include <odb/tr1/lazy-ptr.txx>
+
+#include <odb/tr1/lazy-pointer-traits.hxx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_TR1_LAZY_PTR_HXX
diff --git a/libodb/odb/tr1/lazy-ptr.ixx b/libodb/odb/tr1/lazy-ptr.ixx
new file mode 100644
index 0000000..dc5000f
--- /dev/null
+++ b/libodb/odb/tr1/lazy-ptr.ixx
@@ -0,0 +1,638 @@
+// file : odb/tr1/lazy-ptr.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace tr1
+ {
+ //
+ // lazy_shared_ptr
+ //
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr () {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (Y* p): p_ (p) {}
+
+ template <class T>
+ template <class Y, class D>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (Y* p, D d): p_ (p, d) {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_shared_ptr& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_shared_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const lazy_weak_ptr<Y>& r): i_ (r.i_)
+ {
+ // If the pointer has expired but can be re-loaded, then don't throw.
+ //
+ p_ = r.lock ();
+
+ if (!p_ && !i_)
+ throw std::tr1::bad_weak_ptr ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (std::auto_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>::
+ ~lazy_shared_ptr () {}
+
+ template <class T>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (const lazy_shared_ptr& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (const lazy_shared_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (std::auto_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline void lazy_shared_ptr<T>::
+ swap (lazy_shared_ptr& b)
+ {
+ p_.swap (b.p_);
+ i_.swap (b.i_);
+ }
+
+ template <class T>
+ inline void lazy_shared_ptr<T>::
+ reset ()
+ {
+ p_.reset ();
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (Y* p)
+ {
+ p_.reset (p);
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class Y, class D>
+ inline void lazy_shared_ptr<T>::
+ reset (Y* p, D d)
+ {
+ p_.reset (p, d);
+ i_.reset ();
+ }
+
+ template <class T>
+ inline T& lazy_shared_ptr<T>::
+ operator* () const
+ {
+ return *p_;
+ }
+
+ template <class T>
+ inline T* lazy_shared_ptr<T>::
+ operator-> () const
+ {
+ return p_.operator-> ();
+ }
+
+ template <class T>
+ inline T* lazy_shared_ptr<T>::
+ get () const
+ {
+ return p_.get ();
+ }
+
+ template <class T>
+ inline bool lazy_shared_ptr<T>::
+ unique () const
+ {
+ return p_.unique ();
+ }
+
+ template <class T>
+ inline long lazy_shared_ptr<T>::
+ use_count () const
+ {
+ return p_.use_count ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const std::tr1::shared_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (const std::tr1::weak_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_shared_ptr<T>& lazy_shared_ptr<T>::
+ operator= (const std::tr1::shared_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline bool lazy_shared_ptr<T>::
+ loaded () const
+ {
+ bool i (i_);
+ return !p_ != i; // !p_ XOR i_
+ }
+
+ template <class T>
+ inline std::tr1::shared_ptr<T> lazy_shared_ptr<T>::
+ load () const
+ {
+ if (!p_ && i_)
+ p_ = i_.template load<T> (true); // Reset id.
+
+ return p_;
+ }
+
+ template <class T>
+ inline void lazy_shared_ptr<T>::
+ unload () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (p_)
+ {
+ if (i_.database () != 0)
+ i_.reset_id (object_traits<object_type>::id (*p_));
+
+ p_.reset ();
+ }
+ }
+
+ template <class T>
+ inline std::tr1::shared_ptr<T> lazy_shared_ptr<T>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, const ID& id): i_ (db, id) {}
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, Y* p)
+ : p_ (p)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y, class D>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, Y* p, D d)
+ : p_ (p, d)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, std::auto_ptr<Y>& r)
+ : p_ (r)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, const std::tr1::shared_ptr<Y>& r)
+ : p_ (r)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_shared_ptr<T>::
+ lazy_shared_ptr (DB& db, const std::tr1::weak_ptr<Y>& r)
+ : p_ (r)
+ {
+ if (p_)
+ i_.reset_db (db);
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, const ID& id)
+ {
+ p_.reset ();
+ i_.reset (db, id);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, Y* p)
+ {
+ p_.reset (p);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y, class D>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, Y* p, D d)
+ {
+ p_.reset (p, d);
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, std::auto_ptr<Y>& r)
+ {
+ p_ = r;
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_shared_ptr<T>::
+ reset (DB& db, const std::tr1::shared_ptr<Y>& r)
+ {
+ p_ = r;
+
+ if (p_)
+ i_.reset_db (db);
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_shared_ptr<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ return p_
+ ? object_traits<object_type>::id (*p_)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T>
+ inline typename lazy_shared_ptr<T>::database_type& lazy_shared_ptr<T>::
+ database () const
+ {
+ return *i_.database ();
+ }
+
+ template<class T, class Y>
+ inline bool
+ operator== (const lazy_shared_ptr<T>& a, const lazy_shared_ptr<Y>& b)
+ {
+ return a.equal (b);
+ }
+
+ template<class T, class Y>
+ inline bool
+ operator!= (const lazy_shared_ptr<T>& a, const lazy_shared_ptr<Y>& b)
+ {
+ return !a.equal (b);
+ }
+
+ template<class T>
+ inline void
+ swap (lazy_shared_ptr<T>& a, lazy_shared_ptr<T>& b)
+ {
+ a.swap (b);
+ }
+
+ template<class D, class T>
+ inline D*
+ get_deleter (const lazy_shared_ptr<T>& p)
+ {
+ return std::tr1::get_deleter<D> (p.p_);
+ }
+
+
+ //
+ // lazy_weak_ptr
+ //
+
+ template <class T>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr () {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const lazy_shared_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const lazy_weak_ptr& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const lazy_weak_ptr<Y>& r): p_ (r.p_), i_ (r.i_) {}
+
+ template <class T>
+ inline lazy_weak_ptr<T>::
+ ~lazy_weak_ptr () {}
+
+ template <class T>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const lazy_weak_ptr& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const lazy_weak_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const lazy_shared_ptr<Y>& r)
+ {
+ p_ = r.p_;
+ i_ = r.i_;
+ return *this;
+ }
+
+ template <class T>
+ inline void lazy_weak_ptr<T>::
+ swap (lazy_weak_ptr<T>& r)
+ {
+ p_.swap (r.p_);
+ i_.swap (r.i_);
+ }
+
+ template <class T>
+ inline void lazy_weak_ptr<T>::
+ reset ()
+ {
+ p_.reset ();
+ i_.reset ();
+ }
+
+ template <class T>
+ inline long lazy_weak_ptr<T>::
+ use_count () const
+ {
+ return p_.use_count ();
+ }
+
+ template <class T>
+ inline bool lazy_weak_ptr<T>::
+ expired () const
+ {
+ return p_.expired ();
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const std::tr1::weak_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (const std::tr1::shared_ptr<Y>& r): p_ (r) {}
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const std::tr1::weak_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ template <class Y>
+ inline lazy_weak_ptr<T>& lazy_weak_ptr<T>::
+ operator= (const std::tr1::shared_ptr<Y>& r)
+ {
+ p_ = r;
+ i_.reset ();
+ return *this;
+ }
+
+ template <class T>
+ inline bool lazy_weak_ptr<T>::
+ loaded () const
+ {
+ bool i (i_);
+ return expired () != i; // expired () XOR i_
+ }
+
+ template <class T>
+ inline lazy_shared_ptr<T> lazy_weak_ptr<T>::
+ lock () const
+ {
+ return lazy_shared_ptr<T> (p_.lock (), i_);
+ }
+
+ template <class T>
+ inline std::tr1::shared_ptr<T> lazy_weak_ptr<T>::
+ load () const
+ {
+ std::tr1::shared_ptr<T> r (p_.lock ());
+
+ if (!r && i_)
+ {
+ r = i_.template load<T> (false); // Keep id.
+ p_ = r;
+ }
+
+ return r;
+ }
+
+ template <class T>
+ inline void lazy_weak_ptr<T>::
+ unload () const
+ {
+ // With weak pointer we always keep i_ up to date.
+ //
+ p_.reset ();
+ }
+
+ template <class T>
+ inline std::tr1::weak_ptr<T> lazy_weak_ptr<T>::
+ get_eager () const
+ {
+ return p_;
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (DB& db, const ID& id): i_ (db, id) {}
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (DB& db, const std::tr1::shared_ptr<Y>& r)
+ : p_ (r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ if (r)
+ i_.reset (db, object_traits<object_type>::id (*r));
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline lazy_weak_ptr<T>::
+ lazy_weak_ptr (DB& db, const std::tr1::weak_ptr<Y>& r)
+ : p_ (r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ std::tr1::shared_ptr<T> sp (p_.lock ());
+
+ if (sp)
+ i_.reset (db, object_traits<object_type>::id (*sp));
+ }
+
+ template <class T>
+ template <class DB, class ID>
+ inline void lazy_weak_ptr<T>::
+ reset (DB& db, const ID& id)
+ {
+ p_.reset ();
+ i_.reset (db, id);
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_weak_ptr<T>::
+ reset (DB& db, const std::tr1::shared_ptr<Y>& r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ p_ = r;
+
+ if (r)
+ i_.reset (db, object_traits<object_type>::id (*r));
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class DB, class Y>
+ inline void lazy_weak_ptr<T>::
+ reset (DB& db, const std::tr1::weak_ptr<Y>& r)
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ p_ = r;
+ std::tr1::shared_ptr<T> sp (p_.lock ());
+
+ if (sp)
+ i_.reset (db, object_traits<object_type>::id (*sp));
+ else
+ i_.reset ();
+ }
+
+ template <class T>
+ template <class O>
+ inline typename object_traits<O>::id_type lazy_weak_ptr<T>::
+ object_id () const
+ {
+ typedef typename object_traits<T>::object_type object_type;
+
+ std::tr1::shared_ptr<T> sp (p_.lock ());
+ return sp
+ ? object_traits<object_type>::id (*sp)
+ : i_.template object_id<O> ();
+ }
+
+ template <class T>
+ inline typename lazy_weak_ptr<T>::database_type& lazy_weak_ptr<T>::
+ database () const
+ {
+ return *i_.database ();
+ }
+
+ template<class T>
+ inline void
+ swap (lazy_weak_ptr<T>& a, lazy_weak_ptr<T>& b)
+ {
+ a.swap (b);
+ }
+ }
+}
diff --git a/libodb/odb/tr1/lazy-ptr.txx b/libodb/odb/tr1/lazy-ptr.txx
new file mode 100644
index 0000000..7e36cd9
--- /dev/null
+++ b/libodb/odb/tr1/lazy-ptr.txx
@@ -0,0 +1,43 @@
+// file : odb/tr1/lazy-ptr.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ namespace tr1
+ {
+ //
+ // lazy_shared_ptr
+ //
+
+ template <class T>
+ template <class Y>
+ bool lazy_shared_ptr<T>::
+ equal (const lazy_shared_ptr<Y>& r) const
+ {
+ bool t1 (!p_ == loaded ());
+ bool t2 (!r.p_ == r.loaded ());
+
+ // If both are transient, then compare the underlying pointers.
+ //
+ if (t1 && t2)
+ return p_ == r.p_;
+
+ // If one is transient and the other is persistent, then compare
+ // the underlying pointers but only if they are non NULL. Note
+ // that an unloaded persistent object is always unequal to a
+ // transient object.
+ //
+ if (t1 || t2)
+ return p_ == r.p_ && p_;
+
+ // If both objects are persistent, then we compare databases and
+ // object ids.
+ //
+ typedef typename object_traits<T>::object_type object_type1;
+ typedef typename object_traits<Y>::object_type object_type2;
+
+ return i_.database () == r.i_.database () &&
+ object_id<object_type1> () == r.template object_id<object_type2> ();
+ }
+ }
+}
diff --git a/libodb/odb/tr1/memory.hxx b/libodb/odb/tr1/memory.hxx
new file mode 100644
index 0000000..b9e9f45
--- /dev/null
+++ b/libodb/odb/tr1/memory.hxx
@@ -0,0 +1,37 @@
+// file : odb/tr1/memory.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TR1_MEMORY_HXX
+#define ODB_TR1_MEMORY_HXX
+
+//
+// Try to include TR1 <memory> in a compiler-specific manner. Fall back
+// on the Boost TR1 implementation if the compiler does not support TR1.
+//
+
+#include <cstddef> // __GLIBCXX__, _HAS_TR1
+
+// GNU C++ or Intel C++ using libstd++.
+//
+#if defined (__GNUC__) && __GNUC__ >= 4 && defined (__GLIBCXX__)
+# include <tr1/memory>
+//
+// IBM XL C++.
+//
+#elif defined (__xlC__) && __xlC__ >= 0x0900
+# define __IBMCPP_TR1__
+# include <memory>
+//
+// VC++ or Intel C++ using VC++ standard library.
+//
+#elif defined (_MSC_VER) && \
+ (_MSC_VER == 1500 && defined (_HAS_TR1) || _MSC_VER > 1500)
+# include <memory>
+//
+// Boost fall-back.
+//
+#else
+# include <boost/tr1/memory.hpp>
+#endif
+
+#endif // ODB_TR1_MEMORY_HXX
diff --git a/libodb/odb/tr1/pointer-traits.hxx b/libodb/odb/tr1/pointer-traits.hxx
new file mode 100644
index 0000000..df4f25f
--- /dev/null
+++ b/libodb/odb/tr1/pointer-traits.hxx
@@ -0,0 +1,121 @@
+// file : odb/tr1/pointer-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TR1_POINTER_TRAITS_HXX
+#define ODB_TR1_POINTER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+// In VC++ std::shared_ptr and std::tr1::shared_ptr is the same class
+// template. One is just a using-declaration for the other.
+//
+#if !(defined(ODB_CXX11) && defined(_MSC_VER))
+
+//
+// This header assumes that the necessary TR1 header has already
+// been included.
+//
+
+#include <odb/pointer-traits.hxx>
+#include <odb/details/meta/remove-const.hxx>
+
+namespace odb
+{
+ // Specialization for std::tr1::shared_ptr.
+ //
+ template <typename T>
+ class pointer_traits<std::tr1::shared_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_shared;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef std::tr1::shared_ptr<element_type> pointer_type;
+ typedef std::tr1::shared_ptr<const element_type> const_pointer_type;
+ typedef typename odb::details::meta::remove_const<element_type>::result
+ unrestricted_element_type;
+ typedef std::tr1::shared_ptr<unrestricted_element_type>
+ unrestricted_pointer_type;
+ typedef smart_ptr_guard<pointer_type> guard;
+
+ static element_type*
+ get_ptr (const pointer_type& p)
+ {
+ return p.get ();
+ }
+
+ static element_type&
+ get_ref (const pointer_type& p)
+ {
+ return *p;
+ }
+
+ static bool
+ null_ptr (const pointer_type& p)
+ {
+ return !p;
+ }
+
+ static unrestricted_pointer_type
+ const_pointer_cast (const pointer_type& p)
+ {
+ return std::tr1::const_pointer_cast<unrestricted_element_type> (p);
+ }
+
+ template <typename T1>
+ static std::tr1::shared_ptr<T1>
+ static_pointer_cast (const pointer_type& p)
+ {
+ return std::tr1::static_pointer_cast<T1> (p);
+ }
+
+ template <typename T1>
+ static std::tr1::shared_ptr<T1>
+ dynamic_pointer_cast (const pointer_type& p)
+ {
+ return std::tr1::dynamic_pointer_cast<T1> (p);
+ }
+
+ public:
+ static void*
+ allocate (std::size_t n)
+ {
+ return operator new (n);
+ }
+
+ static void
+ free (void* p)
+ {
+ operator delete (p);
+ }
+ };
+
+ // Specialization for std::tr1::weak_ptr.
+ //
+ template <typename T>
+ class pointer_traits<std::tr1::weak_ptr<T> >
+ {
+ public:
+ static const pointer_kind kind = pk_weak;
+ static const bool lazy = false;
+
+ typedef T element_type;
+ typedef std::tr1::weak_ptr<element_type> pointer_type;
+ typedef std::tr1::shared_ptr<element_type> strong_pointer_type;
+
+ static strong_pointer_type
+ lock (const pointer_type& p)
+ {
+ return p.lock ();
+ }
+ };
+}
+
+#endif // !(ODB_CXX11 && _MSC_VER)
+
+#include <odb/post.hxx>
+
+#endif // ODB_TR1_POINTER_TRAITS_HXX
diff --git a/libodb/odb/tr1/wrapper-traits.hxx b/libodb/odb/tr1/wrapper-traits.hxx
new file mode 100644
index 0000000..f878ef6
--- /dev/null
+++ b/libodb/odb/tr1/wrapper-traits.hxx
@@ -0,0 +1,76 @@
+// file : odb/tr1/wrapper-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TR1_WRAPPER_TRAITS_HXX
+#define ODB_TR1_WRAPPER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+// In VC++ std::shared_ptr and std::tr1::shared_ptr is the same class
+// template. One is just a using-declaration for the other.
+//
+#if !(defined(ODB_CXX11) && defined(_MSC_VER))
+
+//
+// This header assumes that the necessary TR1 header has already
+// been included.
+//
+
+#include <odb/wrapper-traits.hxx>
+
+namespace odb
+{
+ // Specialization for std::tr1::shared_ptr.
+ //
+ template <typename T>
+ class wrapper_traits< std::tr1::shared_ptr<T> >
+ {
+ public:
+ typedef T wrapped_type;
+ typedef std::tr1::shared_ptr<T> wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename odb::details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = false;
+
+ static bool
+ get_null (const wrapper_type& p)
+ {
+ return !p;
+ }
+
+ static void
+ set_null (wrapper_type& p)
+ {
+ p.reset ();
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& p)
+ {
+ return *p;
+ }
+
+ static unrestricted_wrapped_type&
+ set_ref (wrapper_type& p)
+ {
+ if (!p)
+ p.reset (new unrestricted_wrapped_type);
+
+ return const_cast<unrestricted_wrapped_type&> (*p);
+ }
+ };
+}
+
+#endif // !(ODB_CXX11 && _MSC_VER)
+
+#include <odb/post.hxx>
+
+#endif // ODB_TR1_WRAPPER_TRAITS_HXX
diff --git a/libodb/odb/tracer.cxx b/libodb/odb/tracer.cxx
new file mode 100644
index 0000000..1e636a7
--- /dev/null
+++ b/libodb/odb/tracer.cxx
@@ -0,0 +1,95 @@
+// file : odb/tracer.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <iostream>
+
+#include <odb/tracer.hxx>
+#include <odb/statement.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ //
+ // tracer
+ //
+
+ 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&)
+ {
+ }
+
+ //
+ // stderr_tracer
+ //
+
+ class stderr_tracer_type: public tracer
+ {
+ public:
+ stderr_tracer_type (bool full): full_ (full) {}
+
+ virtual void
+ prepare (connection&, const statement&);
+
+ virtual void
+ execute (connection&, const char* statement);
+
+ virtual void
+ deallocate (connection&, const statement&);
+
+ // Override the other version to get rid of a Sun CC warning.
+ //
+ virtual void
+ execute (connection&, const statement&);
+
+ private:
+ bool full_;
+ };
+
+ void stderr_tracer_type::
+ prepare (connection&, const statement& s)
+ {
+ if (full_)
+ cerr << "PREPARE " << s.text () << endl;
+ }
+
+ void stderr_tracer_type::
+ execute (connection&, const char* s)
+ {
+ cerr << s << endl;
+ }
+
+ void stderr_tracer_type::
+ deallocate (connection&, const statement& s)
+ {
+ if (full_)
+ cerr << "DEALLOCATE " << s.text () << endl;
+ }
+
+ void stderr_tracer_type::
+ execute (connection& c, const statement& s)
+ {
+ execute (c, s.text ());
+ }
+
+ static stderr_tracer_type stderr_tracer_ (false);
+ static stderr_tracer_type stderr_full_tracer_ (true);
+ tracer& stderr_tracer = stderr_tracer_;
+ tracer& stderr_full_tracer = stderr_full_tracer_;
+}
diff --git a/libodb/odb/tracer.hxx b/libodb/odb/tracer.hxx
new file mode 100644
index 0000000..11e4e76
--- /dev/null
+++ b/libodb/odb/tracer.hxx
@@ -0,0 +1,36 @@
+// file : odb/tracer.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TRACER_HXX
+#define ODB_TRACER_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ class LIBODB_EXPORT 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&);
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_TRACER_HXX
diff --git a/libodb/odb/traits.hxx b/libodb/odb/traits.hxx
new file mode 100644
index 0000000..2c6f5d6
--- /dev/null
+++ b/libodb/odb/traits.hxx
@@ -0,0 +1,317 @@
+// file : odb/traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TRAITS_HXX
+#define ODB_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+#include <odb/pointer-traits.hxx>
+
+namespace odb
+{
+ // Fallback dummy for non-persistent classes. It is necessary to allow
+ // the C++ compiler to instantiate persist(), etc., signatures in class
+ // database when T is a pointer (raw, smart). The overloads that use
+ // these dummy would never actually be selected by the compiler.
+ //
+ template <typename T>
+ class access::object_traits
+ {
+ // If a C++ compiler issues an error pointing to this class and saying
+ // that it is missing some declaration, then you are most likely trying
+ // to perform a database operation on a C++ type that is not a persistent
+ // object. Or you forgot to include the corresponding -odb.hxx file.
+ //
+ public:
+ struct id_type {};
+ typedef T object_type;
+ typedef T* pointer_type;
+
+ static const bool polymorphic = false;
+ };
+
+ template <typename T, typename P>
+ class access::object_factory
+ {
+ public:
+ typedef T object_type;
+ typedef P pointer_type;
+
+ static P
+ create ()
+ {
+ // By default use pointer-specific construction.
+ //
+ return pointer_factory<T, P>::create ();
+ }
+ };
+
+ template <typename T, typename P>
+ class access::view_factory
+ {
+ public:
+ typedef T view_type;
+ typedef P pointer_type;
+
+ static P
+ create ()
+ {
+ // By default use pointer-specific construction.
+ //
+ return pointer_factory<T, P>::create ();
+ }
+ };
+
+ template <typename T, typename P>
+ class access::pointer_factory
+ {
+ public:
+ typedef T value_type;
+ typedef P pointer_type;
+
+
+ // Suppress bogus use-after-free introduced in GCC 12 (GCC bug #105327).
+ //
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 12
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuse-after-free"
+#endif
+
+ static P
+ create ()
+ {
+ void* v (pointer_traits<P>::allocate (sizeof (T)));
+ mem_guard g (v);
+ P p (new (v) T);
+ g.release ();
+ return p;
+ }
+
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 12
+#pragma GCC diagnostic pop
+#endif
+
+ private:
+ struct mem_guard
+ {
+ mem_guard (void* p): p_ (p) {}
+ ~mem_guard () {if (p_) pointer_traits<P>::free (p_);}
+ void release () {p_ = 0;}
+ void* p_;
+ };
+ };
+
+ //
+ // class_traits
+ //
+ enum class_kind
+ {
+ class_object,
+ class_view,
+ class_other
+ };
+
+ template <typename T>
+ struct class_traits
+ {
+ static const class_kind kind = class_other;
+ };
+
+ template <typename T>
+ struct class_traits<const T>
+ {
+ static const class_kind kind = class_traits<T>::kind;
+ };
+
+ //
+ // object_traits
+ //
+
+ template <typename T>
+ //
+ // If a C++ compiler issues an error pointing to this struct and
+ // saying that it is incomplete, then you are most likely trying to
+ // perform a database operation on a C++ type that is not a persistent
+ // object. Or you forgot to include the corresponding -odb.hxx file.
+ //
+ struct object_traits:
+ access::object_traits<T>,
+ access::object_factory<T, typename access::object_traits<T>::pointer_type>
+ {
+ typedef
+ odb::pointer_traits<typename access::object_traits<T>::pointer_type>
+ pointer_traits;
+
+ typedef typename access::object_traits<T>::object_type object_type;
+ typedef typename access::object_traits<T>::pointer_type pointer_type;
+ typedef typename pointer_traits::const_pointer_type const_pointer_type;
+ };
+
+ // Specialization for const objects. It only defines the id, object,
+ // pointer, and const_pointer types with pointer and const_pointer
+ // being the same. The idea is to only use this specialization in the
+ // interfaces, with the implementations detecting this situation and
+ // using the non-const object_traits version.
+ //
+ template <typename T>
+ struct object_traits<const T>
+ {
+ private:
+ typedef
+ odb::pointer_traits<typename access::object_traits<T>::pointer_type>
+ pointer_traits;
+
+ public:
+ typedef typename access::object_traits<T>::id_type id_type;
+ typedef typename access::object_traits<T>::object_type object_type;
+ typedef typename pointer_traits::const_pointer_type const_pointer_type;
+ typedef const_pointer_type pointer_type;
+
+ static const bool polymorphic = access::object_traits<T>::polymorphic;
+ };
+
+ // Specialization for section to allow instantiation of all the load()
+ // signature.
+ //
+ template <>
+ struct object_traits<section> {};
+
+ template <typename T, database_id DB>
+ //
+ // If a C++ compiler issues an error pointing to this struct and
+ // saying that it is incomplete, then you are most likely trying to
+ // perform a database operation on a C++ type that is not a persistent
+ // object. Or you forgot to include the corresponding -odb.hxx file.
+ //
+ struct object_traits_impl:
+ access::object_traits_impl<T, DB>,
+ access::object_factory<T, typename access::object_traits<T>::pointer_type>
+ {
+ typedef
+ odb::pointer_traits<typename access::object_traits<T>::pointer_type>
+ pointer_traits;
+
+ typedef typename access::object_traits<T>::object_type object_type;
+ typedef typename access::object_traits<T>::pointer_type pointer_type;
+ typedef typename pointer_traits::const_pointer_type const_pointer_type;
+ };
+
+ //
+ // view_traits
+ //
+
+ template <typename T>
+ //
+ // If a C++ compiler issues an error pointing to this struct and
+ // saying that it is incomplete, then you are most likely trying to
+ // perform a database operation on a C++ type that is not a view
+ // Or you forgot to include the corresponding -odb.hxx file.
+ //
+ struct view_traits:
+ access::view_traits<T>,
+ access::view_factory<T, typename access::view_traits<T>::pointer_type>
+ {
+ typedef
+ odb::pointer_traits<typename access::view_traits<T>::pointer_type>
+ pointer_traits;
+
+ typedef typename access::view_traits<T>::view_type view_type;
+ typedef typename access::view_traits<T>::pointer_type pointer_type;
+ };
+
+ // Specialization for const views. It only defines the view, pointer,
+ // and const_pointer types with pointer and const_pointer being the
+ // same. Similar to objects, the idea is to only use this specialization
+ // in the interfaces, with the implementations detecting this situation
+ // and using the non-const view_traits version.
+ //
+ template <typename T>
+ struct view_traits<const T>
+ {
+ private:
+ typedef
+ odb::pointer_traits<typename access::view_traits<T>::pointer_type>
+ pointer_traits;
+
+ public:
+ typedef typename access::view_traits<T>::view_type view_type;
+ typedef typename pointer_traits::const_pointer_type const_pointer_type;
+ typedef const_pointer_type pointer_type;
+ };
+
+ template <typename T, database_id DB>
+ //
+ // If a C++ compiler issues an error pointing to this struct and
+ // saying that it is incomplete, then you are most likely trying to
+ // perform a database operation on a C++ type that is not a view
+ // Or you forgot to include the corresponding -odb.hxx file.
+ //
+ struct view_traits_impl:
+ access::view_traits_impl<T, DB>,
+ access::view_factory<T, typename access::view_traits<T>::pointer_type>
+ {
+ typedef
+ odb::pointer_traits<typename access::view_traits<T>::pointer_type>
+ pointer_traits;
+
+ typedef typename access::view_traits<T>::view_type view_type;
+ typedef typename access::view_traits<T>::pointer_type pointer_type;
+ };
+
+ //
+ // composite_value_traits
+ //
+
+ template <typename T, database_id DB>
+ struct composite_value_traits: access::composite_value_traits<T, DB>
+ {
+ };
+
+ //
+ // Get root image from a polymorphic image chain.
+ //
+
+ template <typename T, std::size_t d>
+ struct root_image_impl
+ {
+ typedef root_image_impl<typename T::base_traits, d - 1> base_type;
+ typedef typename base_type::image_type image_type;
+
+ static image_type&
+ get (typename T::image_type& i) {return base_type::get (*i.base);}
+ };
+
+ template <typename T>
+ struct root_image_impl<T, 1>
+ {
+ typedef typename T::image_type image_type;
+
+ static image_type&
+ get (image_type& i) {return i;}
+ };
+
+ template <typename T, bool p>
+ struct root_image
+ {
+ typedef root_image_impl<T, T::depth> impl_type;
+ typedef typename impl_type::image_type image_type;
+
+ static image_type&
+ get (typename T::image_type& i) {return impl_type::get (i);}
+ };
+
+ template <typename T>
+ struct root_image<T, false>
+ {
+ typedef typename T::image_type image_type;
+
+ static image_type&
+ get (image_type& i) {return i;}
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_TRAITS_HXX
diff --git a/libodb/odb/transaction.cxx b/libodb/odb/transaction.cxx
new file mode 100644
index 0000000..f75cf32
--- /dev/null
+++ b/libodb/odb/transaction.cxx
@@ -0,0 +1,356 @@
+// file : odb/transaction.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/transaction.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/details/tls.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ using namespace details;
+
+ //
+ // transaction
+ //
+
+ static ODB_TLS_POINTER (transaction) current_transaction;
+
+ transaction::
+ ~transaction ()
+ {
+ if (!finalized_)
+ try {rollback ();} catch (...) {}
+ }
+
+ void transaction::
+ reset (transaction_impl* impl, bool make_current)
+ {
+ details::unique_ptr<transaction_impl> i (impl);
+
+ if (!finalized_)
+ rollback ();
+
+ impl_.reset (i.release ());
+
+ if (make_current && tls_get (current_transaction) != 0)
+ throw already_in_transaction ();
+
+ impl_->start ();
+ finalized_ = false;
+
+ if (make_current)
+ tls_set (current_transaction, this);
+ }
+
+ bool transaction::
+ has_current ()
+ {
+ return tls_get (current_transaction) != 0;
+ }
+
+ transaction& transaction::
+ current ()
+ {
+ transaction* cur (tls_get (current_transaction));
+
+ if (cur == 0)
+ throw not_in_transaction ();
+
+ return *cur;
+ }
+
+ void transaction::
+ current (transaction& t)
+ {
+ tls_set (current_transaction, &t);
+ }
+
+ void transaction::
+ reset_current ()
+ {
+ transaction* t (0);
+ tls_set (current_transaction, t);
+ }
+
+ struct rollback_guard
+ {
+ rollback_guard (transaction& t): t_ (&t) {}
+ ~rollback_guard ()
+ {if (t_ != 0) t_->callback_call (transaction::event_rollback);}
+ void release () {t_ = 0;}
+ private:
+ transaction* t_;
+ };
+
+ void transaction::
+ commit ()
+ {
+ if (finalized_)
+ throw transaction_already_finalized ();
+
+ finalized_ = true;
+ rollback_guard rg (*this);
+
+ impl_->tracer (0);
+
+ if (tls_get (current_transaction) == this)
+ {
+ transaction* t (0);
+ tls_set (current_transaction, t);
+ }
+
+ impl_->commit ();
+ rg.release ();
+
+ if (callback_count_ != 0)
+ callback_call (event_commit);
+ }
+
+ void transaction::
+ rollback ()
+ {
+ if (finalized_)
+ throw transaction_already_finalized ();
+
+ finalized_ = true;
+ rollback_guard rg (*this);
+
+ impl_->tracer (0);
+
+ if (tls_get (current_transaction) == this)
+ {
+ transaction* t (0);
+ tls_set (current_transaction, t);
+ }
+
+ impl_->rollback ();
+ rg.release ();
+
+ if (callback_count_ != 0)
+ callback_call (event_rollback);
+ }
+
+ void transaction::
+ callback_call (unsigned short event)
+ {
+ size_t stack_count (callback_count_ < stack_callback_count
+ ? callback_count_ : stack_callback_count);
+ size_t dyn_count (callback_count_ - stack_count);
+
+ // We need to be careful with the situation where a callback
+ // throws and we neither call the rest of the callbacks nor
+ // reset their states. To make sure this doesn't happen, we
+ // do a first pass and reset all the states.
+ //
+ for (size_t i (0); i < stack_count; ++i)
+ {
+ callback_data& d (stack_callbacks_[i]);
+ if (d.event != 0 && d.state != 0)
+ *d.state = 0;
+ }
+
+ for (size_t i (0); i < dyn_count; ++i)
+ {
+ callback_data& d (dyn_callbacks_[i]);
+ if (d.event != 0 && d.state != 0)
+ *d.state = 0;
+ }
+
+ // Now do the actual calls.
+ //
+ for (size_t i (0); i < stack_count; ++i)
+ {
+ callback_data& d (stack_callbacks_[i]);
+ if (d.event & event)
+ d.func (event, d.key, d.data);
+ }
+
+ for (size_t i (0); i < dyn_count; ++i)
+ {
+ callback_data& d (dyn_callbacks_[i]);
+ if (d.event & event)
+ d.func (event, d.key, d.data);
+ }
+
+ // Clean things up in case this instance is going to be reused.
+ //
+ if (dyn_count != 0)
+ dyn_callbacks_.clear ();
+
+ free_callback_ = max_callback_count;
+ callback_count_ = 0;
+ }
+
+ void transaction::
+ callback_register (callback_type func,
+ void* key,
+ unsigned short event,
+ unsigned long long data,
+ transaction** state)
+ {
+ callback_data* s;
+
+ // If we have a free slot, use it.
+ //
+ if (free_callback_ != max_callback_count)
+ {
+ s = (free_callback_ < stack_callback_count)
+ ? stack_callbacks_ + free_callback_
+ : &dyn_callbacks_[free_callback_ - stack_callback_count];
+
+ free_callback_ = reinterpret_cast<size_t> (s->key);
+ }
+ // If we have space in the stack, grab that.
+ //
+ else if (callback_count_ < stack_callback_count)
+ {
+ s = stack_callbacks_ + callback_count_;
+ callback_count_++;
+ }
+ // Otherwise use the dynamic storage.
+ //
+ else
+ {
+ dyn_callbacks_.push_back (callback_data ());
+ s = &dyn_callbacks_.back ();
+ callback_count_++;
+ }
+
+ s->func = func;
+ s->key = key;
+ s->event = event;
+ s->data = data;
+ s->state = state;
+ }
+
+ size_t transaction::
+ callback_find (void* key)
+ {
+ if (callback_count_ == 0)
+ return 0;
+
+ size_t stack_count;
+
+ // See if this is the last slot registered. This will be a fast path if,
+ // for example, things are going to be unregistered from destructors.
+ //
+ if (callback_count_ <= stack_callback_count)
+ {
+ if (stack_callbacks_[callback_count_ - 1].key == key)
+ return callback_count_ - 1;
+
+ stack_count = callback_count_;
+ }
+ else
+ {
+ if (dyn_callbacks_.back ().key == key)
+ return callback_count_ - 1;
+
+ stack_count = stack_callback_count;
+ }
+
+ // Otherwise do a linear search.
+ //
+ for (size_t i (0); i < stack_count; ++i)
+ if (stack_callbacks_[i].key == key)
+ return i;
+
+ for (size_t i (0), dyn_count (callback_count_ - stack_count);
+ i < dyn_count; ++i)
+ if (dyn_callbacks_[i].key == key)
+ return i + stack_callback_count;
+
+ return callback_count_;
+ }
+
+ void transaction::
+ callback_unregister (void* key)
+ {
+ size_t i (callback_find (key));
+
+ // It is ok for this function not to find the key.
+ //
+ if (i == callback_count_)
+ return;
+
+ // See if this is the last slot registered.
+ //
+ if (i == callback_count_ - 1)
+ {
+ if (i >= stack_callback_count)
+ dyn_callbacks_.pop_back ();
+
+ callback_count_--;
+ }
+ else
+ {
+ callback_data& d (
+ i < stack_callback_count
+ ? stack_callbacks_[i]
+ : dyn_callbacks_[i - stack_callback_count]);
+
+ // Add to the free list.
+ //
+ d.event = 0;
+ d.key = reinterpret_cast<void*> (free_callback_);
+ free_callback_ = i;
+ }
+ }
+
+ void transaction::
+ callback_update (void* key,
+ unsigned short event,
+ unsigned long long data,
+ transaction** state)
+ {
+ size_t i (callback_find (key));
+
+ // It is ok for this function not to find the key.
+ //
+ if (i == callback_count_)
+ return;
+
+ callback_data& d (
+ i < stack_callback_count
+ ? stack_callbacks_[i]
+ : dyn_callbacks_[i - stack_callback_count]);
+
+ d.event = event;
+ d.data = data;
+ d.state = state;
+ }
+
+ //
+ // transaction_impl
+ //
+
+ transaction_impl::
+ ~transaction_impl ()
+ {
+ }
+
+ connection& transaction_impl::
+ connection (database_type* db)
+ {
+ assert (db == 0 || db == &database_);
+ return *connection_;
+ }
+
+ // The transaction-specific tracer is stored in the connection. See the
+ // connection class for the reason.
+ //
+ void transaction_impl::
+ tracer (tracer_type* t)
+ {
+ connection_->transaction_tracer_ = t;
+ }
+
+ tracer* transaction_impl::
+ tracer () const
+ {
+ return connection_->transaction_tracer_;
+ }
+}
diff --git a/libodb/odb/transaction.hxx b/libodb/odb/transaction.hxx
new file mode 100644
index 0000000..1958df3
--- /dev/null
+++ b/libodb/odb/transaction.hxx
@@ -0,0 +1,278 @@
+// file : odb/transaction.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_TRANSACTION_HXX
+#define ODB_TRANSACTION_HXX
+
+#include <odb/pre.hxx>
+
+#include <vector>
+#include <cstddef> // std::size_t
+
+#include <odb/forward.hxx>
+
+#include <odb/details/export.hxx>
+#include <odb/details/unique-ptr.hxx>
+
+namespace odb
+{
+ class transaction_impl;
+
+ class LIBODB_EXPORT transaction
+ {
+ public:
+ typedef odb::database database_type;
+ typedef odb::connection connection_type;
+
+ // If the second argument is false, then this transaction is not made
+ // the current transaction of the thread.
+ //
+ explicit
+ transaction (transaction_impl*, bool make_current = true);
+
+ // Create a finalized transaction instance which can later be initialized
+ // with reset().
+ //
+ transaction ();
+
+ // Unless the transaction has already been finalized (explicitly
+ // committed or rolled back), the destructor will roll it back.
+ //
+ ~transaction ();
+
+ // Unless the current transaction has already been finalized (explicitly
+ // committed or rolled back), reset will roll it back. If the second
+ // argument is false, then this transaction is not made the current
+ // transaction of the thread.
+ //
+ void
+ reset (transaction_impl*, bool make_current = true);
+
+ void
+ commit ();
+
+ void
+ rollback ();
+
+ // Return the database this transaction is on.
+ //
+ database_type&
+ database ();
+
+ // Return the connection this transaction is on.
+ //
+ // The second version verifies the connection is to the specified
+ // database. For database implementations that support attaching multiple
+ // databases it may also select the connection corresponding to the
+ // specified database.
+ //
+ connection_type&
+ connection ();
+
+ connection_type&
+ connection (database_type&);
+
+ bool
+ finalized () const {return finalized_;}
+
+ public:
+ // Return true if there is a transaction in effect.
+ //
+ static bool
+ has_current ();
+
+ // 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&);
+
+ // Revert to the no transaction in effect state for the current thread.
+ //
+ static void
+ reset_current ();
+
+ // SQL statement tracing.
+ //
+ public:
+ typedef odb::tracer tracer_type;
+
+ void
+ tracer (tracer_type&);
+
+ void
+ tracer (tracer_type*);
+
+ tracer_type*
+ tracer () const;
+
+ // Post-commit/rollback callbacks.
+ //
+ public:
+ static const unsigned short event_commit = 0x01;
+ static const unsigned short event_rollback = 0x02;
+ static const unsigned short event_all = event_commit | event_rollback;
+
+ typedef void (*callback_type) (
+ unsigned short event, void* key, unsigned long long data);
+
+ // Register a post-commit/rollback callback. The data argument
+ // can be used to store any user data that does not exceed 8
+ // bytes and doesn't require alignment greater than unsigned
+ // long long, such as an old value that needs to be restored
+ // in case of a rollback.
+ //
+ // The state argument can be used to indicate to the caller
+ // that the callback has been unregistered because the
+ // transaction has terminated. In this case the transaction
+ // resets the passed pointer to 0.
+ //
+ // Note that the order in which the callbacks are called is
+ // unspecified.
+ //
+ void
+ callback_register (callback_type,
+ void* key,
+ unsigned short event = event_all,
+ unsigned long long data = 0,
+ transaction** state = 0);
+
+ // Unregister a post-commit/rollback callback. Note that this is a
+ // potentially slow operation. You also don't need to unregister
+ // a callback that has been called or auto-reset using the state
+ // argument passed to register(). This function does nothing if
+ // the key is not found.
+ //
+ void
+ callback_unregister (void* key);
+
+ // Update the event, data, and state values for a callback. Note
+ // that just like unregister(), this is a potentially slow operation.
+ //
+ void
+ callback_update (void* key,
+ unsigned short event,
+ unsigned long long data = 0,
+ transaction** state = 0);
+
+ public:
+ transaction_impl&
+ implementation ();
+
+ // Copying or assignment of transactions is not supported.
+ //
+ private:
+ transaction (const transaction&);
+ transaction& operator= (const transaction&);
+
+ protected:
+ friend struct rollback_guard;
+
+ std::size_t
+ callback_find (void* key);
+
+ void
+ callback_call (unsigned short event);
+
+ protected:
+ bool finalized_;
+ details::unique_ptr<transaction_impl> impl_;
+
+ // Callbacks.
+ //
+ struct callback_data
+ {
+ unsigned short event;
+ callback_type func;
+ void* key;
+ unsigned long long data;
+ transaction** state;
+ };
+
+ // Slots for the first 20 callback are pre-allocated on the stack.
+ // For the rest they are allocated dynamically as needed.
+ //
+ // Note, if you change stack_callback_count, make sure you also
+ // update the common/transaction/callback test accordingly.
+ //
+ static const std::size_t stack_callback_count = 20;
+ static const std::size_t max_callback_count = ~(std::size_t (0));
+
+ callback_data stack_callbacks_[stack_callback_count];
+ std::vector<callback_data> dyn_callbacks_;
+
+ // When a callback is unregistered, the free slot from the stack is
+ // added to the linked list of free slots which is organized by
+ // re-using the key data member to store the slot's index (we cannot
+ // store a pointer because std::vector may move slots on expansion).
+ // The value equal to max_callback_count indicates no free slots are
+ // available.
+ //
+ std::size_t free_callback_;
+
+ // Total number of used slots, both registered and in the free list.
+ //
+ std::size_t callback_count_;
+ };
+
+ class LIBODB_EXPORT transaction_impl
+ {
+ public:
+ typedef odb::tracer tracer_type;
+ typedef odb::database database_type;
+ typedef odb::connection connection_type;
+
+ virtual
+ ~transaction_impl ();
+
+ virtual void
+ start () = 0;
+
+ virtual void
+ commit () = 0;
+
+ virtual void
+ rollback () = 0;
+
+ database_type&
+ database ()
+ {
+ return database_;
+ }
+
+ virtual connection_type&
+ connection (database_type*);
+
+ virtual void
+ tracer (tracer_type*);
+
+ virtual tracer_type*
+ tracer () const;
+
+ protected:
+ transaction_impl (database_type& db)
+ : database_ (db), connection_ (0)
+ {
+ }
+
+ transaction_impl (database_type& db, connection_type& c)
+ : database_ (db), connection_ (&c)
+ {
+ }
+
+ protected:
+ database_type& database_;
+ connection_type* connection_;
+ };
+}
+
+#include <odb/transaction.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_TRANSACTION_HXX
diff --git a/libodb/odb/transaction.ixx b/libodb/odb/transaction.ixx
new file mode 100644
index 0000000..cc1ce5e
--- /dev/null
+++ b/libodb/odb/transaction.ixx
@@ -0,0 +1,68 @@
+// file : odb/transaction.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/connection.hxx>
+
+namespace odb
+{
+ inline transaction::
+ transaction ()
+ : finalized_ (true),
+ impl_ (0),
+ free_callback_ (max_callback_count),
+ callback_count_ (0)
+ {
+ }
+
+ inline transaction::
+ transaction (transaction_impl* impl, bool make_current)
+ : finalized_ (true),
+ impl_ (0),
+ free_callback_ (max_callback_count),
+ callback_count_ (0)
+ {
+ reset (impl, make_current);
+ }
+
+ inline transaction::database_type& transaction::
+ database ()
+ {
+ return impl_->database ();
+ }
+
+ inline transaction::connection_type& transaction::
+ connection ()
+ {
+ return impl_->connection (0);
+ }
+
+ inline transaction::connection_type& transaction::
+ connection (database_type& db)
+ {
+ return impl_->connection (&db);
+ }
+
+ inline transaction_impl& transaction::
+ implementation ()
+ {
+ return *impl_;
+ }
+
+ inline void transaction::
+ tracer (tracer_type& t)
+ {
+ impl_->tracer (&t);
+ }
+
+ inline void transaction::
+ tracer (tracer_type* t)
+ {
+ impl_->tracer (t);
+ }
+
+ inline transaction::tracer_type* transaction::
+ tracer () const
+ {
+ return impl_->tracer ();
+ }
+}
diff --git a/libodb/odb/vector-impl.cxx b/libodb/odb/vector-impl.cxx
new file mode 100644
index 0000000..ca30f8d
--- /dev/null
+++ b/libodb/odb/vector-impl.cxx
@@ -0,0 +1,208 @@
+// file : odb/vector-impl.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/vector-impl.hxx>
+
+#include <cstring> // std::memcpy, std::memset
+#include <algorithm> // std::swap
+
+using namespace std;
+
+namespace odb
+{
+ // vector_impl
+ //
+ const unsigned char vector_impl::mask_[4] = {0x3, 0xC, 0x30, 0xC0};
+ const unsigned char vector_impl::shift_[4] = {0, 2, 4, 6};
+
+ vector_impl::
+ vector_impl (const vector_impl& x)
+ : state_ (x.state_), size_ (0), tail_ (0), capacity_ (0), data_ (0)
+ {
+ // Copy the data over if we are tracking.
+ //
+ if (state_ == state_tracking && x.size_ != 0)
+ {
+ realloc (x.size_ < 1024 ? 1024 : x.size_);
+ memcpy (data_, x.data_, x.size_ / 4 + (x.size_ % 4 == 0 ? 0 : 1));
+ size_ = x.size_;
+ tail_ = x.tail_;
+ }
+ }
+
+ void vector_impl::
+ realloc (size_t n)
+ {
+ // The new capacity can be less or greater than the old one, but
+ // it cannot be less than size.
+ //
+ size_t b (n / 4 + (n % 4 == 0 ? 0 : 1));
+
+ if (b != capacity_ * 4)
+ {
+ unsigned char* d (static_cast<unsigned char*> (operator new (b)));
+
+ if (size_ != 0)
+ memcpy (d, data_, size_ / 4 + (size_ % 4 == 0 ? 0 : 1));
+
+ if (data_ != 0)
+ operator delete (data_);
+
+ data_ = d;
+ capacity_ = b * 4;
+ }
+ }
+
+ void vector_impl::
+ shrink_to_fit ()
+ {
+ if (size_ != capacity_)
+ {
+ if (size_ != 0)
+ realloc (size_);
+ else
+ {
+ operator delete (data_);
+ data_ = 0;
+ capacity_ = 0;
+ }
+ }
+ }
+
+ void vector_impl::
+ start (size_t n)
+ {
+ if (n != 0)
+ {
+ if (capacity_ < n)
+ {
+ size_ = 0;
+ realloc (n < 1024 ? 1024 : n);
+ }
+
+ memset (data_, 0, n / 4 + (n % 4 == 0 ? 0 : 1));
+ }
+
+ state_ = state_tracking;
+ size_ = tail_ = n;
+ }
+
+ void vector_impl::
+ push_back (size_t n)
+ {
+ for (; n != 0; --n)
+ {
+ size_t i (tail_);
+
+ element_state_type s;
+ if (i != size_)
+ // We have an erased element we can reuse.
+ //
+ s = state_updated;
+ else
+ {
+ if (size_ == capacity_)
+ {
+ size_t c (capacity_ == 0 ? 1024 : capacity_ * 2);
+ if (c < size_ + n)
+ c = size_ + n;
+ realloc (c);
+ }
+
+ s = state_inserted;
+ size_++;
+ }
+
+ set (i, s);
+ tail_++;
+ }
+ }
+
+ void vector_impl::
+ pop_back (size_t n)
+ {
+ for (; n != 0; --n)
+ {
+ size_t i (tail_ - 1);
+
+ if (state (i) != state_inserted)
+ set (i, state_erased);
+ else
+ size_--; // tail_ == size_
+
+ tail_--;
+ }
+ }
+
+ void vector_impl::
+ insert (size_t i, size_t n)
+ {
+ for (; i != tail_; ++i)
+ if (state (i) != state_inserted)
+ set (i, state_updated);
+
+ push_back (n);
+ }
+
+ void vector_impl::
+ erase (size_t i, size_t n)
+ {
+ pop_back (n);
+
+ for (; i != tail_; ++i)
+ if (state (i) != state_inserted)
+ set (i, state_updated);
+ }
+
+ void vector_impl::
+ clear ()
+ {
+ // The idea is to drop any inserted elements from the back and
+ // set everything else to erased.
+ //
+ if (tail_ == size_)
+ {
+ while (size_ != 0 && state (size_ - 1) == state_inserted)
+ size_--;
+
+ tail_ = size_;
+ }
+
+ if (tail_ != 0)
+ memset (data_, 0xFF, tail_ / 4 + (tail_ % 4 == 0 ? 0 : 1));
+
+ tail_ = 0;
+ }
+
+ // vector_base
+ //
+ void vector_base::
+ rollback (unsigned short, void* key, unsigned long long)
+ {
+ // Mark as changed.
+ //
+ static_cast<vector_base*> (key)->impl_.change ();
+ }
+
+ void vector_base::
+ swap_tran (vector_base& x)
+ {
+ // If either instance is armed, then we need to update the
+ // callback registration.
+ //
+ transaction* t (x.tran_);
+ if (tran_ != 0)
+ {
+ tran_->callback_unregister (this);
+ x._arm (*tran_);
+ }
+
+ if (t != 0)
+ {
+ t->callback_unregister (&x);
+ _arm (*t);
+ }
+
+ std::swap (tran_, x.tran_);
+ }
+}
diff --git a/libodb/odb/vector-impl.hxx b/libodb/odb/vector-impl.hxx
new file mode 100644
index 0000000..9f2ea7c
--- /dev/null
+++ b/libodb/odb/vector-impl.hxx
@@ -0,0 +1,221 @@
+// file : odb/vector-impl.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_VECTOR_IMPL_HXX
+#define ODB_VECTOR_IMPL_HXX
+
+#include <odb/pre.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <new>
+#include <cstddef> // std::size_t
+
+#include <odb/transaction.hxx>
+#include <odb/details/export.hxx>
+
+namespace odb
+{
+ // Change tracking vector implementation details.
+ //
+ class LIBODB_EXPORT vector_impl
+ {
+ public:
+ enum element_state_type
+ {
+ state_unchanged,
+ state_inserted,
+ state_updated,
+ state_erased
+ };
+
+ enum container_state_type
+ {
+ state_tracking,
+ state_not_tracking,
+ state_changed // Container has changed but individual changes
+ // were not tracked.
+ };
+
+ vector_impl ();
+ ~vector_impl ();
+
+ // The copy constructor will copy the state. The idea is that the
+ // copy keeps tracking changes, just like the original.
+ //
+ vector_impl (const vector_impl&);
+
+#ifdef ODB_CXX11
+ vector_impl (vector_impl&&) noexcept;
+#endif
+
+ void
+ swap (vector_impl& x);
+
+ // Allocate enough memory to store the specified number of
+ // elements.
+ //
+ void
+ reserve (std::size_t);
+
+ // Reduce capacity to size.
+ //
+ void
+ shrink_to_fit ();
+
+ // Capacity (each entry takes 2 bits).
+ //
+ std::size_t
+ capacity () const;
+
+ // (Re)start tracking changes for a vector with n elements.
+ //
+ void
+ start (std::size_t);
+
+ // Stop tracking changes.
+ //
+ void
+ stop ();
+
+ // Mark the container as changed without tracking the changes.
+ // This state is useful as a fallback mechnism for situations
+ // where the change information has been discarded (e.g., after
+ // its state has been updated in the database) but the container
+ // should remain changed (e.g., after the transaction is rolled
+ // back).
+ //
+ void
+ change ();
+
+ // Get the state of the container.
+ //
+ container_state_type
+ state () const;
+
+ // Shortcut for state() == state_tracking.
+ //
+ bool
+ tracking () const;
+
+ // Note that the returned size can be greater than the actual,
+ // parallel vector size. In this case the difference is the
+ // erased elements at the back.
+ //
+ std::size_t
+ size () const;
+
+ // Get the change state of the specified element.
+ //
+ element_state_type
+ state (std::size_t) const;
+
+ // Change notifications.
+ //
+ void
+ push_back (std::size_t n = 1);
+
+ void
+ pop_back (std::size_t n = 1);
+
+ void
+ insert (std::size_t, std::size_t n = 1);
+
+ void
+ erase (std::size_t, std::size_t n = 1);
+
+ void
+ modify (std::size_t, std::size_t n = 1);
+
+ void
+ clear ();
+
+ void
+ assign (std::size_t n);
+
+ void
+ resize (std::size_t n);
+
+ private:
+ // Assignment does not make sense (it is changing of the content).
+ //
+ vector_impl& operator= (const vector_impl&);
+
+ private:
+ void
+ realloc (std::size_t);
+
+ void
+ set (std::size_t, element_state_type);
+
+ static const unsigned char mask_[4];
+ static const unsigned char shift_[4];
+
+ container_state_type state_;
+
+ // Size, tail, and capacity are in 2-bit blocks. Size is the number
+ // of elements we have in data. Tail is the position of the first
+ // erased element at the back. If there are no erased elements, then
+ // tail is equal size. Capacity is the number of elements we can
+ // store in data.
+ //
+ std::size_t size_;
+ std::size_t tail_;
+ std::size_t capacity_;
+ unsigned char* data_;
+ };
+
+ // Base class that provides a change tracking interface and
+ // handles the rollback callback. The only function that's
+ // missing is _start() which needs to know the number of
+ // elements currently in the vector.
+ //
+ class LIBODB_EXPORT vector_base
+ {
+ public:
+ void
+ _stop () const;
+
+ bool
+ _tracking () const;
+
+ void
+ _arm (transaction& t) const;
+
+ vector_impl&
+ _impl () const {return impl_;}
+
+ private:
+ // Assignment is changing of the content.
+ //
+ vector_base& operator= (const vector_base&);
+
+ protected:
+ ~vector_base ();
+ vector_base ();
+ vector_base (const vector_base&);
+
+#ifdef ODB_CXX11
+ vector_base (vector_base&&) noexcept;
+#endif
+
+ void
+ swap (vector_base&);
+
+ static void
+ rollback (unsigned short, void* key, unsigned long long);
+
+ private:
+ void
+ swap_tran (vector_base&);
+
+ protected:
+ mutable vector_impl impl_;
+ mutable transaction* tran_;
+ };
+}
+
+#include <odb/vector-impl.ixx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_VECTOR_IMPL_HXX
diff --git a/libodb/odb/vector-impl.ixx b/libodb/odb/vector-impl.ixx
new file mode 100644
index 0000000..21999d5
--- /dev/null
+++ b/libodb/odb/vector-impl.ixx
@@ -0,0 +1,210 @@
+// file : odb/vector-impl.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifdef ODB_CXX11
+# include <utility> // std::swap, std::move
+#else
+# include <algorithm> // std::swap
+#endif
+
+namespace odb
+{
+ // vector_impl
+ //
+ inline vector_impl::
+ vector_impl ()
+ : state_ (state_not_tracking),
+ size_ (0), tail_ (0), capacity_ (0), data_ (0)
+ {
+ }
+
+ inline void vector_impl::
+ swap (vector_impl& x)
+ {
+ std::swap (state_, x.state_);
+ std::swap (size_, x.size_);
+ std::swap (tail_, x.tail_);
+ std::swap (capacity_, x.capacity_);
+ std::swap (data_, x.data_);
+ }
+
+#ifdef ODB_CXX11
+ inline vector_impl::
+ vector_impl (vector_impl&& x) noexcept
+ : state_ (state_not_tracking),
+ size_ (0), tail_ (0), capacity_ (0), data_ (0)
+ {
+ swap (x);
+ }
+#endif
+
+ inline vector_impl::
+ ~vector_impl ()
+ {
+ if (data_ != 0)
+ operator delete (data_);
+ }
+
+ inline void vector_impl::
+ reserve (std::size_t n)
+ {
+ if (n > capacity_)
+ realloc (n);
+ }
+
+ inline void vector_impl::
+ stop ()
+ {
+ state_ = state_not_tracking;
+ size_ = tail_ = 0;
+ }
+
+ inline void vector_impl::
+ change ()
+ {
+ state_ = state_changed;
+ size_ = tail_ = 0;
+ }
+
+ inline vector_impl::container_state_type vector_impl::
+ state () const
+ {
+ return state_;
+ }
+
+ inline bool vector_impl::
+ tracking () const
+ {
+ return state_ == state_tracking;
+ }
+
+ inline std::size_t vector_impl::
+ size () const
+ {
+ return size_;
+ }
+
+ inline std::size_t vector_impl::
+ capacity () const
+ {
+ return capacity_;
+ }
+
+ inline vector_impl::element_state_type vector_impl::
+ state (std::size_t i) const
+ {
+ std::size_t r (i % 4);
+ unsigned char v (data_[i / 4]);
+ return static_cast<element_state_type> ((v & mask_[r]) >> shift_[r]);
+ }
+
+ inline void vector_impl::
+ set (std::size_t i, element_state_type s)
+ {
+ std::size_t r (i % 4);
+ i /= 4;
+ unsigned char v (static_cast<unsigned char> (s));
+ v <<= shift_[r];
+ data_[i] = (data_[i] & ~mask_[r]) | v;
+ }
+
+ inline void vector_impl::
+ modify (std::size_t i, std::size_t n)
+ {
+ for (; n != 0; --n, ++i)
+ if (state (i) != state_inserted)
+ set (i, state_updated);
+ }
+
+ inline void vector_impl::
+ assign (std::size_t n)
+ {
+ if (tail_ != 0)
+ clear ();
+
+ push_back (n);
+ }
+
+ inline void vector_impl::
+ resize (size_t n)
+ {
+ if (n < tail_)
+ pop_back (tail_ - n);
+ else if (n > tail_)
+ push_back (n - tail_);
+ }
+
+ // vector_base
+ //
+ inline vector_base::
+ ~vector_base ()
+ {
+ if (tran_ != 0)
+ tran_->callback_unregister (this);
+ }
+
+ inline vector_base::
+ vector_base (): tran_ (0) {}
+
+ inline void vector_base::
+ _arm (transaction& t) const
+ {
+ tran_ = &t;
+ t.callback_register (&rollback,
+ const_cast<vector_base*> (this),
+ transaction::event_rollback,
+ 0,
+ &tran_);
+ }
+
+ inline vector_base::
+ vector_base (const vector_base& x)
+ : impl_ (x.impl_), tran_ (0)
+ {
+ // If the original is armed, then arm ourselves as well.
+ //
+ if (x.tran_ != 0)
+ _arm (*x.tran_);
+ }
+
+ inline void vector_base::
+ swap (vector_base& x)
+ {
+ impl_.swap (x.impl_);
+
+ if (tran_ != 0 || x.tran_ != 0)
+ swap_tran (x);
+ }
+
+#ifdef ODB_CXX11
+ inline vector_base::
+ vector_base (vector_base&& x) noexcept
+ : impl_ (std::move (x.impl_)), tran_ (0)
+ {
+ if (x.tran_ != 0)
+ {
+ x.tran_->callback_unregister (&x);
+
+ // Note that _arm() can potentially throw bad_alloc while adding a new
+ // callback to the callbacks list of the transaction object. However, we
+ // assume that this will not happen since the new callback should be
+ // saved into an existing slot, freed by the above callback_unregister()
+ // call.
+ //
+ _arm (*x.tran_);
+ }
+ }
+#endif
+
+ inline void vector_base::
+ _stop () const
+ {
+ impl_.stop ();
+ }
+
+ inline bool vector_base::
+ _tracking () const
+ {
+ return impl_.tracking ();
+ }
+}
diff --git a/libodb/odb/vector-traits.hxx b/libodb/odb/vector-traits.hxx
new file mode 100644
index 0000000..5e6cf14
--- /dev/null
+++ b/libodb/odb/vector-traits.hxx
@@ -0,0 +1,106 @@
+// file : odb/vector-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_VECTOR_TRAITS_HXX
+#define ODB_VECTOR_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/vector.hxx>
+#include <odb/vector-impl.hxx>
+#include <odb/container-traits.hxx>
+#include <odb/transaction.hxx>
+
+namespace odb
+{
+ template <typename V, typename A LIBODB_VECTOR_ARG_DECL>
+ class access::container_traits<vector<V, A LIBODB_VECTOR_ARG_USE> >
+ {
+ public:
+ static const container_kind kind = ck_ordered;
+ static const bool smart = true;
+
+ typedef vector<V, A> container_type;
+
+ typedef V value_type;
+ typedef typename container_type::size_type index_type;
+
+ typedef smart_ordered_functions<index_type, value_type> functions;
+ typedef ordered_functions<index_type, value_type> dumb_functions;
+
+ public:
+ static void
+ persist (const container_type& c, const functions& f)
+ {
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+
+ // Now that this container is persistent, start tracking changes.
+ //
+ c._start ();
+ }
+
+ static void
+ load (container_type& c, bool more, const functions& f)
+ {
+ // Stop tracking changes.
+ //
+ c._stop ();
+
+ // Load.
+ //
+ c.clear ();
+ while (more)
+ {
+ index_type dummy;
+ c.push_back (value_type ());
+ more = f.select (dummy, c.modify_back ());
+ }
+
+ // Start tracking changes.
+ //
+ c._start ();
+ }
+
+ static bool
+ changed (const container_type&);
+
+ static void
+ update (const container_type&, const functions&);
+
+ static void
+ erase (const container_type* c, const functions& f)
+ {
+ f.delete_ (0);
+
+ // Stop tracking changes.
+ //
+ if (c != 0)
+ c->_stop ();
+ }
+
+ // Version of load() for dumb functions. Used to support
+ // inverse members of the container type. The implementation
+ // is identical to the smart one except we don't turn off/on
+ // change tracking.
+ //
+ static void
+ load (container_type& c, bool more, const dumb_functions& f)
+ {
+ c.clear ();
+
+ while (more)
+ {
+ index_type dummy;
+ c.push_back (value_type ());
+ more = f.select (dummy, c.modify_back ());
+ }
+ }
+ };
+}
+
+#include <odb/vector-traits.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_VECTOR_TRAITS_HXX
diff --git a/libodb/odb/vector-traits.txx b/libodb/odb/vector-traits.txx
new file mode 100644
index 0000000..6c33876
--- /dev/null
+++ b/libodb/odb/vector-traits.txx
@@ -0,0 +1,100 @@
+// file : odb/vector-traits.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ template <typename V, typename A LIBODB_VECTOR_ARG_DECL>
+ bool access::container_traits<vector<V, A LIBODB_VECTOR_ARG_USE> >::
+ changed (const container_type& c)
+ {
+ // Because modifications can cancel each other (e.g., push and pop),
+ // it is tricky to keep track of whether there are any changes in
+ // the container. Instead, we are just going to examine each element
+ // just like update().
+ //
+
+ // We should either be tracking or summarily changed.
+ //
+ if (c._tracking ())
+ {
+ const vector_impl& impl (c._impl ());
+
+ for (std::size_t i (0), n (impl.size ()); i < n; ++i)
+ {
+ if (impl.state (i) != vector_impl::state_unchanged)
+ return true;
+ }
+ }
+ else
+ return true;
+
+ return false;
+ }
+
+ template <typename V, typename A LIBODB_VECTOR_ARG_DECL>
+ void access::container_traits<vector<V, A LIBODB_VECTOR_ARG_USE> >::
+ update (const container_type& c, const functions& f)
+ {
+ bool u (false); // Updated flag.
+
+ if (c._tracking ())
+ {
+ const vector_impl& impl (c._impl ());
+
+ for (std::size_t i (0), n (impl.size ()); i < n; ++i)
+ {
+ vector_impl::element_state_type s (impl.state (i));
+
+ switch (s)
+ {
+ case vector_impl::state_unchanged:
+ {
+ break;
+ }
+ case vector_impl::state_inserted:
+ {
+ f.insert (i, c[static_cast<index_type> (i)]);
+ u = u || true;
+ break;
+ }
+ case vector_impl::state_updated:
+ {
+ f.update (i, c[static_cast<index_type> (i)]);
+ u = u || true;
+ break;
+ }
+ case vector_impl::state_erased:
+ {
+ f.delete_ (i); // Delete from i onwards.
+ u = u || true;
+ break;
+ }
+ }
+
+ // We delete all trailing elements in one go.
+ //
+ if (s == vector_impl::state_erased)
+ break;
+ }
+ }
+ else
+ {
+ // Fall back to delete all/insert all.
+ //
+ f.delete_ (0);
+
+ for (index_type i (0), n (c.size ()); i < n; ++i)
+ f.insert (i, c[i]);
+
+ u = true;
+ }
+
+ // Arm the rollback callback and (re)start change tracking.
+ //
+ if (u)
+ {
+ c._arm (transaction::current ());
+ c._start ();
+ }
+ }
+}
diff --git a/libodb/odb/vector.hxx b/libodb/odb/vector.hxx
new file mode 100644
index 0000000..3fe7d8a
--- /dev/null
+++ b/libodb/odb/vector.hxx
@@ -0,0 +1,635 @@
+// file : odb/vector.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_VECTOR_HXX
+#define ODB_VECTOR_HXX
+
+#include <odb/pre.hxx>
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#include <vector>
+#include <iterator>
+#include <cstddef> // std::ptrdiff_t
+
+#ifdef ODB_CXX11
+# include <utility> // std::move, std::forward
+# ifdef ODB_CXX11_INITIALIZER_LIST
+# include <initializer_list>
+# endif
+#endif
+
+#include <odb/vector-impl.hxx>
+
+// Because both std::vector and odb::vector are called 'vector' (who
+// cares about namespace qualifications, right?), Sun CC complains
+// with a bogus "Ambiguous partial specialization" error. A really
+// hideous workaround for this bug is to to add a dummy third template
+// argument (with a default value).
+//
+#ifdef __SUNPRO_CC
+# define LIBODB_VECTOR_ARG_DEFAULT ,int = 0
+# define LIBODB_VECTOR_ARG_DECL ,int DUMMY
+# define LIBODB_VECTOR_ARG_USE ,DUMMY
+#else
+# define LIBODB_VECTOR_ARG_DEFAULT
+# define LIBODB_VECTOR_ARG_DECL
+# define LIBODB_VECTOR_ARG_USE
+#endif
+
+namespace odb
+{
+ // An std::vector-like container that keeps track of changes.
+ //
+ // Note that the style and order of definitions is as appears
+ // in the standard.
+ //
+ template <class V, class I>
+ class vector_iterator;
+
+ template <class T, class A = std::allocator<T> LIBODB_VECTOR_ARG_DEFAULT>
+ class vector: public vector_base
+ {
+ public:
+ typedef std::vector<T, A> base_vector_type;
+ typedef typename base_vector_type::iterator base_iterator_type;
+ typedef typename base_vector_type::reverse_iterator
+ base_reverse_iterator_type;
+ // types:
+ //
+ typedef typename base_vector_type::reference reference;
+ typedef typename base_vector_type::const_reference const_reference;
+ typedef vector_iterator<vector, base_iterator_type> iterator;
+ typedef typename base_vector_type::const_iterator const_iterator;
+ typedef typename base_vector_type::size_type size_type;
+ typedef typename base_vector_type::difference_type difference_type;
+ typedef T value_type;
+ typedef A allocator_type;
+ typedef typename base_vector_type::pointer pointer;
+ typedef typename base_vector_type::const_pointer const_pointer;
+ // No non-const reverse iterator support for Sun CC with non-standard STL.
+ //
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ typedef vector_iterator<vector, base_reverse_iterator_type>
+ reverse_iterator;
+#endif
+ typedef typename base_vector_type::const_reverse_iterator
+ const_reverse_iterator;
+ // construct/copy/destroy:
+ //
+ explicit vector(const A& a = A()): v_ (a) {}
+ explicit vector(size_type n): v_ (n) {} // C++11
+ vector(size_type n, const T& v, const A& a = A()): v_ (n, v, a) {}
+ template <class I>
+ vector(I f, I l, const A& a = A()) : v_ (f, l, a) {}
+ vector(const vector& x): vector_base (x), v_ (x.v_) {}
+ // ~vector() {}
+ vector& operator=(const vector&);
+ template <class I>
+ void assign(I f, I l);
+ void assign(size_type n, const T& u);
+ allocator_type get_allocator() const /*noexcept*/
+ {return v_.get_allocator ();}
+
+#ifdef ODB_CXX11
+ vector(vector&& x) noexcept
+ : vector_base (std::move (x)), v_ (std::move (x.v_)) {}
+
+ vector(const vector& x, const A& a): vector_base (x), v_ (x.v_, a) {}
+ vector(vector&& x, const A& a)
+ : vector_base (std::move (x)), v_ (std::move (x.v_), a) {}
+
+ // Note: noexcept is not specified since it can throw while reallocating
+ // impl_.
+ //
+ vector& operator=(vector&&);
+#ifdef ODB_CXX11_INITIALIZER_LIST
+ vector(std::initializer_list<T> il, const A& a = A()): v_ (il, a) {}
+ vector& operator=(std::initializer_list<T>);
+ void assign(std::initializer_list<T>);
+#endif
+#endif
+
+ // iterators: (all /*noexcept*/)
+ //
+ iterator begin() {return iterator (this, v_.begin ());}
+ iterator end() {return iterator (this, v_.end ());}
+ const_iterator begin() const {return v_.begin ();}
+ const_iterator end() const {return v_.end ();}
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ reverse_iterator rbegin() {return reverse_iterator (this, v_.rbegin ());}
+ reverse_iterator rend() {return reverse_iterator (this, v_.rend ());}
+#endif
+ const_reverse_iterator rbegin() const {return v_.rbegin ();}
+ const_reverse_iterator rend() const {return v_.rend ();}
+
+ // Return standard vector iterators. The begin() functions mark all
+ // the elements as modified.
+ //
+ base_iterator_type mbegin ();
+ base_iterator_type mend () {return v_.end ();}
+ base_reverse_iterator_type mrbegin ();
+ base_reverse_iterator_type mrend () {return v_.rend ();}
+
+#ifdef ODB_CXX11
+ const_iterator cbegin() const {return v_.cbegin ();}
+ const_iterator cend() const {return v_.cend ();}
+ const_reverse_iterator crbegin() const {return v_.crbegin ();}
+ const_reverse_iterator crend() const {return v_.crend ();}
+#endif
+
+ // capacity:
+ //
+ size_type size() const /*noexcept*/ {return v_.size ();}
+ size_type max_size() const /*noexcept*/ {return v_.max_size ();}
+ void resize(size_type); // C++11
+ void resize(size_type, const T&);
+ size_type capacity() const /*noexcept*/ {return v_.capacity ();}
+ bool empty() const /*noexcept*/ {return v_.empty ();}
+ void reserve(size_type);
+
+#ifdef ODB_CXX11
+ void shrink_to_fit();
+#endif
+
+ // element access:
+ //
+ //reference operator[](size_type n);
+ reference modify(size_type n);
+ const_reference operator[](size_type n) const {return v_[n];}
+ //reference at(size_type n);
+ reference modify_at(size_type n);
+ const_reference at(size_type n) const {return v_.at (n);}
+ //reference front();
+ reference modify_front();
+ const_reference front() const {return v_.front ();}
+ //reference back();
+ reference modify_back();
+ const_reference back() const {return v_.back ();}
+
+ // data access:
+ //
+#ifdef ODB_CXX11
+ //T* data() noexcept;
+ T* modify_data() /*noexcept*/;
+ const T* data() const /*noexcept*/ {return v_.data ();}
+#endif
+
+ // modifiers:
+ //
+ void push_back(const T& x);
+ void pop_back();
+ iterator insert(iterator position, const T& x);
+ void insert(iterator position, size_type n, const T& x);
+ template <class I>
+ void insert(iterator position, I first, I last);
+ iterator erase(iterator position);
+ iterator erase(iterator first, iterator last);
+ void swap(vector&);
+ void clear() /*noexcept*/;
+
+#ifdef ODB_CXX11
+ // In C++11 all modifiers use const_iterator instead of iterator
+ // to represent position. However, some standard libraries (notably
+ // GCC's) still use iterator and so we will do that as well, for now.
+ //
+ void push_back(T&& x);
+ iterator insert(iterator position, T&& x);
+
+#ifdef ODB_CXX11_VARIADIC_TEMPLATE
+ template <class... Args>
+ void emplace_back(Args&&... args);
+ template <class... Args>
+ iterator emplace(iterator position, Args&&... args);
+#endif
+#endif
+
+ // Interfacing with the base vector.
+ //
+ vector (const base_vector_type& x): v_ (x) {}
+ vector& operator= (const base_vector_type&);
+ operator const base_vector_type& () const {return v_;}
+ base_vector_type& base () {return v_;}
+ const base_vector_type& base () const {return v_;}
+
+#ifdef ODB_CXX11
+ vector (base_vector_type&& x): v_ (std::move (x)) {}
+ vector& operator= (base_vector_type&&);
+#endif
+
+ // Change tracking (the rest comes from vector_base).
+ //
+ public:
+ void
+ _start () const {impl_.start (v_.size ());}
+
+ private:
+ base_vector_type v_;
+ };
+
+ namespace core
+ {
+ using odb::vector;
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator==(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x.base () == y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator==(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const std::vector<T,A>& y)
+ {return x.base () == y;}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator==(const std::vector<T,A>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x == y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator< (const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x.base () < y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator<(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const std::vector<T,A>& y)
+ {return x.base () < y;}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator<(const std::vector<T,A>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x < y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator!=(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x.base () != y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator!=(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const std::vector<T,A>& y)
+ {return x.base () != y;}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator!=(const std::vector<T,A>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x != y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator> (const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x.base () > y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator>=(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x.base () >= y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator>=(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const std::vector<T,A>& y)
+ {return x.base () >= y;}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator>=(const std::vector<T,A>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x >= y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator<=(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x.base () <= y.base ();}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator<=(const vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ const std::vector<T,A>& y)
+ {return x.base () <= y;}
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline bool operator<=(const std::vector<T,A>& x,
+ const vector<T,A LIBODB_VECTOR_ARG_USE>& y)
+ {return x <= y.base ();}
+
+ template <class V, class I>
+ class vector_iterator
+ {
+ public:
+ typedef V vector_type;
+ typedef I base_iterator_type;
+ typedef typename vector_type::const_iterator const_iterator_type;
+
+ // Sun CC with non-standard STL does not have iterator_traits.
+ //
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ typedef std::iterator_traits<base_iterator_type> base_iterator_traits;
+
+ typedef typename base_iterator_traits::value_type value_type;
+ typedef typename base_iterator_traits::difference_type difference_type;
+ typedef typename base_iterator_traits::pointer pointer;
+ typedef typename base_iterator_traits::reference reference;
+ typedef typename base_iterator_traits::iterator_category iterator_category;
+#else
+ // Base iterator is just a pointer.
+ //
+ typedef typename vector_type::value_type value_type;
+ typedef typename vector_type::pointer pointer;
+ typedef typename vector_type::reference reference;
+ typedef std::random_access_iterator_tag iterator_category;
+ typedef std::ptrdiff_t difference_type;
+#endif
+
+ typedef typename vector_type::size_type size_type;
+ typedef typename vector_type::const_reference const_reference;
+ typedef typename vector_type::const_pointer const_pointer;
+
+ vector_iterator (): v_ (0), i_ () {}
+ vector_iterator (vector_type* v, const base_iterator_type& i)
+ : v_ (v), i_ (i) {}
+ operator const_iterator_type () const {return i_;}
+ base_iterator_type base () const {return i_;}
+ vector_type* vector () const {return v_;}
+
+ // Note: const_{reference,pointer}.
+ //
+ const_reference operator* () const {return *i_;}
+ const_pointer operator-> () const {return i_.operator -> ();}
+ const_reference operator[] (difference_type n) const {return i_[n];}
+
+ // Modifiers.
+ //
+ // Buggy Sun CC cannot have them out of class.
+ //
+ reference modify () const
+ {
+ if (v_->_tracking ())
+ v_->_impl ().modify (
+ static_cast<size_type> (i_ - v_->base ().begin ()));
+ return *i_;
+ }
+
+ reference modify (difference_type n) const
+ {
+ if (v_->_tracking ())
+ v_->_impl ().modify (
+ static_cast<size_type> (i_ - v_->base ().begin () + n));
+ return i_[n];
+ }
+
+ vector_iterator& operator++ () {++i_; return *this;}
+ vector_iterator operator++ (int) {return vector_iterator (v_, i_++);}
+ vector_iterator& operator-- () {--i_; return *this;}
+ vector_iterator operator-- (int) {return vector_iterator (v_, i_--);}
+
+ vector_iterator operator+ (difference_type n) const
+ {return vector_iterator (v_, i_ + n);}
+ vector_iterator& operator+= (difference_type n) {i_ += n; return *this;}
+ vector_iterator operator- (difference_type n) const
+ {return vector_iterator (v_, i_ - n);}
+ vector_iterator& operator-= (difference_type n) {i_ -= n; return *this;}
+
+ // Implementation details.
+ //
+ public:
+ base_iterator_type _base () const {return i_;} // Same as base ().
+
+ private:
+ vector_type* v_;
+ base_iterator_type i_;
+ };
+
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ template <class V, class J>
+ class vector_iterator<V, std::reverse_iterator<J> >
+ {
+ public:
+ typedef V vector_type;
+ typedef std::reverse_iterator<J> base_iterator_type;
+ typedef typename vector_type::const_reverse_iterator const_iterator_type;
+ typedef std::iterator_traits<base_iterator_type> base_iterator_traits;
+
+ typedef typename vector_type::iterator iterator_type;
+ typedef typename base_iterator_traits::value_type value_type;
+ typedef typename base_iterator_traits::difference_type difference_type;
+ typedef typename base_iterator_traits::pointer pointer;
+ typedef typename base_iterator_traits::reference reference;
+ typedef typename base_iterator_traits::iterator_category iterator_category;
+
+ typedef typename vector_type::size_type size_type;
+ typedef typename vector_type::const_reference const_reference;
+ typedef typename vector_type::const_pointer const_pointer;
+
+ vector_iterator (): v_ (0), i_ () {}
+ explicit vector_iterator (const iterator_type& i)
+ : v_ (i.vector ()), i_ (i.base ()) {}
+ vector_iterator (vector_type* v, const base_iterator_type& i)
+ : v_ (v), i_ (i) {}
+ operator const_iterator_type () const {return i_;}
+ iterator_type base () const {return iterator_type (v_, i_.base ());}
+ base_iterator_type rbase () const {return i_;}
+ vector_type* vector () const {return v_;}
+
+ // Note: const_{reference,pointer}.
+ //
+ const_reference operator* () const {return *i_;}
+ const_pointer operator-> () const {return i_.operator -> ();}
+ const_reference operator[] (difference_type n) const {return i_[n];}
+
+ // Modifiers.
+ //
+ reference modify () const
+ {
+ if (v_->_tracking ())
+ v_->_impl ().modify (
+ static_cast<size_type> (v_->base ().rend () - i_ - 1));
+ return *i_;
+ }
+
+ reference modify (difference_type n) const
+ {
+ if (v_->_tracking ())
+ // Note: going in the opposite direction.
+ v_->_impl ().modify (
+ static_cast<size_type> (v_->base ().rend () - i_ - 1 - n));
+ return i_[n];
+ }
+
+ vector_iterator& operator++ () {++i_; return *this;}
+ vector_iterator operator++ (int) {return vector_iterator (v_, i_++);}
+ vector_iterator& operator-- () {--i_; return *this;}
+ vector_iterator operator-- (int) {return vector_iterator (v_, i_--);}
+
+ vector_iterator operator+ (difference_type n) const
+ {return vector_iterator (v_, i_ + n);}
+ vector_iterator& operator+= (difference_type n) {i_ += n; return *this;}
+ vector_iterator operator- (difference_type n) const
+ {return vector_iterator (v_, i_ - n);}
+ vector_iterator& operator-= (difference_type n) {i_ -= n; return *this;}
+
+ // Implementation details.
+ //
+ public:
+ base_iterator_type _base () const {return i_;} // Same as rbase().
+
+ private:
+ vector_type* v_;
+ base_iterator_type i_;
+ };
+#endif // _RWSTD_NO_CLASS_PARTIAL_SPEC
+
+ // operator==
+ //
+ template <class V, class I>
+ inline bool
+ operator== (const vector_iterator<V, I>& x, const vector_iterator<V, I>& y)
+ {return x._base () == y._base ();}
+
+ template <class V, class I>
+ inline bool
+ operator== (const vector_iterator<V, I>& x,
+ const typename vector_iterator<V, I>::const_iterator_type& y)
+ {return x._base () == y;}
+
+ template <class V, class I>
+ inline bool
+ operator== (const typename vector_iterator<V, I>::const_iterator_type& x,
+ const vector_iterator<V, I>& y)
+ {return x == y._base ();}
+
+ // operator<
+ //
+ template <class V, class I>
+ inline bool
+ operator< (const vector_iterator<V, I>& x, const vector_iterator<V, I>& y)
+ {return x._base () < y._base ();}
+
+ template <class V, class I>
+ inline bool
+ operator< (const vector_iterator<V, I>& x,
+ const typename vector_iterator<V, I>::const_iterator_type& y)
+ {return x._base () < y;}
+
+ template <class V, class I>
+ inline bool
+ operator< (const typename vector_iterator<V, I>::const_iterator_type& x,
+ const vector_iterator<V, I>& y)
+ {return x < y._base ();}
+
+ // operator!=
+ //
+ template <class V, class I>
+ inline bool
+ operator!= (const vector_iterator<V, I>& x, const vector_iterator<V, I>& y)
+ {return x._base () != y._base ();}
+
+ template <class V, class I>
+ inline bool
+ operator!= (const vector_iterator<V, I>& x,
+ const typename vector_iterator<V, I>::const_iterator_type& y)
+ {return x._base () != y;}
+
+ template <class V, class I>
+ inline bool
+ operator!= (const typename vector_iterator<V, I>::const_iterator_type& x,
+ const vector_iterator<V, I>& y)
+ {return x != y._base ();}
+
+ // operator>
+ //
+ template <class V, class I>
+ inline bool
+ operator> (const vector_iterator<V, I>& x, const vector_iterator<V, I>& y)
+ {return x._base () > y._base ();}
+
+ template <class V, class I>
+ inline bool
+ operator> (const vector_iterator<V, I>& x,
+ const typename vector_iterator<V, I>::const_iterator_type& y)
+ {return x._base () > y;}
+
+ template <class V, class I>
+ inline bool
+ operator> (const typename vector_iterator<V, I>::const_iterator_type& x,
+ const vector_iterator<V, I>& y)
+ {return x > y._base ();}
+
+ // operator>=
+ //
+ template <class V, class I>
+ inline bool
+ operator>= (const vector_iterator<V, I>& x, const vector_iterator<V, I>& y)
+ {return x._base () >= y._base ();}
+
+ template <class V, class I>
+ inline bool
+ operator>= (const vector_iterator<V, I>& x,
+ const typename vector_iterator<V, I>::const_iterator_type& y)
+ {return x._base () >= y;}
+
+ template <class V, class I>
+ inline bool
+ operator>= (const typename vector_iterator<V, I>::const_iterator_type& x,
+ const vector_iterator<V, I>& y)
+ {return x >= y._base ();}
+
+ // operator<=
+ //
+ template <class V, class I>
+ inline bool
+ operator<= (const vector_iterator<V, I>& x, const vector_iterator<V, I>& y)
+ {return x._base () <= y._base ();}
+
+ template <class V, class I>
+ inline bool
+ operator<= (const vector_iterator<V, I>& x,
+ const typename vector_iterator<V, I>::const_iterator_type& y)
+ {return x._base () <= y;}
+
+ template <class V, class I>
+ inline bool
+ operator<= (const typename vector_iterator<V, I>::const_iterator_type& x,
+ const vector_iterator<V, I>& y)
+ {return x <= y._base ();}
+
+ // operator-
+ //
+ template <class V, class I>
+ inline typename vector_iterator<V, I>::difference_type
+ operator-(const vector_iterator<V, I>& x, const vector_iterator<V, I>& y)
+ {return x._base () - y._base ();}
+
+ template <class V, class I>
+ inline typename vector_iterator<V, I>::difference_type
+ operator-(const vector_iterator<V, I>& x,
+ const typename vector_iterator<V, I>::const_iterator_type& y)
+ {return x._base () - y;}
+
+ template <class V, class I>
+ inline typename vector_iterator<V, I>::difference_type
+ operator-(const typename vector_iterator<V, I>::const_iterator_type& x,
+ const vector_iterator<V, I>& y)
+ {return x - y._base ();}
+
+ // operator+
+ //
+ template <class V, class I>
+ inline vector_iterator<V, I>
+ operator+(typename vector_iterator<V, I>::difference_type n,
+ const vector_iterator<V, I>& x)
+ {return vector_iterator<V, I> (x.vector (), n + x._base ());}
+}
+
+namespace std
+{
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void swap(odb::vector<T,A LIBODB_VECTOR_ARG_USE>& x,
+ odb::vector<T,A LIBODB_VECTOR_ARG_USE>& y) {x.swap (y);}
+}
+
+#include <odb/vector.ixx>
+
+#include <odb/vector-traits.hxx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_VECTOR_HXX
diff --git a/libodb/odb/vector.ixx b/libodb/odb/vector.ixx
new file mode 100644
index 0000000..230b187
--- /dev/null
+++ b/libodb/odb/vector.ixx
@@ -0,0 +1,359 @@
+// file : odb/vector.ixx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ // construct/copy/destroy:
+ //
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline vector<T, A LIBODB_VECTOR_ARG_USE>&
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ operator= (const vector& x)
+ {
+ v_ = x.v_;
+ if (_tracking ())
+ impl_.assign (v_.size ());
+ return *this;
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ template <class I>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ assign (I f, I l)
+ {
+ v_.assign (f, l);
+ if (_tracking ())
+ impl_.assign (v_.size ());
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ assign (size_type n, const T& u)
+ {
+ v_.assign (n, u);
+ if (_tracking ())
+ impl_.assign (n);
+ }
+
+#ifdef ODB_CXX11
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline vector<T, A LIBODB_VECTOR_ARG_USE>&
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ operator=(vector&& x)
+ {
+ v_ = std::move (x.v_);
+ if (_tracking ())
+ impl_.assign (v_.size ());
+ return *this;
+ }
+
+#ifdef ODB_CXX11_INITIALIZER_LIST
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline vector<T, A LIBODB_VECTOR_ARG_USE>&
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ operator= (std::initializer_list<T> il)
+ {
+ v_ = il;
+ if (_tracking ())
+ impl_.assign (v_.size ());
+ return *this;
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ assign (std::initializer_list<T> il)
+ {
+ v_.assign (il);
+ if (_tracking ())
+ impl_.assign (v_.size ());
+ }
+#endif
+#endif
+
+ // iterators:
+ //
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::base_iterator_type
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ mbegin ()
+ {
+ if (_tracking ())
+ impl_.modify (0, v_.size ());
+ return v_.begin ();
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline
+ typename vector<T, A LIBODB_VECTOR_ARG_USE>::base_reverse_iterator_type
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ mrbegin ()
+ {
+ if (_tracking ())
+ impl_.modify (0, v_.size ());
+ return v_.rbegin ();
+ }
+
+ // capacity:
+ //
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ resize (size_type n)
+ {
+ v_.resize (n);
+ if (_tracking ())
+ impl_.resize (n);
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ resize (size_type n, const T& c)
+ {
+ v_.resize (n, c);
+ if (_tracking ())
+ impl_.resize (n);
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ reserve (size_type n)
+ {
+ v_.reserve (n);
+ if (_tracking ())
+ impl_.reserve (n);
+ }
+
+#ifdef ODB_CXX11
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ shrink_to_fit ()
+ {
+ v_.shrink_to_fit ();
+ impl_.shrink_to_fit ();
+ }
+#endif
+
+ // element access:
+ //
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::reference
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ modify (size_type n)
+ {
+ reference r (v_[n]);
+ if (_tracking ())
+ impl_.modify (n);
+ return r;
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::reference
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ modify_at (size_type n)
+ {
+ reference r (v_.at (n));
+ if (_tracking ())
+ impl_.modify (n);
+ return r;
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::reference
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ modify_front ()
+ {
+ reference r (v_.front ());
+ if (_tracking ())
+ impl_.modify (0);
+ return r;
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::reference
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ modify_back ()
+ {
+ reference r (v_.back ());
+ if (_tracking ())
+ impl_.modify (v_.size () - 1);
+ return r;
+ }
+
+#ifdef ODB_CXX11
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline T* vector<T, A LIBODB_VECTOR_ARG_USE>::
+ modify_data() /*noexcept*/
+ {
+ if (_tracking ())
+ impl_.modify (0, v_.size ());
+ return v_.data ();
+ }
+#endif
+
+ // modifiers:
+ //
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ push_back (const T& x)
+ {
+ v_.push_back (x);
+ if (_tracking ())
+ impl_.push_back ();
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ pop_back ()
+ {
+ v_.pop_back ();
+ if (_tracking ())
+ impl_.pop_back ();
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::iterator
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ insert (iterator p, const T& x)
+ {
+ if (_tracking ())
+ impl_.insert (static_cast<size_type> (p.base () - v_.begin ()));
+ return iterator (this, v_.insert (p.base (), x));
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ insert (iterator p, size_type n, const T& x)
+ {
+ if (_tracking ())
+ impl_.insert (static_cast<size_type> (p.base () - v_.begin ()), n);
+ v_.insert (p.base (), n, x);
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ template <class I>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ insert (iterator p, I f, I l)
+ {
+ size_type i, n;
+ if (_tracking ())
+ {
+ i = static_cast<size_type> (p.base () - v_.begin ());
+ n = v_.size ();
+ }
+
+ v_.insert (p.base (), f, l);
+
+ if (_tracking ())
+ impl_.insert (i, v_.size () - n);
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::iterator
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ erase (iterator p)
+ {
+ if (_tracking ())
+ impl_.erase (static_cast<size_type> (p.base () - v_.begin ()));
+ return iterator (this, v_.erase (p.base ()));
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::iterator
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ erase (iterator f, iterator l)
+ {
+ if (_tracking ())
+ impl_.erase (static_cast<size_type> (f.base () - v_.begin ()),
+ static_cast<size_type> (l - f));
+ return iterator (this, v_.erase (f.base (), l.base ()));
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ swap (vector& x)
+ {
+ v_.swap (x.v_);
+ vector_base::swap (x);
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ clear ()
+ {
+ v_.clear ();
+ if (_tracking ())
+ impl_.clear ();
+ }
+
+#ifdef ODB_CXX11
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ push_back(T&& x)
+ {
+ v_.push_back (std::move (x));
+ if (_tracking ())
+ impl_.push_back ();
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::iterator
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ insert (iterator p, T&& x)
+ {
+ base_iterator_type r (v_.insert (p.base (), std::move (x)));
+ if (_tracking ())
+ impl_.insert (static_cast<size_type> (r - v_.begin ()));
+ return iterator (this, r);
+ }
+
+#ifdef ODB_CXX11_VARIADIC_TEMPLATE
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ template <class... Args>
+ inline void vector<T, A LIBODB_VECTOR_ARG_USE>::
+ emplace_back (Args&&... args)
+ {
+ v_.push_back (std::forward<Args> (args)...);
+ if (_tracking ())
+ impl_.push_back ();
+ }
+
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ template <class... Args>
+ inline typename vector<T, A LIBODB_VECTOR_ARG_USE>::iterator
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ emplace (iterator p, Args&&... args)
+ {
+ base_iterator_type r (
+ v_.emplace (p.base (), std::forward<Args> (args)...));
+ if (_tracking ())
+ impl_.insert (static_cast<size_type> (r - v_.begin ()));
+ return iterator (this, r);
+ }
+#endif
+#endif
+
+ // Interfacing with base vector.
+ //
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline vector<T, A LIBODB_VECTOR_ARG_USE>&
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ operator= (const base_vector_type& x)
+ {
+ v_ = x;
+ if (_tracking ())
+ impl_.assign (v_.size ());
+ return *this;
+ }
+
+#ifdef ODB_CXX11
+ template <class T, class A LIBODB_VECTOR_ARG_DECL>
+ inline vector<T, A LIBODB_VECTOR_ARG_USE>&
+ vector<T, A LIBODB_VECTOR_ARG_USE>::
+ operator= (base_vector_type&& x)
+ {
+ v_ = std::move (x);
+ if (_tracking ())
+ impl_.assign (v_.size ());
+ return *this;
+ }
+#endif
+}
diff --git a/libodb/odb/version-build2-stub.hxx b/libodb/odb/version-build2-stub.hxx
new file mode 100644
index 0000000..f4c7937
--- /dev/null
+++ b/libodb/odb/version-build2-stub.hxx
@@ -0,0 +1,4 @@
+// file : odb/version-build2-stub.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/version.hxx>
diff --git a/libodb/odb/version-build2.hxx b/libodb/odb/version-build2.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libodb/odb/version-build2.hxx
diff --git a/libodb/odb/version-build2.hxx.in b/libodb/odb/version-build2.hxx.in
new file mode 100644
index 0000000..3672585
--- /dev/null
+++ b/libodb/odb/version-build2.hxx.in
@@ -0,0 +1,42 @@
+// file : odb/version-build2.hxx.in
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef LIBODB_VERSION // Note: using the version macro itself.
+
+// For the ODB compiler (temporary).
+//
+#define ODB_VERSION 20476
+
+// 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_VERSION $libodb.version.project_number$ULL
+#define LIBODB_VERSION_STR "$libodb.version.project$"
+#define LIBODB_VERSION_ID "$libodb.version.project_id$"
+
+#define LIBODB_VERSION_MAJOR $libodb.version.major$
+#define LIBODB_VERSION_MINOR $libodb.version.minor$
+#define LIBODB_VERSION_PATCH $libodb.version.patch$
+
+#define LIBODB_PRE_RELEASE $libodb.version.pre_release$
+
+#define LIBODB_SNAPSHOT $libodb.version.snapshot_sn$ULL
+#define LIBODB_SNAPSHOT_ID "$libodb.version.snapshot_id$"
+
+#endif // LIBODB_VERSION
diff --git a/libodb/odb/version.hxx b/libodb/odb/version.hxx
new file mode 100644
index 0000000..4961e1a
--- /dev/null
+++ b/libodb/odb/version.hxx
@@ -0,0 +1,43 @@
+// file : odb/version.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifdef LIBODB_BUILD2
+# include <odb/version-build2.hxx>
+#else
+
+#ifndef ODB_VERSION_HXX
+#define ODB_VERSION_HXX
+
+#include <odb/pre.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
+//
+
+// ODB interface version: minor, major, and alpha/beta versions.
+//
+#define ODB_VERSION 20476
+#define ODB_VERSION_STR "2.5-b.26"
+
+// libodb version: interface version plus the bugfix version.
+//
+#define LIBODB_VERSION 2049976
+#define LIBODB_VERSION_STR "2.5.0-b.26"
+
+#include <odb/post.hxx>
+
+#endif // ODB_VERSION_HXX
+#endif // LIBODB_BUILD2
diff --git a/libodb/odb/view-image.hxx b/libodb/odb/view-image.hxx
new file mode 100644
index 0000000..51f7cc0
--- /dev/null
+++ b/libodb/odb/view-image.hxx
@@ -0,0 +1,36 @@
+// file : odb/view-image.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_VIEW_IMAGE_HXX
+#define ODB_VIEW_IMAGE_HXX
+
+#include <odb/pre.hxx>
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+
+namespace odb
+{
+ // Helper to create a complete image chain for a polymorphic
+ // object hierarchy.
+ //
+ template <typename D, typename R, database_id DB>
+ struct view_object_image: object_traits_impl<D, DB>::image_type
+ {
+ view_object_image () {this->base = &base_;}
+
+ private:
+ // Data member names in the generated image_type never end with
+ // an underscore, so this name shouldn't clash.
+ //
+ view_object_image<typename object_traits_impl<D, DB>::base_type, R, DB>
+ base_;
+ };
+
+ template <typename R, database_id DB>
+ struct view_object_image<R, R, DB>: object_traits_impl<R, DB>::image_type {};
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_VIEW_IMAGE_HXX
diff --git a/libodb/odb/view-result.hxx b/libodb/odb/view-result.hxx
new file mode 100644
index 0000000..601c3b4
--- /dev/null
+++ b/libodb/odb/view-result.hxx
@@ -0,0 +1,231 @@
+// file : odb/view-result.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_VIEW_RESULT_HXX
+#define ODB_VIEW_RESULT_HXX
+
+#include <odb/pre.hxx>
+
+#include <cstddef> // std::ptrdiff_t, std::size_t
+#include <iterator> // iterator categories
+#include <utility> // std::move
+
+#include <odb/forward.hxx>
+#include <odb/traits.hxx>
+#include <odb/result.hxx>
+#include <odb/pointer-traits.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+namespace odb
+{
+ template <typename T>
+ class view_result_impl: public result_impl
+ {
+ protected:
+ friend class result<T>;
+ friend class result<const T>;
+ friend class result_iterator<T, class_view>;
+ friend class result_iterator<const T, class_view>;
+
+ // In result_impl, T is always non-const and the same as view_type.
+ //
+ typedef T view_type;
+ typedef odb::view_traits<view_type> view_traits;
+
+ typedef typename view_traits::pointer_type pointer_type;
+ typedef odb::pointer_traits<pointer_type> pointer_traits;
+
+ view_result_impl (odb::connection& conn)
+ : result_impl (conn), begin_ (true), end_ (false), current_ ()
+ {
+ }
+
+ // To make this work with all kinds of pointers (raw, std::auto_ptr,
+ // shared), we need to make sure we don't make any copies of the
+ // pointer on the return path.
+ //
+ pointer_type&
+ current ();
+
+ void
+ release ()
+ {
+ current_ = pointer_type ();
+ guard_.release ();
+ }
+
+ void
+ begin ()
+ {
+ if (begin_)
+ {
+ next ();
+ begin_ = false;
+ }
+ }
+
+ bool
+ end () const
+ {
+ return end_;
+ }
+
+ protected:
+ virtual void
+ load (view_type&) = 0;
+
+ virtual void
+ next () = 0;
+
+ virtual void
+ cache () = 0;
+
+ virtual std::size_t
+ size () = 0;
+
+ protected:
+#ifdef ODB_CXX11
+ void
+ current (pointer_type& p)
+ {
+ current_ = std::move (p);
+ guard_.reset (current_);
+ }
+
+ void
+ current (pointer_type&& p)
+ {
+ current (p);
+ }
+#else
+ void
+ current (pointer_type p)
+ {
+ current_ = p;
+ guard_.reset (current_);
+ }
+#endif
+
+ bool begin_;
+ bool end_;
+
+ private:
+ pointer_type current_;
+ typename pointer_traits::guard guard_;
+ };
+
+ template <typename T>
+ class result_iterator<T, class_view>
+ {
+ public:
+ typedef T value_type;
+ typedef value_type& reference;
+ typedef value_type* pointer;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::input_iterator_tag iterator_category;
+
+ // T can be const T while view_type is always non-const.
+ //
+ typedef typename view_traits<T>::view_type view_type;
+
+ typedef view_result_impl<view_type> result_impl_type;
+
+ public:
+ explicit
+ result_iterator (result_impl_type* res = 0)
+ : res_ (res)
+ {
+ }
+
+ // Input iterator requirements.
+ //
+ public:
+ reference
+ operator* () const
+ {
+ return pointer_traits::get_ref (res_->current ());
+ }
+
+ // Our value_type is already a pointer so return it instead of
+ // a pointer to it (operator-> will just have to go one deeper
+ // in the latter case).
+ //
+ pointer
+ operator-> () const
+ {
+ return pointer_traits::get_ptr (res_->current ());
+ }
+
+ result_iterator&
+ operator++ ()
+ {
+ res_->next ();
+ return *this;
+ }
+
+ result_iterator
+ operator++ (int)
+ {
+ // All non-end iterators for a result object move together.
+ //
+ res_->next ();
+ return *this;
+ }
+
+ public:
+ typedef typename view_traits<T>::pointer_type pointer_type;
+
+ pointer_type
+ load ()
+ {
+#ifdef ODB_CXX11
+ pointer_type r (std::move (res_->current ()));
+#else
+ pointer_type r (res_->current ());
+#endif
+ res_->release ();
+ return r;
+ }
+
+ void
+ load (view_type&);
+
+ public:
+ bool
+ equal (result_iterator j) const
+ {
+ return (res_ ? res_->end () : true) == (j.res_ ? j.res_->end () : true);
+ }
+
+ private:
+ // Use unrestricted pointer traits since that's what is returned by
+ // result_impl.
+ //
+ typedef
+ odb::pointer_traits<typename view_traits<view_type>::pointer_type>
+ pointer_traits;
+
+ result_impl_type* res_;
+ };
+
+ //
+ //
+ template <typename T>
+ class result_base<T, class_view>
+ {
+ public:
+ typedef typename view_traits<T>::pointer_type value_type;
+
+ // T can be const T while view_type is always non-const.
+ //
+ typedef typename view_traits<T>::view_type view_type;
+ typedef view_result_impl<view_type> result_impl_type;
+ };
+}
+
+#include <odb/view-result.txx>
+
+#include <odb/post.hxx>
+
+#endif // ODB_VIEW_RESULT_HXX
diff --git a/libodb/odb/view-result.txx b/libodb/odb/view-result.txx
new file mode 100644
index 0000000..5c62253
--- /dev/null
+++ b/libodb/odb/view-result.txx
@@ -0,0 +1,39 @@
+// file : odb/view-result.txx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+namespace odb
+{
+ //
+ // view_result_impl
+ //
+
+ template <typename T>
+ typename view_result_impl<T>::pointer_type&
+ view_result_impl<T>::
+ current ()
+ {
+ if (pointer_traits::null_ptr (current_) && !end_)
+ {
+ pointer_type p (view_traits::create ());
+ view_type& view (pointer_traits::get_ref (p));
+ current (p);
+ load (view);
+ }
+
+ return current_;
+ }
+
+ //
+ // result_iterator
+ //
+
+ template <typename T>
+ void result_iterator<T, class_view>::
+ load (view_type& view)
+ {
+ if (res_->end ())
+ return;
+
+ res_->load (view);
+ }
+}
diff --git a/libodb/odb/wrapper-traits.hxx b/libodb/odb/wrapper-traits.hxx
new file mode 100644
index 0000000..d31425d
--- /dev/null
+++ b/libodb/odb/wrapper-traits.hxx
@@ -0,0 +1,276 @@
+// file : odb/wrapper-traits.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_WRAPPER_TRAITS_HXX
+#define ODB_WRAPPER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <memory> // std::auto_ptr, std::unique_ptr, std::shared_ptr/weak_ptr
+
+#include <odb/nullable.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+#include <odb/details/meta/remove-const.hxx>
+
+namespace odb
+{
+ template <typename T>
+ class wrapper_traits;
+
+ // Sample specialization for raw pointers. It is not enabled by default
+ // since it makes many assumptions that may not always hold true (such
+ // as that instances are allocated with new and freed with delete).
+ // This makes it too dangerous to be enabled unconditionally. If you
+ // need this functionality, you can copy the below code into your
+ // application. Also consider changing it to only specialize for
+ // specific types instead of for any pointer (it will almost always
+ // do the wrong thing for char*).
+ //
+#if 0
+ template <typename T>
+ class wrapper_traits<T*>
+ {
+ public:
+ typedef T wrapped_type;
+ typedef T* wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = false;
+
+ static bool
+ get_null (const wrapper_type& p)
+ {
+ return p == 0;
+ }
+
+ static void
+ set_null (wrapper_type& p)
+ {
+ delete p;
+ p = 0;
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& p)
+ {
+ return *p;
+ }
+
+ static unrestricted_wrapped_type&
+ set_ref (wrapper_type& p)
+ {
+ if (p == 0)
+ p = new unrestricted_wrapped_type;
+
+ return const_cast<unrestricted_wrapped_type&> (*p);
+ }
+ };
+#endif
+
+ // Specialization for std::auto_ptr.
+ //
+#ifndef ODB_CXX11
+ template <typename T>
+ class wrapper_traits< std::auto_ptr<T> >
+ {
+ public:
+ // T can be const.
+ //
+ typedef T wrapped_type;
+ typedef std::auto_ptr<T> wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename odb::details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = false;
+
+ static bool
+ get_null (const wrapper_type& p)
+ {
+ return p.get () == 0;
+ }
+
+ static void
+ set_null (wrapper_type& p)
+ {
+ p.reset ();
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& p)
+ {
+ return *p;
+ }
+
+ static unrestricted_wrapped_type&
+ set_ref (wrapper_type& p)
+ {
+ if (p.get () == 0)
+ p.reset (new unrestricted_wrapped_type ());
+
+ return const_cast<unrestricted_wrapped_type&> (*p);
+ }
+ };
+#endif
+
+#ifdef ODB_CXX11
+
+ // Specialization for C++11 std::unique_ptr.
+ //
+ template <typename T, typename D>
+ class wrapper_traits<std::unique_ptr<T, D>>
+ {
+ public:
+ // T can be const.
+ //
+ typedef T wrapped_type;
+ typedef std::unique_ptr<T, D> wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename odb::details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = false;
+
+ static bool
+ get_null (const wrapper_type& p)
+ {
+ return !p;
+ }
+
+ static void
+ set_null (wrapper_type& p)
+ {
+ p.reset ();
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& p)
+ {
+ return *p;
+ }
+
+ static unrestricted_wrapped_type&
+ set_ref (wrapper_type& p)
+ {
+ if (!p)
+ p.reset (new unrestricted_wrapped_type ());
+
+ return const_cast<unrestricted_wrapped_type&> (*p);
+ }
+ };
+
+ // Specialization for C++11 std::shared_ptr.
+ //
+ template <typename T>
+ class wrapper_traits<std::shared_ptr<T>>
+ {
+ public:
+ typedef T wrapped_type;
+ typedef std::shared_ptr<T> wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename odb::details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = false;
+
+ static bool
+ get_null (const wrapper_type& p)
+ {
+ return !p;
+ }
+
+ static void
+ set_null (wrapper_type& p)
+ {
+ p.reset ();
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& p)
+ {
+ return *p;
+ }
+
+ static unrestricted_wrapped_type&
+ set_ref (wrapper_type& p)
+ {
+ if (!p)
+ p.reset (new unrestricted_wrapped_type);
+
+ return const_cast<unrestricted_wrapped_type&> (*p);
+ }
+ };
+
+#endif // ODB_CXX11
+
+ // Specialization for odb::nullable.
+ //
+ template <typename T>
+ class wrapper_traits< nullable<T> >
+ {
+ public:
+ // T can be const.
+ //
+ typedef T wrapped_type;
+ typedef nullable<T> wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename odb::details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = true;
+
+ static bool
+ get_null (const wrapper_type& n)
+ {
+ return n.null ();
+ }
+
+ static void
+ set_null (wrapper_type& n)
+ {
+ n.reset ();
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& n)
+ {
+ return *n;
+ }
+
+ static unrestricted_wrapped_type&
+ set_ref (wrapper_type& n)
+ {
+ if (n.null ())
+ n = unrestricted_wrapped_type ();
+
+ return const_cast<unrestricted_wrapped_type&> (*n);
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_WRAPPER_TRAITS_HXX
diff --git a/libodb/tests/.gitignore b/libodb/tests/.gitignore
new file mode 100644
index 0000000..e54525b
--- /dev/null
+++ b/libodb/tests/.gitignore
@@ -0,0 +1 @@
+driver
diff --git a/libodb/tests/basics/buildfile b/libodb/tests/basics/buildfile
new file mode 100644
index 0000000..d568216
--- /dev/null
+++ b/libodb/tests/basics/buildfile
@@ -0,0 +1,6 @@
+# file : tests/basics/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+import libs = libodb%lib{odb}
+
+exe{driver}: {hxx cxx}{*} $libs
diff --git a/libodb/tests/basics/driver.cxx b/libodb/tests/basics/driver.cxx
new file mode 100644
index 0000000..57cec1c
--- /dev/null
+++ b/libodb/tests/basics/driver.cxx
@@ -0,0 +1,29 @@
+// 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 <odb/exceptions.hxx>
+#include <odb/transaction.hxx>
+
+using namespace odb;
+
+int
+main ()
+{
+ // Transaction.
+ //
+ {
+ assert (!transaction::has_current ());
+
+ try
+ {
+ transaction::current ();
+ assert(false);
+ }
+ catch (const not_in_transaction&) {}
+ }
+}
diff --git a/libodb/tests/build/.gitignore b/libodb/tests/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libodb/tests/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libodb/tests/build/bootstrap.build b/libodb/tests/build/bootstrap.build
new file mode 100644
index 0000000..6ee38db
--- /dev/null
+++ b/libodb/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/tests/build/root.build b/libodb/tests/build/root.build
new file mode 100644
index 0000000..6c5a90b
--- /dev/null
+++ b/libodb/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/tests/buildfile b/libodb/tests/buildfile
new file mode 100644
index 0000000..57588a4
--- /dev/null
+++ b/libodb/tests/buildfile
@@ -0,0 +1,4 @@
+# file : tests/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+./: {*/ -build/}