diff options
Diffstat (limited to 'libodb')
197 files changed, 26145 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..d96b938 --- /dev/null +++ b/libodb/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2009-2024 Code Synthesis. + +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/NEWS b/libodb/NEWS new file mode 120000 index 0000000..0fae0f8 --- /dev/null +++ b/libodb/NEWS @@ -0,0 +1 @@ +../NEWS
\ No newline at end of file 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..2efd5a3 --- /dev/null +++ b/libodb/manifest @@ -0,0 +1,326 @@ +: 1 +name: libodb +version: 2.5.0-b.27 +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/odb/ +email: odb-users@codesynthesis.com +build-warning-email: odb-builds@codesynthesis.com +requires: c++11 + +# @@ TMP Bump the toolchain version to 0.17.0 after it is released. +# +depends: * build2 >= 0.16.0 +depends: * bpkg >= 0.16.0 + +#tests: odb-tests == $ ? ($config.odb_tests.libodb_test) +#examples: odb-examples == $ ? ($config.odb_examples.libodb_example) + +default-builds: all +default-build-config: +\ +{ + config.odb_tests.libodb_test=true + config.odb_tests.database=sqlite +}+ odb-tests + +{ + config.odb_examples.libodb_example=true + config.odb_examples.database=sqlite +}+ odb-examples +\ + +# Only build this package configuration where it can be tested via odb-tests +# package and where libodb-* libraries can be built (see their manifests for +# details). +# +multi-builds: all +multi-builds: -wasm ; Not supported by libodb-{mysql,pgsql}. +multi-builds: -( +windows -gcc ) ; Requires MinGW GCC. +multi-builds: &gcc ; Requires GCC with plugin support enabled. +multi-builds: &gcc-5+ ; Requires GCC 5 or later. +multi-builds: -static ; Implementation uses plugins and requires -fPIC. +multi-build-auxiliary-mysql: *-mysql_* +multi-build-auxiliary-pgsql: *-postgresql_* +multi-build-config: +\ +{ + config.odb_tests.libodb_test=true + config.odb_tests.database='mysql sqlite pgsql' + + config.odb_tests.mysql.user=$getenv(MYSQL_DATABASE_USER) + config.odb_tests.mysql.database=$getenv(MYSQL_DATABASE_NAME) + config.odb_tests.mysql.host=$getenv(MYSQL_DATABASE_HOST) + config.odb_tests.mysql.port=$getenv(MYSQL_DATABASE_PORT) + + config.odb_tests.pgsql.user=$getenv(PGSQL_DATABASE_USER) + config.odb_tests.pgsql.database=$getenv(PGSQL_DATABASE_NAME) + config.odb_tests.pgsql.host=$getenv(PGSQL_DATABASE_HOST) + config.odb_tests.pgsql.port=$getenv(PGSQL_DATABASE_PORT) +}+ odb-tests +\ + +# Complements the default configuration (see odb-{tests,examples} for +# background). +# +custom-builds: latest ; Requires latest config with GCC as host compiler. +custom-builds: -static ; Implementation uses plugins and requires -fPIC. +#custom-build-bot: -- see below. +custom-build-config: +\ +{ + config.odb_tests.libodb_test=true + config.odb_tests.database=sqlite +}+ odb-tests + +{ + config.odb_examples.libodb_example=true + config.odb_examples.database=sqlite +}+ odb-examples +\ + +# Complements the multi configuration (see odb-tests for background). +# +custom-multi-builds: latest ; Requires latest config with GCC as host compiler. +custom-multi-builds: -wasm ; Not supported by libodb-{mysql,pgsql}. +custom-multi-builds: -static ; Implementation uses plugins and requires -fPIC. +custom-multi-build-auxiliary-mysql: *-mysql_* +custom-multi-build-auxiliary-pgsql: *-postgresql_* +#custom-multi-build-bot: -- see below. +custom-multi-build-config: +\ +{ + config.odb_tests.libodb_test=true + config.odb_tests.database='mysql sqlite pgsql' + + config.odb_tests.mysql.user=$getenv(MYSQL_DATABASE_USER) + config.odb_tests.mysql.database=$getenv(MYSQL_DATABASE_NAME) + config.odb_tests.mysql.host=$getenv(MYSQL_DATABASE_HOST) + config.odb_tests.mysql.port=$getenv(MYSQL_DATABASE_PORT) + + config.odb_tests.pgsql.user=$getenv(PGSQL_DATABASE_USER) + config.odb_tests.pgsql.database=$getenv(PGSQL_DATABASE_NAME) + config.odb_tests.pgsql.host=$getenv(PGSQL_DATABASE_HOST) + config.odb_tests.pgsql.port=$getenv(PGSQL_DATABASE_PORT) +}+ odb-tests +\ + +# Complements the multi configuration (see libodb-oracle for background). +# +custom-multi-oracle-builds: latest ; Requires proprietary software. +custom-multi-oracle-builds: -wasm ; Not supported by libodb-{mysql,pgsql,oracle}. +custom-multi-oracle-builds: &( +linux &gcc ) ; Only test on Linux with main compiler. +custom-multi-oracle-builds: -static ; Implementation uses plugins and requires -fPIC. +custom-multi-oracle-build-auxiliary-mysql: *-mysql_* +custom-multi-oracle-build-auxiliary-pgsql: *-postgresql_* +custom-multi-oracle-build-auxiliary-oracle: *-oracle_* +#custom-multi-oracle-build-bot: -- see below. +custom-multi-oracle-build-config: +\ +{ + config.odb_tests.libodb_test=true + config.odb_tests.database='mysql sqlite pgsql oracle' + + config.odb_tests.mysql.user=$getenv(MYSQL_DATABASE_USER) + config.odb_tests.mysql.database=$getenv(MYSQL_DATABASE_NAME) + config.odb_tests.mysql.host=$getenv(MYSQL_DATABASE_HOST) + config.odb_tests.mysql.port=$getenv(MYSQL_DATABASE_PORT) + + config.odb_tests.pgsql.user=$getenv(PGSQL_DATABASE_USER) + config.odb_tests.pgsql.database=$getenv(PGSQL_DATABASE_NAME) + config.odb_tests.pgsql.host=$getenv(PGSQL_DATABASE_HOST) + config.odb_tests.pgsql.port=$getenv(PGSQL_DATABASE_PORT) + + config.odb_tests.oracle.user=$getenv(ORACLE_DATABASE_USER) + config.odb_tests.oracle.passwd=$getenv(ORACLE_DATABASE_PASSWORD) + config.odb_tests.oracle.host=$getenv(ORACLE_DATABASE_HOST) + config.odb_tests.oracle.port=$getenv(ORACLE_DATABASE_PORT) + config.odb_tests.oracle.service=$getenv(ORACLE_DATABASE_SERVICE) +}+ odb-tests +\ + +# Complements the multi configuration (see libodb-mssql for background). +# +custom-multi-mssql-builds: latest ; Requires proprietary software. +custom-multi-mssql-builds: -wasm ; Not supported by libodb-{mysql,pgsql,mssql}. +custom-multi-mssql-builds: &( +( +windows &msvc ) +( +linux &gcc ) ) ; Only test on Windows and Linux with main compiler. +custom-multi-mssql-builds: -static ; Implementation uses plugins and requires -fPIC. +custom-multi-mssql-build-auxiliary-mysql: *-mysql_* +custom-multi-mssql-build-auxiliary-pgsql: *-postgresql_* +custom-multi-mssql-build-auxiliary-mssql: *-mssql_* +#custom-multi-mssql-build-bot: -- see below. +custom-multi-mssql-build-config: +\ +{ + config.odb_tests.libodb_test=true + config.odb_tests.database='mysql sqlite pgsql mssql' + + config.odb_tests.mysql.user=$getenv(MYSQL_DATABASE_USER) + config.odb_tests.mysql.database=$getenv(MYSQL_DATABASE_NAME) + config.odb_tests.mysql.host=$getenv(MYSQL_DATABASE_HOST) + config.odb_tests.mysql.port=$getenv(MYSQL_DATABASE_PORT) + + config.odb_tests.pgsql.user=$getenv(PGSQL_DATABASE_USER) + config.odb_tests.pgsql.database=$getenv(PGSQL_DATABASE_NAME) + config.odb_tests.pgsql.host=$getenv(PGSQL_DATABASE_HOST) + config.odb_tests.pgsql.port=$getenv(PGSQL_DATABASE_PORT) + + config.odb_tests.mssql.user=$getenv(MSSQL_DATABASE_USER) + config.odb_tests.mssql.passwd=$getenv(MSSQL_DATABASE_PASSWORD) + config.odb_tests.mssql.database=$getenv(MSSQL_DATABASE_NAME) + config.odb_tests.mssql.host=$getenv(MSSQL_DATABASE_HOST) + config.odb_tests.mssql.port=$getenv(MSSQL_DATABASE_PORT) + config.odb_tests.mssql.driver=$getenv(MSSQL_ODBC_DRIVER) +}+ odb-tests +\ + +custom-build-bot: +\ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuF4YmJmPHY52Q6N+YO0M +lt/fCovdezleb2tVplyTnvbyAiPdmYCIIjVrsqUn3y46PdFtWEiSdsrCcncoxi6H +8KelOB/oQ9pNTyEvwGKEH5ZIU7noLZYdXEfoNdvdL/pbY/7uLBZOSekfdQShZtbe +uOZCM2Mhg2DD76TP/VAwaXuDCnEvxxU/yneUl5ZaBo62AWNrYJuSGAliCOpVpl6X +X1kbHOvnCx7c9e3LxgaVivPaeZRKYg0OaFt96SBYEZzNPvjA8pMuKuj/vatHaCQ3 +NO9+r3TJ+4dQd7qN6Ju3zUJq9J/ndSh4lPvUalvvhdykecefhcyHwRZOG4xyFMFE +nJM4sM+aZu6WoKATIKtk7On70inVr0sZJXwJ4Lt4oqaK2VthcSTby3wf2Yv4p5hL +zNo31cCPmBRYzABcIc6ADYvexVK4uCwaim8xs7RK5Ug2Gv6vUWoRNZW8grIgDwUY +5pZ4Zk3hW4ii2vehTaVrrmdW6XipIsT+ayiVX7eWuHHNxAeCojXVjOJu9B0ExMlD +5tHZCs+SNdV5MceexecbptB7fZtRebP120yjLiSnZ5FpaQ1stusr0hSg+VQaX4np +f5m1W/CcDr53PKWg/ayY9nWMUQaIwH4b69kLM+VTpYSbzu5UQJkmNBNq2EOHgoTv +9MLA+cE/nNJ/rMI//MZ1+kcCAwEAAQ== +-----END PUBLIC KEY----- +\ + +custom-build-bot: +\ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuLYZ68rotGDAtWViFHOe +XEsmZB8BGI+af1ixh9JOy9BE4ohGTfPr1YsjGDzh+PhOVLAtyykOoT/qG7cuGB0T +gBInoRrgVB2/ZKTMwxeDGb/TA3uykaXxcw7/liTsizHAY+phCNTbke8iER5Y78js +9GlnTPmNhwFqEj2fwCz+2o08eyZvZ9Vj1fH/bFDCmDmU33JR3crtJlC8wPiF70Ho +FJzHFdaFQl3MxvEV92HjOsyqozMi6tAVVefN1vapVQeNtjkB0Di18p0/EMugEuGU +OxktjDHQWNaV8Ao6cCDk6OkJnM3ZNL1no3cV4cuF+/xI8UZzwfPoBnwg/s183Qzu +pHHKOSHmuO0oVE/XohJhepSw3tb+wf5BwejRhYHikIjqCxJdm9H0QTiqXT82y24K +yg3gkRMOgqnVxERKKP4ZknLSMQCEKiND/t2zdLJ/lxH9eHZdPHKk3OZZG292j+Bh +fknxcTKNk1Dmf32Irs5hVrjsoU8eAutbItovzXdBaj//rn/ry/kUlCa1Ov6iLIDJ +gyxmsDlgKNR/uE9ogmDn0ishJIoCmxeqenRfJkttr9pEsDsUFuB425QGqiSxa1jh +PCNca3iRtO44wADXaQMTGpvLzBfdfVc8LoFpn+kynN0V1MvxAX4mHRXxw8ERXd3U +dpHDhOthPLolJQrYKb/YyW8CAwEAAQ== +-----END PUBLIC KEY----- +\ + +custom-multi-build-bot: +\ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuF4YmJmPHY52Q6N+YO0M +lt/fCovdezleb2tVplyTnvbyAiPdmYCIIjVrsqUn3y46PdFtWEiSdsrCcncoxi6H +8KelOB/oQ9pNTyEvwGKEH5ZIU7noLZYdXEfoNdvdL/pbY/7uLBZOSekfdQShZtbe +uOZCM2Mhg2DD76TP/VAwaXuDCnEvxxU/yneUl5ZaBo62AWNrYJuSGAliCOpVpl6X +X1kbHOvnCx7c9e3LxgaVivPaeZRKYg0OaFt96SBYEZzNPvjA8pMuKuj/vatHaCQ3 +NO9+r3TJ+4dQd7qN6Ju3zUJq9J/ndSh4lPvUalvvhdykecefhcyHwRZOG4xyFMFE +nJM4sM+aZu6WoKATIKtk7On70inVr0sZJXwJ4Lt4oqaK2VthcSTby3wf2Yv4p5hL +zNo31cCPmBRYzABcIc6ADYvexVK4uCwaim8xs7RK5Ug2Gv6vUWoRNZW8grIgDwUY +5pZ4Zk3hW4ii2vehTaVrrmdW6XipIsT+ayiVX7eWuHHNxAeCojXVjOJu9B0ExMlD +5tHZCs+SNdV5MceexecbptB7fZtRebP120yjLiSnZ5FpaQ1stusr0hSg+VQaX4np +f5m1W/CcDr53PKWg/ayY9nWMUQaIwH4b69kLM+VTpYSbzu5UQJkmNBNq2EOHgoTv +9MLA+cE/nNJ/rMI//MZ1+kcCAwEAAQ== +-----END PUBLIC KEY----- +\ + +custom-multi-build-bot: +\ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuLYZ68rotGDAtWViFHOe +XEsmZB8BGI+af1ixh9JOy9BE4ohGTfPr1YsjGDzh+PhOVLAtyykOoT/qG7cuGB0T +gBInoRrgVB2/ZKTMwxeDGb/TA3uykaXxcw7/liTsizHAY+phCNTbke8iER5Y78js +9GlnTPmNhwFqEj2fwCz+2o08eyZvZ9Vj1fH/bFDCmDmU33JR3crtJlC8wPiF70Ho +FJzHFdaFQl3MxvEV92HjOsyqozMi6tAVVefN1vapVQeNtjkB0Di18p0/EMugEuGU +OxktjDHQWNaV8Ao6cCDk6OkJnM3ZNL1no3cV4cuF+/xI8UZzwfPoBnwg/s183Qzu +pHHKOSHmuO0oVE/XohJhepSw3tb+wf5BwejRhYHikIjqCxJdm9H0QTiqXT82y24K +yg3gkRMOgqnVxERKKP4ZknLSMQCEKiND/t2zdLJ/lxH9eHZdPHKk3OZZG292j+Bh +fknxcTKNk1Dmf32Irs5hVrjsoU8eAutbItovzXdBaj//rn/ry/kUlCa1Ov6iLIDJ +gyxmsDlgKNR/uE9ogmDn0ishJIoCmxeqenRfJkttr9pEsDsUFuB425QGqiSxa1jh +PCNca3iRtO44wADXaQMTGpvLzBfdfVc8LoFpn+kynN0V1MvxAX4mHRXxw8ERXd3U +dpHDhOthPLolJQrYKb/YyW8CAwEAAQ== +-----END PUBLIC KEY----- +\ + +custom-multi-oracle-build-bot: +\ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuF4YmJmPHY52Q6N+YO0M +lt/fCovdezleb2tVplyTnvbyAiPdmYCIIjVrsqUn3y46PdFtWEiSdsrCcncoxi6H +8KelOB/oQ9pNTyEvwGKEH5ZIU7noLZYdXEfoNdvdL/pbY/7uLBZOSekfdQShZtbe +uOZCM2Mhg2DD76TP/VAwaXuDCnEvxxU/yneUl5ZaBo62AWNrYJuSGAliCOpVpl6X +X1kbHOvnCx7c9e3LxgaVivPaeZRKYg0OaFt96SBYEZzNPvjA8pMuKuj/vatHaCQ3 +NO9+r3TJ+4dQd7qN6Ju3zUJq9J/ndSh4lPvUalvvhdykecefhcyHwRZOG4xyFMFE +nJM4sM+aZu6WoKATIKtk7On70inVr0sZJXwJ4Lt4oqaK2VthcSTby3wf2Yv4p5hL +zNo31cCPmBRYzABcIc6ADYvexVK4uCwaim8xs7RK5Ug2Gv6vUWoRNZW8grIgDwUY +5pZ4Zk3hW4ii2vehTaVrrmdW6XipIsT+ayiVX7eWuHHNxAeCojXVjOJu9B0ExMlD +5tHZCs+SNdV5MceexecbptB7fZtRebP120yjLiSnZ5FpaQ1stusr0hSg+VQaX4np +f5m1W/CcDr53PKWg/ayY9nWMUQaIwH4b69kLM+VTpYSbzu5UQJkmNBNq2EOHgoTv +9MLA+cE/nNJ/rMI//MZ1+kcCAwEAAQ== +-----END PUBLIC KEY----- +\ + +custom-multi-oracle-build-bot: +\ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuLYZ68rotGDAtWViFHOe +XEsmZB8BGI+af1ixh9JOy9BE4ohGTfPr1YsjGDzh+PhOVLAtyykOoT/qG7cuGB0T +gBInoRrgVB2/ZKTMwxeDGb/TA3uykaXxcw7/liTsizHAY+phCNTbke8iER5Y78js +9GlnTPmNhwFqEj2fwCz+2o08eyZvZ9Vj1fH/bFDCmDmU33JR3crtJlC8wPiF70Ho +FJzHFdaFQl3MxvEV92HjOsyqozMi6tAVVefN1vapVQeNtjkB0Di18p0/EMugEuGU +OxktjDHQWNaV8Ao6cCDk6OkJnM3ZNL1no3cV4cuF+/xI8UZzwfPoBnwg/s183Qzu +pHHKOSHmuO0oVE/XohJhepSw3tb+wf5BwejRhYHikIjqCxJdm9H0QTiqXT82y24K +yg3gkRMOgqnVxERKKP4ZknLSMQCEKiND/t2zdLJ/lxH9eHZdPHKk3OZZG292j+Bh +fknxcTKNk1Dmf32Irs5hVrjsoU8eAutbItovzXdBaj//rn/ry/kUlCa1Ov6iLIDJ +gyxmsDlgKNR/uE9ogmDn0ishJIoCmxeqenRfJkttr9pEsDsUFuB425QGqiSxa1jh +PCNca3iRtO44wADXaQMTGpvLzBfdfVc8LoFpn+kynN0V1MvxAX4mHRXxw8ERXd3U +dpHDhOthPLolJQrYKb/YyW8CAwEAAQ== +-----END PUBLIC KEY----- +\ + +custom-multi-mssql-build-bot: +\ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuF4YmJmPHY52Q6N+YO0M +lt/fCovdezleb2tVplyTnvbyAiPdmYCIIjVrsqUn3y46PdFtWEiSdsrCcncoxi6H +8KelOB/oQ9pNTyEvwGKEH5ZIU7noLZYdXEfoNdvdL/pbY/7uLBZOSekfdQShZtbe +uOZCM2Mhg2DD76TP/VAwaXuDCnEvxxU/yneUl5ZaBo62AWNrYJuSGAliCOpVpl6X +X1kbHOvnCx7c9e3LxgaVivPaeZRKYg0OaFt96SBYEZzNPvjA8pMuKuj/vatHaCQ3 +NO9+r3TJ+4dQd7qN6Ju3zUJq9J/ndSh4lPvUalvvhdykecefhcyHwRZOG4xyFMFE +nJM4sM+aZu6WoKATIKtk7On70inVr0sZJXwJ4Lt4oqaK2VthcSTby3wf2Yv4p5hL +zNo31cCPmBRYzABcIc6ADYvexVK4uCwaim8xs7RK5Ug2Gv6vUWoRNZW8grIgDwUY +5pZ4Zk3hW4ii2vehTaVrrmdW6XipIsT+ayiVX7eWuHHNxAeCojXVjOJu9B0ExMlD +5tHZCs+SNdV5MceexecbptB7fZtRebP120yjLiSnZ5FpaQ1stusr0hSg+VQaX4np +f5m1W/CcDr53PKWg/ayY9nWMUQaIwH4b69kLM+VTpYSbzu5UQJkmNBNq2EOHgoTv +9MLA+cE/nNJ/rMI//MZ1+kcCAwEAAQ== +-----END PUBLIC KEY----- +\ + +custom-multi-mssql-build-bot: +\ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuLYZ68rotGDAtWViFHOe +XEsmZB8BGI+af1ixh9JOy9BE4ohGTfPr1YsjGDzh+PhOVLAtyykOoT/qG7cuGB0T +gBInoRrgVB2/ZKTMwxeDGb/TA3uykaXxcw7/liTsizHAY+phCNTbke8iER5Y78js +9GlnTPmNhwFqEj2fwCz+2o08eyZvZ9Vj1fH/bFDCmDmU33JR3crtJlC8wPiF70Ho +FJzHFdaFQl3MxvEV92HjOsyqozMi6tAVVefN1vapVQeNtjkB0Di18p0/EMugEuGU +OxktjDHQWNaV8Ao6cCDk6OkJnM3ZNL1no3cV4cuF+/xI8UZzwfPoBnwg/s183Qzu +pHHKOSHmuO0oVE/XohJhepSw3tb+wf5BwejRhYHikIjqCxJdm9H0QTiqXT82y24K +yg3gkRMOgqnVxERKKP4ZknLSMQCEKiND/t2zdLJ/lxH9eHZdPHKk3OZZG292j+Bh +fknxcTKNk1Dmf32Irs5hVrjsoU8eAutbItovzXdBaj//rn/ry/kUlCa1Ov6iLIDJ +gyxmsDlgKNR/uE9ogmDn0ishJIoCmxeqenRfJkttr9pEsDsUFuB425QGqiSxa1jh +PCNca3iRtO44wADXaQMTGpvLzBfdfVc8LoFpn+kynN0V1MvxAX4mHRXxw8ERXd3U +dpHDhOthPLolJQrYKb/YyW8CAwEAAQ== +-----END PUBLIC KEY----- +\ diff --git a/libodb/odb/buildfile b/libodb/odb/buildfile new file mode 100644 index 0000000..903893f --- /dev/null +++ b/libodb/odb/buildfile @@ -0,0 +1,67 @@ +# file : odb/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +lib{odb}: {hxx ixx txx cxx}{* -version} \ + {hxx}{version} \ + details/{hxx ixx txx}{*} \ + details/{cxx}{* -condition -lock -mutex} \ + details/{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}: in{version} $src_root/manifest +hxx{version}: +{ + dist = true + clean = ($src_root != $out_root) +} + +# Build options. +# +cxx.poptions =+ "-I$out_root" "-I$src_root" + +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" + +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/ + +{h hxx ixx txx}{*}: +{ + install = $install_include + install.subdirs = true +} + +if ($cxx.target.class != "windows") + details/win32/*: install = false + +details/ +{ + if ($cxx.target.system != 'win32-msvc') + h{config-vc}@./: install = false + else + h{config}@./: install = false +} 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), ¶ms_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), ¶ms_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**> (¶ms), + &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 = © + } + }; + + 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/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..7c4def0 --- /dev/null +++ b/libodb/odb/details/config-vc.h @@ -0,0 +1,23 @@ +/* file : odb/details/config-vc.h + * license : GNU GPL v2; see accompanying LICENSE file + */ + +/* Configuration file for Windows/VC++ for the build2 build. + * + * Note that currently we only support ODB_THREADS_NONE and ODB_THREADS_CXX11 + * but could also support the _WIN32 variant with a bit of effort. + * + */ + +#ifndef ODB_DETAILS_CONFIG_VC_H +#define ODB_DETAILS_CONFIG_VC_H + +#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/config.h b/libodb/odb/details/config.h new file mode 100644 index 0000000..4ad9a8d --- /dev/null +++ b/libodb/odb/details/config.h @@ -0,0 +1,18 @@ +/* file : odb/details/config.h + * license : GNU GPL v2; see accompanying LICENSE file + */ + +/* Static configuration file for build2 build. + * + * Note that currently we only support ODB_THREADS_NONE and ODB_THREADS_CXX11 + * but could also support the _POSIX and _WIN32 variants with a bit of effort. + */ + +#ifndef ODB_DETAILS_CONFIG_H +#define ODB_DETAILS_CONFIG_H + +#ifndef ODB_THREADS_NONE +# define ODB_THREADS_CXX11 +#endif + +#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..3168109 --- /dev/null +++ b/libodb/odb/details/config.hxx @@ -0,0 +1,74 @@ +// 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 +#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..2ddc104 --- /dev/null +++ b/libodb/odb/details/export.hxx @@ -0,0 +1,46 @@ +// 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> // LIBODB_STATIC if ODB_COMPILER + +// 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. + +#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 + +#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..3f674ba --- /dev/null +++ b/libodb/odb/details/win32/dll.cxx @@ -0,0 +1,48 @@ +// file : odb/details/win32/dll.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// If we are building a static library then omit DllMain. + +#ifdef LIBODB_SHARED_BUILD + +#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>, + ©<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>, + ©<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>, ©<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..faa55c4 --- /dev/null +++ b/libodb/odb/nullable.hxx @@ -0,0 +1,492 @@ +// 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/details/config.hxx> // ODB_CXX11 + +#ifdef ODB_CXX11 +# include <utility> // std::move +#endif + +#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>&); + +#ifdef ODB_CXX11 + nullable (T&&); + nullable (nullable&&); + template <typename Y> explicit nullable (nullable<Y>&&); + + nullable& operator= (T&&); + nullable& operator= (nullable&&); + template <typename Y> nullable& operator= (nullable<Y>&&); + + ~nullable (); +#endif + + 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 () {}; + +#ifdef ODB_CXX11 + struct empty {}; + + union + { + empty empty_; + T value_; + }; +#else + T value_; +#endif + + 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 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 nullable<T>:: + nullable () + : null_ (true) + { + } + + template <typename T> + inline nullable<T>:: + nullable (const T& v) + : value_ (v), null_ (false) + { + } + +#ifdef ODB_CXX11 + + template <typename T> + inline nullable<T>:: + nullable (T&& v) + : value_ (std::move (v)), null_ (false) + { + } + + template <typename T> + inline nullable<T>:: + nullable (const nullable& y) + : null_ (y.null_) + { + if (!y.null_) + new (&value_) T (y.value_); + } + + template <typename T> + inline nullable<T>:: + nullable (nullable&& y) + : null_ (y.null_) + { + if (!y.null_) + new (&value_) T (std::move (y.value_)); + } + + template <typename T> + template <typename Y> + inline nullable<T>:: + nullable (const nullable<Y>& y) + : null_ (y.null_) + { + if (!y.null_) + new (&value_) T (y.value_); + } + + template <typename T> + template <typename Y> + inline nullable<T>:: + nullable (nullable<Y>&& y) + : null_ (y.null_) + { + if (!y.null_) + new (&value_) T (std::move (y.value_)); + } + + template <typename T> + inline nullable<T>& nullable<T>:: + operator= (const T& v) + { + if (!null_) + value_ = v; + else + { + new (&value_) T (v); + null_ = false; + } + + return *this; + } + + template <typename T> + inline nullable<T>& nullable<T>:: + operator= (T&& v) + { + if (!null_) + value_ = std::move (v); + else + { + new (&value_) T (std::move (v)); + null_ = false; + } + + return *this; + } + + template <typename T> + inline nullable<T>& nullable<T>:: + operator= (const nullable& y) + { + if (this != &y) + { + if (!y.null_) + { + if (!null_) + value_ = y.value_; + else + { + new (&value_) T (y.value_); + null_ = false; + } + } + else if (!null_) + { + value_.~T (); + null_ = true; + } + } + + return *this; + } + + template <typename T> + inline nullable<T>& nullable<T>:: + operator= (nullable&& y) + { + if (this != &y) + { + if (!y.null_) + { + if (!null_) + value_ = std::move (y.value_); + else + { + new (&value_) T (std::move (y.value_)); + null_ = false; + } + } + else if (!null_) + { + value_.~T (); + null_ = true; + } + } + + return *this; + } + + template <typename T> + template <typename Y> + inline nullable<T>& nullable<T>:: + operator= (const nullable<Y>& y) + { + if (!y.null_) + { + if (!null_) + value_ = y.value_; + else + { + new (&value_) T (y.value_); + null_ = false; + } + } + else if (!null_) + { + value_.~T (); + null_ = true; + } + + return *this; + } + + template <typename T> + template <typename Y> + inline nullable<T>& nullable<T>:: + operator= (nullable<Y>&& y) + { + if (!y.null_) + { + if (!null_) + value_ = std::move (y.value_); + else + { + new (&value_) T (std::move (y.value_)); + null_ = false; + } + } + else if (!null_) + { + value_.~T (); + null_ = true; + } + + return *this; + } + + template <typename T> + inline void nullable<T>:: + swap (nullable& y) + { + if (!y.null_) + { + if (!null_) + { + T v (std::move (value_)); + value_ = y.value_; + y.value_ = v; + } + else + { + new (&value_) T (std::move (y.value_)); + null_ = false; + + y.value_.~T (); + y.null_ = true; + } + } + else + { + if (!null_) + { + new (&y.value_) T (std::move (value_)); + y.null_ = false; + + value_.~T (); + null_ = true; + } + } + } + + template <typename T> + inline void nullable<T>:: + reset () + { + if (!null_) + { + value_.~T (); + null_ = true; + } + } + + template <typename T> + inline nullable<T>:: + ~nullable () + { + if (!null_) + value_.~T (); + } + +#else // C++98 + + 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 void nullable<T>:: + reset () + { + value_ = T (); + null_ = true; + } + +#endif +} + +#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..8c4edae --- /dev/null +++ b/libodb/odb/query-dynamic.hxx @@ -0,0 +1,1069 @@ +// 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 (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; + }; + + // 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 (decay_traits<T>::instance () == + decay_traits<T2>::instance ())); + + 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 (decay_traits<T>::instance () != + decay_traits<T2>::instance ())); + + 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 (decay_traits<T>::instance () < + decay_traits<T2>::instance ())); + + 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 (decay_traits<T>::instance () > + decay_traits<T2>::instance ())); + + 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 (decay_traits<T>::instance () <= + decay_traits<T2>::instance ())); + + 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 (decay_traits<T>::instance () >= + decay_traits<T2>::instance ())); + + 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..03810a1 --- /dev/null +++ b/libodb/odb/transaction.cxx @@ -0,0 +1,358 @@ +// 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> +#include <odb/details/unused.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) + { + ODB_POTENTIALLY_UNUSED (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.hxx b/libodb/odb/version.hxx new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libodb/odb/version.hxx diff --git a/libodb/odb/version.hxx.in b/libodb/odb/version.hxx.in new file mode 100644 index 0000000..0de61c5 --- /dev/null +++ b/libodb/odb/version.hxx.in @@ -0,0 +1,65 @@ +// file : odb/version.hxx.in +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef LIBODB_VERSION // Note: using the version macro itself. + +// New 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_FULL $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$" + + +// Old/deprecated numeric 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 20477 +#define ODB_VERSION_STR "2.5-b.27" + +// libodb version: interface version plus the bugfix version. +// +#define LIBODB_VERSION 2049977 + +#endif // LIBODB_VERSION 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/} |