diff --git a/.clang-format b/.clang-format index 4606f173b..e05a53c8c 100644 --- a/.clang-format +++ b/.clang-format @@ -54,7 +54,7 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true -DerivePointerAlignment: false +DerivePointerAlignment: true DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: false @@ -94,7 +94,6 @@ PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 -PointerAlignment: Right ReflowComments: false SortIncludes: false SortUsingDeclarations: true diff --git a/.github/workflows/clang-format-lint.yaml b/.github/workflows/clang-format-lint.yaml index fa76253c1..d7e6488ef 100644 --- a/.github/workflows/clang-format-lint.yaml +++ b/.github/workflows/clang-format-lint.yaml @@ -9,4 +9,6 @@ jobs: steps: - uses: actions/checkout@v1 - name: clang-format lint - uses: DoozyX/clang-format-lint-action@v0.3.1 + uses: DoozyX/clang-format-lint-action@v0.12 + with: + clangFormatVersion: 12 diff --git a/.travis.yml b/.travis.yml index 7811f8aa8..d0a3685d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ # Defaults os: linux -dist: bionic +dist: focal matrix: include: @@ -38,52 +38,49 @@ matrix: env: - SQLITE_ORM_OMITS_CODECVT: ON -# - name: "[C++14] AppleClang-10.0.1" -# os: osx -# osx_image: xcode10.2 -# language: cpp -# env: -# - SQLITE_ORM_OMITS_CODECVT: ON -# addons: -# homebrew: -# packages: -# - catch2 -# - ninja -# update: true + - name: "[C++14] AppleClang-10.0.1" + os: osx + osx_image: xcode10.2 + language: cpp + env: + - SQLITE_ORM_OMITS_CODECVT: ON + addons: + homebrew: + packages: + - ninja + update: true -# - name: "[C++14] LLVM/Clang (latest)" -# os: osx -# osx_image: xcode10.2 -# addons: -# homebrew: -# packages: -# - llvm -# - catch2 -# - ninja -# update: true -# env: -# - CPPFLAGS: "-I/usr/local/opt/llvm/include" -# - LDFLAGS: "-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib" -# - CPATH: /usr/local/opt/llvm/include -# - LIBRARY_PATH: /usr/local/opt/llvm/lib -# - LD_LIBRARY_PATH: /usr/local/opt/llvm/lib -# - CC: /usr/local/opt/llvm/bin/clang -# - CXX: /usr/local/opt/llvm/bin/clang++ -# - SQLITE_ORM_OMITS_CODECVT: ON + - name: "[C++14] LLVM/Clang (latest)" + os: osx + osx_image: xcode10.2 + addons: + homebrew: + packages: + - llvm + - ninja + update: true + env: + - CPPFLAGS: "-I/usr/local/opt/llvm/include" + - LDFLAGS: "-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib" + - CPATH: /usr/local/opt/llvm/include + - LIBRARY_PATH: /usr/local/opt/llvm/lib + - LD_LIBRARY_PATH: /usr/local/opt/llvm/lib + - CC: /usr/local/opt/llvm/bin/clang + - CXX: /usr/local/opt/llvm/bin/clang++ + - SQLITE_ORM_OMITS_CODECVT: ON -# - name: "[C++14] GCC-6" -# os: osx -# osx_image: xcode10.2 -# addons: -# homebrew: -# packages: -# - gcc@6 -# - catch2 -# - ninja -# update: true -# env: -# - CC: gcc-6 -# - CXX: g++-6 + - name: "[C++14] GCC-6" + os: osx + osx_image: xcode10.2 + addons: + homebrew: + packages: + - gcc@6 + - ninja + update: true + env: + - CC: gcc-6 + - CXX: g++-6 - name: "[C++17] GCC-9" addons: @@ -111,52 +108,41 @@ matrix: - CXX: g++-7 - SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_17=ON" -# - name: "[C++17] AppleClang-10.0.1" -# os: osx -# osx_image: xcode10.2 -# language: cpp -# env: -# - SQLITE_ORM_OMITS_CODECVT: ON -# - SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_17=ON" -# addons: -# homebrew: -# packages: -# - catch2 -# - ninja -# update: true + - name: "[C++17] AppleClang-10.0.1" + os: osx + osx_image: xcode10.2 + language: cpp + env: + - SQLITE_ORM_OMITS_CODECVT: ON + - SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_17=ON" + addons: + homebrew: + packages: + - ninja + update: true -# - name: "[C++17] LLVM/Clang (latest)" -# os: osx -# osx_image: xcode10.2 -# addons: -# homebrew: -# packages: -# - llvm -# - catch2 -# - ninja -# update: true -# env: -# - CPPFLAGS: "-I/usr/local/opt/llvm/include" -# - LDFLAGS: "-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib" -# - CPATH: /usr/local/opt/llvm/include -# - LIBRARY_PATH: /usr/local/opt/llvm/lib -# - LD_LIBRARY_PATH: /usr/local/opt/llvm/lib -# - CC: /usr/local/opt/llvm/bin/clang -# - CXX: /usr/local/opt/llvm/bin/clang++ -# - SQLITE_ORM_OMITS_CODECVT: ON -# - SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_17=ON" + - name: "[C++17] LLVM/Clang (latest)" + os: osx + osx_image: xcode10.2 + addons: + homebrew: + packages: + - llvm + - ninja + update: true + env: + - CPPFLAGS: "-I/usr/local/opt/llvm/include" + - LDFLAGS: "-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib" + - CPATH: /usr/local/opt/llvm/include + - LIBRARY_PATH: /usr/local/opt/llvm/lib + - LD_LIBRARY_PATH: /usr/local/opt/llvm/lib + - CC: /usr/local/opt/llvm/bin/clang + - CXX: /usr/local/opt/llvm/bin/clang++ + - SQLITE_ORM_OMITS_CODECVT: ON + - SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_17=ON" before_install: - - | - if [[ ${TRAVIS_OS_NAME} == "osx" ]]; then - export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH" # Use coreutils from homebrew. - fi - # Add ppa repo for cmake and delete cmake 3.12.4 folder of travis - - wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | sudo tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null - - sudo apt-add-repository -y 'deb https://apt.kitware.com/ubuntu/ bionic main' - - sudo apt-get update -qq -y - - sudo apt-get install -y cmake - - sudo rm -r /usr/local/cmake-3.12.4/ + - if [[ ${TRAVIS_OS_NAME} == "osx" ]]; then export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"; fi # scripts to run before build before_script: @@ -168,4 +154,4 @@ before_script: # build examples, and run tests (ie make & make test) script: - cmake --build . --config Debug -- -k 10 - - ctest --verbose --output-on-failure -C Debug -j $(nproc) + - ctest --verbose --output-on-failure -C Debug -j $(nproc) \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index dd86d2f1f..470ea4cea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,12 @@ -cmake_minimum_required (VERSION 3.16.0) +cmake_minimum_required (VERSION 3.14.0) + +# PACKAGE_VERSION is used by cpack scripts currently +# Both sqlite_orm_VERSION and PACKAGE_VERSION should be the same for now + +set(sqlite_orm_VERSION "1.6.0") +set(PACKAGE_VERSION ${sqlite_orm_VERSION}) + +project("sqlite_orm" VERSION ${PACKAGE_VERSION}) # Handling C++ standard version to use option(SQLITE_ORM_ENABLE_CXX_17 "Enable C++ 17" OFF) @@ -14,15 +22,6 @@ endif() set(CMAKE_CXX_EXTENSIONS OFF) -# PACKAGE_VERSION is used by cpack scripts currently -# Both sqlite_orm_VERSION and PACKAGE_VERSION should be the same for now - -set(sqlite_orm_VERSION "1.3.0") -set(PACKAGE_VERSION ${sqlite_orm_VERSION}) - -project("sqlite_orm" VERSION ${PACKAGE_VERSION}) - - set(CMAKE_VERBOSE_MAKEFILE ON) message(STATUS "Configuring ${CMAKE_PROJECT_NAME} ${sqlite_orm_VERSION}") @@ -54,6 +53,7 @@ if (MSVC) string(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") add_compile_options(/EHsc) add_compile_options(/Zc:__cplusplus) + add_compile_options(/MP) # Allow multi parrallel build if ("${CMAKE_GENERATOR}" MATCHES "(Win64|x64)") message(STATUS "Add /bigobj flag to compiler") diff --git a/COMM-LICENSE b/COMM-LICENSE new file mode 100644 index 000000000..ef5fd18a8 --- /dev/null +++ b/COMM-LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2012-2021 Scott Chacon and others + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7869bd2ac..b1baf532c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,7 +36,7 @@ Instructions: ## If you want to build the project locally ## -See our detailed instructions on the [CMake README](/build/cmake/README.md). +See our detailed instructions on the [CMake README](https://github.com/fnc12/sqlite_orm#usage). ## If you want to review open issues... ## diff --git a/LICENSE b/LICENSE index 2987df785..0ad25db4b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,29 +1,661 @@ -BSD 3-Clause License - -Copyright (c) 2017, Yevgeniy Zakharov -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + 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 +them 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. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/README.md b/README.md index 968133108..12f6021c8 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,12 @@ Sublime's custom image

-[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Donate using PayPal](https://img.shields.io/badge/donate-PayPal-brightgreen.svg)](https://paypal.me/fnc12) [![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/fold_left.svg?style=social&label=Follow%20%40sqlite_orm)](https://twitter.com/sqlite_orm) # SQLite ORM -SQLite ORM light header only library for modern C++ +SQLite ORM light header only library for modern C++. Please read the license precisely. The project has AGPL license for open source project and MIT license after purchasing it for 50$ (using [PayPal](https://paypal.me/fnc12) or any different way (contact using email fnc12@me.com)). # Status | Branch | Travis | Appveyor | @@ -45,6 +44,7 @@ SQLite ORM light header only library for modern C++ * **In memory database support** - provide `:memory:` or empty filename * **COLLATE support** * **Limits setting/getting support** +* **User defined functions support** `sqlite_orm` library allows to create easy data model mappings to your database schema. It is built to manage (CRUD) objects with a primary key and without it. It also allows you to specify table names and column names explicitly no matter how your classes actually named. Take a look at example: diff --git a/TODO.md b/TODO.md index 315e99e47..1af731341 100644 --- a/TODO.md +++ b/TODO.md @@ -6,7 +6,6 @@ * rest of core functions(https://sqlite.org/lang_corefunc.html) * `ATTACH` * blob incremental I/O https://sqlite.org/c3ref/blob_open.html -* explicit FROM for subqueries in FROM argument * CREATE VIEW and other view operations https://sqlite.org/lang_createview.html * triggers * query static check for correct order (e.g. `GROUP BY` after `WHERE`) @@ -16,5 +15,12 @@ * add `static_assert` in crud `get*` functions in case user passes `where_t` instead of id to make compilation error more clear (example https://github.com/fnc12/sqlite_orm/issues/485) * generated columns https://www.sqlite.org/gencol.html * named constraints: constraint can have name `CREATE TABLE heroes(id INTEGER CONSTRAINT pk PRIMARY KEY)` +* `FILTER` clause https://sqlite.org/lang_aggfunc.html#aggfilter +* scalar math functions https://sqlite.org/lang_mathfunc.html +* improve DROP COLUMN in `sync_schema` https://sqlite.org/lang_altertable.html#altertabdropcol +* `UPDATE FROM` support https://sqlite.org/lang_update.html#upfrom +* `iif()` function https://sqlite.org/lang_corefunc.html#iif +* add strong typed collate syntax (more info [here](https://github.com/fnc12/sqlite_orm/issues/767#issuecomment-887689672)) +* raw `INSERT`: `storage.insert_into(&User::id, values(5));` or `storage.insert_into(columns(&User::id, &User::name), values(5, "Puff Diddy"));` or `storage.insert_into(columns(), default_values());` Please feel free to add any feature that isn't listed here and not implemented yet. diff --git a/build.sh b/build.sh deleted file mode 100755 index 5ca371f61..000000000 --- a/build.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh - -# exit on firts error -set -e - -echo "Prepare third party libraries" -cd third_party -git clone https://github.com/Microsoft/vcpkg.git vcpkg -cd vcpkg -chmod +x bootstrap-vcpkg.sh - -if [[ "$CXX" == *"clang"* && "$TRAVIS_OS_NAME" == "osx" ]]; then ./bootstrap-vcpkg.sh --allowAppleClang ; else ./bootstrap-vcpkg.sh ; fi - -chmod +x vcpkg -./vcpkg install gtest -cd ../.. - -echo "Prepare for compile" -mkdir -p compile -cd compile - -cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../third_party/vcpkg/scripts/buildsystems/vcpkg.cmake .. - -echo "Compiling..." -cmake --build . --config Debug - -echo "Run testing..." -ctest -C Debug --output-on-failure -V - diff --git a/dev/alias.h b/dev/alias.h index 916c82195..b2073dbab 100644 --- a/dev/alias.h +++ b/dev/alias.h @@ -39,7 +39,7 @@ namespace sqlite_orm { alias_column_t(){}; - alias_column_t(column_type column_) : column(column_) {} + alias_column_t(column_type column_) : column(std::move(column_)) {} }; template @@ -61,6 +61,9 @@ namespace sqlite_orm { } }; + /** + * Used to store alias for expression + */ template struct as_t { using alias_type = T; diff --git a/dev/arg_values.h b/dev/arg_values.h new file mode 100644 index 000000000..414a3d734 --- /dev/null +++ b/dev/arg_values.h @@ -0,0 +1,145 @@ +#pragma once + +#include + +#include "row_extractor.h" + +namespace sqlite_orm { + + struct arg_value { + + arg_value() : arg_value(nullptr) {} + + arg_value(sqlite3_value *value_) : value(value_) {} + + template + T get() const { + return row_extractor().extract(this->value); + } + + bool is_null() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_NULL; + } + + bool is_text() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_TEXT; + } + + bool is_integer() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_INTEGER; + } + + bool is_float() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_FLOAT; + } + + bool is_blob() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_BLOB; + } + + bool empty() const { + return this->value == nullptr; + } + + private: + sqlite3_value *value = nullptr; + }; + + struct arg_values { + + struct iterator { + + iterator(const arg_values &container_, int index_) : + container(container_), index(index_), + currentValue(index_ < int(container_.size()) ? container_[index_] : arg_value()) {} + + iterator &operator++() { + ++this->index; + if(this->index < int(this->container.size())) { + this->currentValue = this->container[this->index]; + } else { + this->currentValue = {}; + } + return *this; + } + + iterator operator++(int) { + auto res = *this; + ++this->index; + if(this->index < int(this->container.size())) { + this->currentValue = this->container[this->index]; + } else { + this->currentValue = {}; + } + return res; + } + + arg_value operator*() const { + if(this->index < int(this->container.size()) && this->index >= 0) { + return this->currentValue; + } else { + throw std::system_error(std::make_error_code(orm_error_code::index_is_out_of_bounds)); + } + } + + arg_value *operator->() const { + return &this->currentValue; + } + + bool operator==(const iterator &other) const { + return &other.container == &this->container && other.index == this->index; + } + + bool operator!=(const iterator &other) const { + return !(*this == other); + } + + private: + const arg_values &container; + int index = 0; + mutable arg_value currentValue; + }; + + arg_values() : arg_values(0, nullptr) {} + + arg_values(int argsCount_, sqlite3_value **values_) : argsCount(argsCount_), values(values_) {} + + size_t size() const { + return this->argsCount; + } + + bool empty() const { + return 0 == this->argsCount; + } + + arg_value operator[](int index) const { + if(index < this->argsCount && index >= 0) { + auto valuePointer = this->values[index]; + return {valuePointer}; + } else { + throw std::system_error(std::make_error_code(orm_error_code::index_is_out_of_bounds)); + } + } + + arg_value at(int index) const { + return this->operator[](index); + } + + iterator begin() const { + return {*this, 0}; + } + + iterator end() const { + return {*this, this->argsCount}; + } + + private: + int argsCount = 0; + sqlite3_value **values = nullptr; + }; +} diff --git a/dev/ast/excluded.h b/dev/ast/excluded.h new file mode 100644 index 000000000..f474dd0e5 --- /dev/null +++ b/dev/ast/excluded.h @@ -0,0 +1,18 @@ +#pragma once + +namespace sqlite_orm { + namespace internal { + + template + struct excluded_t { + using expression_type = T; + + expression_type expression; + }; + } + + template + internal::excluded_t excluded(T expression) { + return {std::move(expression)}; + } +} diff --git a/dev/ast/upsert_clause.h b/dev/ast/upsert_clause.h new file mode 100644 index 000000000..8f701587e --- /dev/null +++ b/dev/ast/upsert_clause.h @@ -0,0 +1,61 @@ +#pragma once + +#include // std::tuple +#include // std::false_type, std::true_type + +namespace sqlite_orm { + namespace internal { + + template + struct upsert_clause; + + template + struct conflict_target { + using args_tuple = std::tuple; + + args_tuple args; + + upsert_clause> do_nothing() { + return {std::move(this->args), {}}; + } + + template + upsert_clause> do_update(ActionsArgs... actions) { + return {std::move(this->args), {std::make_tuple(std::forward(actions)...)}}; + } + }; + + template + struct upsert_clause, std::tuple> { + using target_args_tuple = std::tuple; + using actions_tuple = std::tuple; + + target_args_tuple target_args; + + actions_tuple actions; + }; + + template + struct is_upsert_clause : std::false_type {}; + + template + struct is_upsert_clause> : std::true_type {}; + } + + /** + * ON CONFLICT upsert clause builder function. + * @example + * storage.insert(into(), + * columns(&Employee::id, &Employee::name, &Employee::age, &Employee::address, &Employee::salary), + * values(std::make_tuple(3, "Sofia", 26, "Madrid", 15000.0), + * std::make_tuple(4, "Doja", 26, "LA", 25000.0)), + * on_conflict(&Employee::id).do_update(set(c(&Employee::name) = excluded(&Employee::name), + * c(&Employee::age) = excluded(&Employee::age), + * c(&Employee::address) = excluded(&Employee::address), + * c(&Employee::salary) = excluded(&Employee::salary)))); + */ + template + internal::conflict_target on_conflict(Args... args) { + return {std::tuple(std::forward(args)...)}; + } +} diff --git a/dev/ast/where.h b/dev/ast/where.h new file mode 100644 index 000000000..db5931211 --- /dev/null +++ b/dev/ast/where.h @@ -0,0 +1,48 @@ +#pragma once + +#include "../serialize_result_type.h" + +namespace sqlite_orm { + namespace internal { + + struct where_string { + serialize_result_type serialize() const { + return "WHERE"; + } + }; + + /** + * WHERE argument holder. + * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc + * Don't construct it manually. Call `where(...)` function instead. + */ + template + struct where_t : where_string { + using expression_type = C; + + expression_type expression; + + where_t(expression_type expression_) : expression(std::move(expression_)) {} + }; + + template + struct is_where : std::false_type {}; + + template + struct is_where> : std::true_type {}; + } + + /** + * WHERE clause. Use it to add WHERE conditions wherever you like. + * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc + * @example + * // SELECT name + * // FROM letters + * // WHERE id > 3 + * auto rows = storage.select(&Letter::name, where(greater_than(&Letter::id, 3))); + */ + template + internal::where_t where(C expression) { + return {std::move(expression)}; + } +} diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h index 30c35020f..c364603c6 100644 --- a/dev/ast_iterator.h +++ b/dev/ast_iterator.h @@ -6,10 +6,14 @@ #include "conditions.h" #include "select_constraints.h" #include "operators.h" -#include "tuple_helper.h" +#include "tuple_helper/tuple_helper.h" #include "core_functions.h" #include "prepared_statement.h" #include "values.h" +#include "function.h" +#include "ast/excluded.h" +#include "ast/upsert_clause.h" +#include "ast/where.h" namespace sqlite_orm { @@ -32,7 +36,7 @@ namespace sqlite_orm { * L is a callable type. Mostly is a templated lambda */ template - void operator()(const T &t, const L &l) const { + void operator()(const T& t, const L& l) const { l(t); } }; @@ -41,18 +45,50 @@ namespace sqlite_orm { * Simplified API */ template - void iterate_ast(const T &t, const L &l) { + void iterate_ast(const T& t, const L& l) { ast_iterator iterator; iterator(t, l); } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct ast_iterator, void> { + using node_type = as_optional_t; + + template + void operator()(const node_type& node, const L& lambda) const { + iterate_ast(node.value, lambda); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template struct ast_iterator, void> { using node_type = std::reference_wrapper; template - void operator()(const node_type &r, const L &l) const { - iterate_ast(r.get(), l); + void operator()(const node_type& r, const L& lambda) const { + iterate_ast(r.get(), lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = excluded_t; + + template + void operator()(const node_type& expression, const L& lambda) const { + iterate_ast(expression.expression, lambda); + } + }; + + template + struct ast_iterator, std::tuple>, void> { + using node_type = upsert_clause, std::tuple>; + + template + void operator()(const node_type& expression, const L& lambda) const { + iterate_ast(expression.actions, lambda); } }; @@ -61,8 +97,8 @@ namespace sqlite_orm { using node_type = where_t; template - void operator()(const node_type &where, const L &l) const { - iterate_ast(where.c, l); + void operator()(const node_type& expression, const L& lambda) const { + iterate_ast(expression.expression, lambda); } }; @@ -71,7 +107,7 @@ namespace sqlite_orm { using node_type = T; template - void operator()(const node_type &binaryCondition, const L &l) const { + void operator()(const node_type& binaryCondition, const L& l) const { iterate_ast(binaryCondition.l, l); iterate_ast(binaryCondition.r, l); } @@ -82,7 +118,7 @@ namespace sqlite_orm { using node_type = binary_operator; template - void operator()(const node_type &binaryOperator, const C &l) const { + void operator()(const node_type& binaryOperator, const C& l) const { iterate_ast(binaryOperator.lhs, l); iterate_ast(binaryOperator.rhs, l); } @@ -93,19 +129,30 @@ namespace sqlite_orm { using node_type = columns_t; template - void operator()(const node_type &cols, const L &l) const { + void operator()(const node_type& cols, const L& l) const { iterate_ast(cols.columns, l); } }; template - struct ast_iterator, void> { - using node_type = in_t; + struct ast_iterator, void> { + using node_type = dynamic_in_t; template - void operator()(const node_type &in, const C &l) const { - iterate_ast(in.l, l); - iterate_ast(in.arg, l); + void operator()(const node_type& in, const C& l) const { + iterate_ast(in.left, l); + iterate_ast(in.argument, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = in_t; + + template + void operator()(const node_type& in, const C& l) const { + iterate_ast(in.left, l); + iterate_ast(in.argument, l); } }; @@ -114,8 +161,8 @@ namespace sqlite_orm { using node_type = std::vector; template - void operator()(const node_type &vec, const L &l) const { - for(auto &i: vec) { + void operator()(const node_type& vec, const L& l) const { + for(auto& i: vec) { iterate_ast(i, l); } } @@ -126,7 +173,7 @@ namespace sqlite_orm { using node_type = std::vector; template - void operator()(const node_type &vec, const L &l) const { + void operator()(const node_type& vec, const L& l) const { l(vec); } }; @@ -136,18 +183,48 @@ namespace sqlite_orm { using node_type = T; template - void operator()(const node_type &c, const L &l) const { + void operator()(const node_type& c, const L& l) const { iterate_ast(c.left, l); iterate_ast(c.right, l); } }; + template + struct ast_iterator, void> { + using node_type = into_t; + + template + void operator()(const node_type& node, const L& l) const { + //.. + } + }; + + template + struct ast_iterator, void> { + using node_type = insert_raw_t; + + template + void operator()(const node_type& node, const L& l) const { + iterate_ast(node.args, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = replace_raw_t; + + template + void operator()(const node_type& node, const L& l) const { + iterate_ast(node.args, l); + } + }; + template struct ast_iterator, void> { using node_type = select_t; template - void operator()(const node_type &sel, const L &l) const { + void operator()(const node_type& sel, const L& l) const { iterate_ast(sel.col, l); iterate_ast(sel.conditions, l); } @@ -158,7 +235,7 @@ namespace sqlite_orm { using node_type = get_all_t; template - void operator()(const node_type &get, const L &l) const { + void operator()(const node_type& get, const L& l) const { iterate_ast(get.conditions, l); } }; @@ -168,7 +245,7 @@ namespace sqlite_orm { using node_type = get_all_pointer_t; template - void operator()(const node_type &get, const L &l) const { + void operator()(const node_type& get, const L& l) const { iterate_ast(get.conditions, l); } }; @@ -179,7 +256,7 @@ namespace sqlite_orm { using node_type = get_all_optional_t; template - void operator()(const node_type &get, const L &l) const { + void operator()(const node_type& get, const L& l) const { iterate_ast(get.conditions, l); } }; @@ -190,7 +267,7 @@ namespace sqlite_orm { using node_type = update_all_t, Wargs...>; template - void operator()(const node_type &u, const L &l) const { + void operator()(const node_type& u, const L& l) const { iterate_ast(u.set, l); iterate_ast(u.conditions, l); } @@ -201,7 +278,7 @@ namespace sqlite_orm { using node_type = remove_all_t; template - void operator()(const node_type &r, const L &l) const { + void operator()(const node_type& r, const L& l) const { iterate_ast(r.conditions, l); } }; @@ -211,7 +288,7 @@ namespace sqlite_orm { using node_type = set_t; template - void operator()(const node_type &s, const L &l) const { + void operator()(const node_type& s, const L& l) const { iterate_ast(s.assigns, l); } }; @@ -221,8 +298,8 @@ namespace sqlite_orm { using node_type = std::tuple; template - void operator()(const node_type &tuple, const L &l) const { - iterate_tuple(tuple, [&l](auto &v) { + void operator()(const node_type& tuple, const L& l) const { + iterate_tuple(tuple, [&l](auto& v) { iterate_ast(v, l); }); } @@ -233,7 +310,7 @@ namespace sqlite_orm { using node_type = having_t; template - void operator()(const node_type &hav, const L &l) const { + void operator()(const node_type& hav, const L& l) const { iterate_ast(hav.t, l); } }; @@ -243,7 +320,7 @@ namespace sqlite_orm { using node_type = cast_t; template - void operator()(const node_type &c, const L &l) const { + void operator()(const node_type& c, const L& l) const { iterate_ast(c.expression, l); } }; @@ -253,7 +330,7 @@ namespace sqlite_orm { using node_type = exists_t; template - void operator()(const node_type &e, const L &l) const { + void operator()(const node_type& e, const L& l) const { iterate_ast(e.t, l); } }; @@ -263,10 +340,10 @@ namespace sqlite_orm { using node_type = like_t; template - void operator()(const node_type &lk, const L &l) const { + void operator()(const node_type& lk, const L& l) const { iterate_ast(lk.arg, l); iterate_ast(lk.pattern, l); - lk.arg3.apply([&l](auto &value) { + lk.arg3.apply([&l](auto& value) { iterate_ast(value, l); }); } @@ -277,7 +354,7 @@ namespace sqlite_orm { using node_type = glob_t; template - void operator()(const node_type &lk, const L &l) const { + void operator()(const node_type& lk, const L& l) const { iterate_ast(lk.arg, l); iterate_ast(lk.pattern, l); } @@ -288,7 +365,7 @@ namespace sqlite_orm { using node_type = between_t; template - void operator()(const node_type &b, const L &l) const { + void operator()(const node_type& b, const L& l) const { iterate_ast(b.expr, l); iterate_ast(b.b1, l); iterate_ast(b.b2, l); @@ -300,7 +377,7 @@ namespace sqlite_orm { using node_type = named_collate; template - void operator()(const node_type &col, const L &l) const { + void operator()(const node_type& col, const L& l) const { iterate_ast(col.expr, l); } }; @@ -310,7 +387,7 @@ namespace sqlite_orm { using node_type = negated_condition_t; template - void operator()(const node_type &neg, const L &l) const { + void operator()(const node_type& neg, const L& l) const { iterate_ast(neg.c, l); } }; @@ -320,7 +397,7 @@ namespace sqlite_orm { using node_type = is_null_t; template - void operator()(const node_type &i, const L &l) const { + void operator()(const node_type& i, const L& l) const { iterate_ast(i.t, l); } }; @@ -330,17 +407,27 @@ namespace sqlite_orm { using node_type = is_not_null_t; template - void operator()(const node_type &i, const L &l) const { + void operator()(const node_type& i, const L& l) const { iterate_ast(i.t, l); } }; + template + struct ast_iterator, void> { + using node_type = function_call; + + template + void operator()(const node_type& f, const L& l) const { + iterate_ast(f.args, l); + } + }; + template - struct ast_iterator, void> { - using node_type = core_function_t; + struct ast_iterator, void> { + using node_type = built_in_function_t; template - void operator()(const node_type &f, const L &l) const { + void operator()(const node_type& f, const L& l) const { iterate_ast(f.args, l); } }; @@ -350,7 +437,7 @@ namespace sqlite_orm { using node_type = left_join_t; template - void operator()(const node_type &j, const L &l) const { + void operator()(const node_type& j, const L& l) const { iterate_ast(j.constraint, l); } }; @@ -360,7 +447,7 @@ namespace sqlite_orm { using node_type = on_t; template - void operator()(const node_type &o, const L &l) const { + void operator()(const node_type& o, const L& l) const { iterate_ast(o.arg, l); } }; @@ -370,7 +457,7 @@ namespace sqlite_orm { using node_type = join_t; template - void operator()(const node_type &j, const L &l) const { + void operator()(const node_type& j, const L& l) const { iterate_ast(j.constraint, l); } }; @@ -380,7 +467,7 @@ namespace sqlite_orm { using node_type = left_outer_join_t; template - void operator()(const node_type &j, const L &l) const { + void operator()(const node_type& j, const L& l) const { iterate_ast(j.constraint, l); } }; @@ -390,7 +477,7 @@ namespace sqlite_orm { using node_type = inner_join_t; template - void operator()(const node_type &j, const L &l) const { + void operator()(const node_type& j, const L& l) const { iterate_ast(j.constraint, l); } }; @@ -400,15 +487,15 @@ namespace sqlite_orm { using node_type = simple_case_t; template - void operator()(const node_type &c, const L &l) const { - c.case_expression.apply([&l](auto &c_) { + void operator()(const node_type& c, const L& l) const { + c.case_expression.apply([&l](auto& c_) { iterate_ast(c_, l); }); - iterate_tuple(c.args, [&l](auto &pair) { + iterate_tuple(c.args, [&l](auto& pair) { iterate_ast(pair.first, l); iterate_ast(pair.second, l); }); - c.else_expression.apply([&l](auto &el) { + c.else_expression.apply([&l](auto& el) { iterate_ast(el, l); }); } @@ -419,7 +506,7 @@ namespace sqlite_orm { using node_type = as_t; template - void operator()(const node_type &a, const L &l) const { + void operator()(const node_type& a, const L& l) const { iterate_ast(a.expression, l); } }; @@ -429,7 +516,7 @@ namespace sqlite_orm { using node_type = limit_t; template - void operator()(const node_type &a, const L &l) const { + void operator()(const node_type& a, const L& l) const { iterate_ast(a.lim, l); } }; @@ -439,9 +526,9 @@ namespace sqlite_orm { using node_type = limit_t; template - void operator()(const node_type &a, const L &l) const { + void operator()(const node_type& a, const L& l) const { iterate_ast(a.lim, l); - a.off.apply([&l](auto &value) { + a.off.apply([&l](auto& value) { iterate_ast(value, l); }); } @@ -452,8 +539,8 @@ namespace sqlite_orm { using node_type = limit_t; template - void operator()(const node_type &a, const L &l) const { - a.off.apply([&l](auto &value) { + void operator()(const node_type& a, const L& l) const { + a.off.apply([&l](auto& value) { iterate_ast(value, l); }); iterate_ast(a.lim, l); @@ -465,8 +552,8 @@ namespace sqlite_orm { using node_type = distinct_t; template - void operator()(const node_type &a, const L &l) const { - iterate_ast(a.t, l); + void operator()(const node_type& a, const L& l) const { + iterate_ast(a.value, l); } }; @@ -475,8 +562,8 @@ namespace sqlite_orm { using node_type = all_t; template - void operator()(const node_type &a, const L &l) const { - iterate_ast(a.t, l); + void operator()(const node_type& a, const L& l) const { + iterate_ast(a.value, l); } }; @@ -485,7 +572,7 @@ namespace sqlite_orm { using node_type = bitwise_not_t; template - void operator()(const node_type &a, const L &l) const { + void operator()(const node_type& a, const L& l) const { iterate_ast(a.argument, l); } }; @@ -495,7 +582,7 @@ namespace sqlite_orm { using node_type = values_t; template - void operator()(const node_type &node, const L &l) const { + void operator()(const node_type& node, const L& l) const { iterate_ast(node.tuple, l); } }; @@ -505,7 +592,7 @@ namespace sqlite_orm { using node_type = dynamic_values_t; template - void operator()(const node_type &node, const L &l) const { + void operator()(const node_type& node, const L& l) const { iterate_ast(node.vector, l); } }; @@ -515,7 +602,7 @@ namespace sqlite_orm { using node_type = collate_t; template - void operator()(const node_type &node, const L &l) const { + void operator()(const node_type& node, const L& l) const { iterate_ast(node.expr, l); } }; diff --git a/dev/backup.h b/dev/backup.h index 0459b0a4c..e1de8481f 100644 --- a/dev/backup.h +++ b/dev/backup.h @@ -19,9 +19,9 @@ namespace sqlite_orm { */ struct backup_t { backup_t(connection_ref to_, - const std::string &zDestName, + const std::string& zDestName, connection_ref from_, - const std::string &zSourceName, + const std::string& zSourceName, std::unique_ptr holder_) : handle(sqlite3_backup_init(to_.get(), zDestName.c_str(), from_.get(), zSourceName.c_str())), to(to_), from(from_), holder(move(holder_)) { @@ -30,7 +30,7 @@ namespace sqlite_orm { } } - backup_t(backup_t &&other) : + backup_t(backup_t&& other) : handle(other.handle), to(other.to), from(other.from), holder(move(other.holder)) { other.handle = nullptr; } @@ -64,7 +64,7 @@ namespace sqlite_orm { } protected: - sqlite3_backup *handle = nullptr; + sqlite3_backup* handle = nullptr; connection_ref to; connection_ref from; std::unique_ptr holder; diff --git a/dev/column.h b/dev/column.h index 2293ede03..e7acca2f4 100644 --- a/dev/column.h +++ b/dev/column.h @@ -6,10 +6,10 @@ #include // std::true_type, std::false_type, std::is_same, std::enable_if, std::is_member_pointer, std::is_member_function_pointer #include "type_is_nullable.h" -#include "tuple_helper.h" +#include "tuple_helper/tuple_helper.h" #include "default_value_extractor.h" #include "constraints.h" -#include "getter_traits.h" +#include "member_traits/member_traits.h" namespace sqlite_orm { @@ -99,7 +99,7 @@ namespace sqlite_orm { */ std::unique_ptr default_value() const { std::unique_ptr res; - iterate_tuple(this->constraints, [&res](auto &v) { + iterate_tuple(this->constraints, [&res](auto& v) { auto dft = internal::default_value_extractor()(v); if(dft) { res = std::move(dft); @@ -109,6 +109,48 @@ namespace sqlite_orm { } }; + // we are compelled to wrap all sfinae-implemented traits to prevent "error: type/value mismatch at argument 2 in template parameter list" + namespace sfinae { + /** + * Column with insertable primary key traits. Common case. + */ + template + struct is_column_with_insertable_primary_key : public std::false_type {}; + + /** + * Column with insertable primary key traits. Specialized case case. + */ + template + struct is_column_with_insertable_primary_key< + column_t, + typename std::enable_if<(tuple_helper::tuple_contains_type< + primary_key_t<>, + typename column_t::constraints_type>::value)>::type> { + using column_type = column_t; + static constexpr bool value = is_primary_key_insertable::value; + }; + + /** + * Column with noninsertable primary key traits. Common case. + */ + template + struct is_column_with_noninsertable_primary_key : public std::false_type {}; + + /** + * Column with noninsertable primary key traits. Specialized case case. + */ + template + struct is_column_with_noninsertable_primary_key< + column_t, + typename std::enable_if<(tuple_helper::tuple_contains_type< + primary_key_t<>, + typename column_t::constraints_type>::value)>::type> { + using column_type = column_t; + static constexpr bool value = !is_primary_key_insertable::value; + }; + + } + /** * Column traits. Common case. */ @@ -121,6 +163,18 @@ namespace sqlite_orm { template struct is_column> : public std::true_type {}; + /** + * Column with insertable primary key traits. + */ + template + struct is_column_with_insertable_primary_key : public sfinae::is_column_with_insertable_primary_key {}; + + /** + * Column with noninsertable primary key traits. + */ + template + struct is_column_with_noninsertable_primary_key : public sfinae::is_column_with_noninsertable_primary_key {}; + template struct column_field_type { using type = void; @@ -150,9 +204,9 @@ namespace sqlite_orm { class T, typename = typename std::enable_if::value>::type, class... Op> - internal::column_t - make_column(const std::string &name, T O::*m, Op... constraints) { - static_assert(constraints::template constraints_size::value == std::tuple_size>::value, + internal::column_t + make_column(const std::string& name, T O::*m, Op... constraints) { + static_assert(internal::template constraints_size::value == std::tuple_size>::value, "Incorrect constraints pack"); static_assert(internal::is_field_member_pointer::value, "second argument expected as a member field pointer, not member function pointer"); @@ -172,11 +226,11 @@ namespace sqlite_orm { G, S, Op...> - make_column(const std::string &name, S setter, G getter, Op... constraints) { + make_column(const std::string& name, S setter, G getter, Op... constraints) { static_assert(std::is_same::field_type, typename internal::getter_traits::field_type>::value, "Getter and setter must get and set same data type"); - static_assert(constraints::template constraints_size::value == std::tuple_size>::value, + static_assert(internal::template constraints_size::value == std::tuple_size>::value, "Incorrect constraints pack"); return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; } @@ -195,11 +249,11 @@ namespace sqlite_orm { G, S, Op...> - make_column(const std::string &name, G getter, S setter, Op... constraints) { + make_column(const std::string& name, G getter, S setter, Op... constraints) { static_assert(std::is_same::field_type, typename internal::getter_traits::field_type>::value, "Getter and setter must get and set same data type"); - static_assert(constraints::template constraints_size::value == std::tuple_size>::value, + static_assert(internal::template constraints_size::value == std::tuple_size>::value, "Incorrect constraints pack"); return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; } diff --git a/dev/column_names_getter.h b/dev/column_names_getter.h index 26e43481c..c516d70a9 100644 --- a/dev/column_names_getter.h +++ b/dev/column_names_getter.h @@ -12,14 +12,14 @@ namespace sqlite_orm { namespace internal { template - std::string serialize(const T &t, const C &context); + std::string serialize(const T& t, const C& context); template struct column_names_getter { using expression_type = T; template - std::vector operator()(const expression_type &t, const C &context) { + std::vector operator()(const expression_type& t, const C& context) { auto newContext = context; newContext.skip_table_name = false; auto columnName = serialize(t, newContext); @@ -32,7 +32,7 @@ namespace sqlite_orm { }; template - std::vector get_column_names(const T &t, const C &context) { + std::vector get_column_names(const T& t, const C& context) { column_names_getter serializator; return serializator(t, context); } @@ -42,7 +42,7 @@ namespace sqlite_orm { using expression_type = std::reference_wrapper; template - std::vector operator()(const expression_type &expression, const C &context) { + std::vector operator()(const expression_type& expression, const C& context) { return get_column_names(expression.get(), context); } }; @@ -52,7 +52,7 @@ namespace sqlite_orm { using expression_type = asterisk_t; template - std::vector operator()(const expression_type &, const C &) { + std::vector operator()(const expression_type&, const C&) { std::vector res; res.push_back("*"); return res; @@ -64,7 +64,7 @@ namespace sqlite_orm { using expression_type = object_t; template - std::vector operator()(const expression_type &, const C &) { + std::vector operator()(const expression_type&, const C&) { std::vector res; res.push_back("*"); return res; @@ -76,12 +76,12 @@ namespace sqlite_orm { using expression_type = columns_t; template - std::vector operator()(const expression_type &cols, const C &context) { + std::vector operator()(const expression_type& cols, const C& context) { std::vector columnNames; columnNames.reserve(static_cast(cols.count)); auto newContext = context; newContext.skip_table_name = false; - iterate_tuple(cols.columns, [&columnNames, &newContext](auto &m) { + iterate_tuple(cols.columns, [&columnNames, &newContext](auto& m) { auto columnName = serialize(m, newContext); if(columnName.length()) { columnNames.push_back(columnName); diff --git a/dev/column_result.h b/dev/column_result.h index a32771be2..71ded29cb 100644 --- a/dev/column_result.h +++ b/dev/column_result.h @@ -11,12 +11,10 @@ #include "alias.h" #include "column.h" #include "storage_traits.h" +#include "function.h" namespace sqlite_orm { - using int64 = sqlite_int64; - using uint64 = sqlite_uint64; - namespace internal { /** @@ -31,6 +29,14 @@ namespace sqlite_orm { template struct column_result_t; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct column_result_t, void> { + using type = std::optional::type>; + }; + +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template struct column_result_t + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + /** * Common case for all getter types. Getter types are defined in column.h file */ @@ -56,12 +72,17 @@ namespace sqlite_orm { }; template - struct column_result_t, void> { + struct column_result_t, void> { using type = R; }; + template + struct column_result_t, void> { + using type = typename callable_arguments::return_type; + }; + template - struct column_result_t, S, X>, void> { + struct column_result_t, S, X>, void> { using type = std::unique_ptr::type>; }; @@ -197,6 +218,11 @@ namespace sqlite_orm { using type = left_result; }; + template + struct column_result_t::value>::type> { + using type = typename T::result_type; + }; + /** * Result for the most simple queries like `SELECT 1` */ @@ -209,7 +235,7 @@ namespace sqlite_orm { * Result for the most simple queries like `SELECT 'ototo'` */ template - struct column_result_t { + struct column_result_t { using type = std::string; }; diff --git a/dev/conditions.h b/dev/conditions.h index cf09c3e08..7ab76df19 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -3,19 +3,17 @@ #include // std::string #include // std::enable_if, std::is_same #include // std::vector -#include // std::tuple +#include // std::tuple, std::tuple_size +#include // std::stringstream #include "collate_argument.h" #include "constraints.h" #include "optional_container.h" -#include "negatable.h" +#include "tags.h" +#include "expression.h" namespace sqlite_orm { - namespace internal { - struct arithmetic_t; - } - namespace internal { struct limit_string { @@ -30,7 +28,7 @@ namespace sqlite_orm { template struct limit_t : limit_string { T lim; - internal::optional_container off; + optional_container off; limit_t() = default; @@ -59,23 +57,18 @@ namespace sqlite_orm { template struct is_offset> : std::true_type {}; - /** - * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators - */ - struct condition_t {}; - /** * Collated something */ template struct collate_t : public condition_t { T expr; - internal::collate_argument argument; + collate_argument argument; - collate_t(T expr_, internal::collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} + collate_t(T expr_, collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} operator std::string() const { - return constraints::collate_t{this->argument}; + return collate_constraint_t{this->argument}; } }; @@ -115,11 +108,16 @@ namespace sqlite_orm { /** * Base class for binary conditions + * L is left argument type + * R is right argument type + * S is 'string' class (a class which has cast to `std::string` operator) + * Res is result type */ - template - struct binary_condition : public condition_t { + template + struct binary_condition : condition_t, S { using left_type = L; using right_type = R; + using result_type = Res; left_type l; right_type r; @@ -139,12 +137,17 @@ namespace sqlite_orm { * Result of and operator */ template - struct and_condition_t : binary_condition, and_condition_string { - using super = binary_condition; + struct and_condition_t : binary_condition { + using super = binary_condition; using super::super; }; + template + and_condition_t make_and_condition(L left, R right) { + return {std::move(left), std::move(right)}; + } + struct or_condition_string { operator std::string() const { return "OR"; @@ -155,12 +158,17 @@ namespace sqlite_orm { * Result of or operator */ template - struct or_condition_t : binary_condition, or_condition_string { - using super = binary_condition; + struct or_condition_t : binary_condition { + using super = binary_condition; using super::super; }; + template + or_condition_t make_or_condition(L left, R right) { + return {std::move(left), std::move(right)}; + } + struct is_equal_string { operator std::string() const { return "="; @@ -171,26 +179,35 @@ namespace sqlite_orm { * = and == operators object */ template - struct is_equal_t : binary_condition, is_equal_string, internal::negatable_t { + struct is_equal_t : binary_condition, negatable_t { using self = is_equal_t; - using binary_condition::binary_condition; + using binary_condition::binary_condition; collate_t collate_binary() const { - return {*this, internal::collate_argument::binary}; + return {*this, collate_argument::binary}; } collate_t collate_nocase() const { - return {*this, internal::collate_argument::nocase}; + return {*this, collate_argument::nocase}; } collate_t collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; + return {*this, collate_argument::rtrim}; } named_collate collate(std::string name) const { return {*this, std::move(name)}; } + + template + named_collate collate() const { + std::stringstream ss; + ss << C::name(); + auto name = ss.str(); + ss.flush(); + return {*this, std::move(name)}; + } }; struct is_not_equal_string { @@ -203,21 +220,21 @@ namespace sqlite_orm { * != operator object */ template - struct is_not_equal_t : binary_condition, is_not_equal_string, internal::negatable_t { + struct is_not_equal_t : binary_condition, negatable_t { using self = is_not_equal_t; - using binary_condition::binary_condition; + using binary_condition::binary_condition; collate_t collate_binary() const { - return {*this, internal::collate_argument::binary}; + return {*this, collate_argument::binary}; } collate_t collate_nocase() const { - return {*this, internal::collate_argument::nocase}; + return {*this, collate_argument::nocase}; } collate_t collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; + return {*this, collate_argument::rtrim}; } }; @@ -231,21 +248,21 @@ namespace sqlite_orm { * > operator object. */ template - struct greater_than_t : binary_condition, greater_than_string, internal::negatable_t { + struct greater_than_t : binary_condition, negatable_t { using self = greater_than_t; - using binary_condition::binary_condition; + using binary_condition::binary_condition; collate_t collate_binary() const { - return {*this, internal::collate_argument::binary}; + return {*this, collate_argument::binary}; } collate_t collate_nocase() const { - return {*this, internal::collate_argument::nocase}; + return {*this, collate_argument::nocase}; } collate_t collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; + return {*this, collate_argument::rtrim}; } }; @@ -259,21 +276,21 @@ namespace sqlite_orm { * >= operator object. */ template - struct greater_or_equal_t : binary_condition, greater_or_equal_string, internal::negatable_t { + struct greater_or_equal_t : binary_condition, negatable_t { using self = greater_or_equal_t; - using binary_condition::binary_condition; + using binary_condition::binary_condition; collate_t collate_binary() const { - return {*this, internal::collate_argument::binary}; + return {*this, collate_argument::binary}; } collate_t collate_nocase() const { - return {*this, internal::collate_argument::nocase}; + return {*this, collate_argument::nocase}; } collate_t collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; + return {*this, collate_argument::rtrim}; } }; @@ -287,21 +304,21 @@ namespace sqlite_orm { * < operator object. */ template - struct lesser_than_t : binary_condition, lesser_than_string, internal::negatable_t { + struct lesser_than_t : binary_condition, negatable_t { using self = lesser_than_t; - using binary_condition::binary_condition; + using binary_condition::binary_condition; collate_t collate_binary() const { - return {*this, internal::collate_argument::binary}; + return {*this, collate_argument::binary}; } collate_t collate_nocase() const { - return {*this, internal::collate_argument::nocase}; + return {*this, collate_argument::nocase}; } collate_t collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; + return {*this, collate_argument::rtrim}; } }; @@ -315,21 +332,21 @@ namespace sqlite_orm { * <= operator object. */ template - struct lesser_or_equal_t : binary_condition, lesser_or_equal_string, internal::negatable_t { + struct lesser_or_equal_t : binary_condition, negatable_t { using self = lesser_or_equal_t; - using binary_condition::binary_condition; + using binary_condition::binary_condition; collate_t collate_binary() const { - return {*this, internal::collate_argument::binary}; + return {*this, collate_argument::binary}; } collate_t collate_nocase() const { - return {*this, internal::collate_argument::nocase}; + return {*this, collate_argument::nocase}; } collate_t collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; + return {*this, collate_argument::rtrim}; } }; @@ -349,13 +366,23 @@ namespace sqlite_orm { * IN operator object. */ template - struct in_t : condition_t, in_base, internal::negatable_t { - using self = in_t; + struct dynamic_in_t : condition_t, in_base, negatable_t { + using self = dynamic_in_t; + + L left; // left expression + A argument; // in arg - L l; // left expression - A arg; // in arg + dynamic_in_t(L left_, A argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} + }; + + template + struct in_t : condition_t, in_base, negatable_t { + L left; + std::tuple argument; - in_t(L l_, A arg_, bool negative_) : in_base{negative_}, l(l_), arg(std::move(arg_)) {} + in_t(L left_, decltype(argument) argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} }; struct is_null_string { @@ -368,7 +395,7 @@ namespace sqlite_orm { * IS NULL operator object. */ template - struct is_null_t : is_null_string, internal::negatable_t { + struct is_null_t : is_null_string, negatable_t { using self = is_null_t; T t; @@ -386,7 +413,7 @@ namespace sqlite_orm { * IS NOT NULL operator object. */ template - struct is_not_null_t : is_not_null_string, internal::negatable_t { + struct is_not_null_t : is_not_null_string, negatable_t { using self = is_not_null_t; T t; @@ -394,32 +421,14 @@ namespace sqlite_orm { is_not_null_t(T t_) : t(std::move(t_)) {} }; - struct where_string { - operator std::string() const { - return "WHERE"; - } - }; - - /** - * WHERE argument holder. - * C is conditions type. Can be any condition like: is_equal_t, is_null_t, exists_t etc - */ - template - struct where_t : where_string { - C c; - - where_t(C c_) : c(std::move(c_)) {} - }; - - template - struct is_where : std::false_type {}; - - template - struct is_where> : std::true_type {}; - struct order_by_base { int asc_desc = 0; // 1: asc, -1: desc std::string _collate_argument; + + order_by_base() = default; + + order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : + asc_desc(asc_desc_), _collate_argument(move(_collate_argument_)) {} }; struct order_by_string { @@ -433,11 +442,12 @@ namespace sqlite_orm { */ template struct order_by_t : order_by_base, order_by_string { - using self = order_by_t; + using expression_type = O; + using self = order_by_t; - O o; + expression_type expression; - order_by_t(O o_) : o(std::move(o_)) {} + order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} self asc() { auto res = *this; @@ -453,22 +463,22 @@ namespace sqlite_orm { self collate_binary() const { auto res = *this; - res._collate_argument = constraints::collate_t::string_from_collate_argument( - sqlite_orm::internal::collate_argument::binary); + res._collate_argument = + collate_constraint_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::binary); return res; } self collate_nocase() const { auto res = *this; - res._collate_argument = constraints::collate_t::string_from_collate_argument( - sqlite_orm::internal::collate_argument::nocase); + res._collate_argument = + collate_constraint_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::nocase); return res; } self collate_rtrim() const { auto res = *this; res._collate_argument = - constraints::collate_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::rtrim); + collate_constraint_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::rtrim); return res; } @@ -477,6 +487,15 @@ namespace sqlite_orm { res._collate_argument = std::move(name); return res; } + + template + self collate() const { + std::stringstream ss; + ss << C::name(); + auto name = ss.str(); + ss.flush(); + return this->collate(move(name)); + } }; /** @@ -488,7 +507,7 @@ namespace sqlite_orm { args_type args; - multi_order_by_t(args_type &&args_) : args(std::move(args_)) {} + multi_order_by_t(args_type&& args_) : args(std::move(args_)) {} }; struct dynamic_order_by_entry_t : order_by_base { @@ -507,13 +526,13 @@ namespace sqlite_orm { using entry_t = dynamic_order_by_entry_t; using const_iterator = typename std::vector::const_iterator; - dynamic_order_by_t(const context_t &context_) : context(context_) {} + dynamic_order_by_t(const context_t& context_) : context(context_) {} template void push_back(order_by_t order_by) { auto newContext = this->context; newContext.skip_table_name = true; - auto columnName = serialize(order_by.o, newContext); + auto columnName = serialize(order_by.expression, newContext); entries.emplace_back(move(columnName), order_by.asc_desc, move(order_by._collate_argument)); } @@ -560,7 +579,7 @@ namespace sqlite_orm { using args_type = std::tuple; args_type args; - group_by_t(args_type &&args_) : args(std::move(args_)) {} + group_by_t(args_type&& args_) : args(std::move(args_)) {} }; template @@ -602,7 +621,7 @@ namespace sqlite_orm { * LIKE operator object. */ template - struct like_t : condition_t, like_string, internal::negatable_t { + struct like_t : condition_t, like_string, negatable_t { using self = like_t; using arg_t = A; using pattern_t = T; @@ -610,15 +629,14 @@ namespace sqlite_orm { arg_t arg; pattern_t pattern; - sqlite_orm::internal::optional_container - arg3; // not escape cause escape exists as a function here + optional_container arg3; // not escape cause escape exists as a function here - like_t(arg_t arg_, pattern_t pattern_, sqlite_orm::internal::optional_container escape_) : + like_t(arg_t arg_, pattern_t pattern_, optional_container escape_) : arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {} template like_t escape(C c) const { - sqlite_orm::internal::optional_container newArg3{std::move(c)}; + optional_container newArg3{std::move(c)}; return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; } }; @@ -849,6 +867,26 @@ namespace sqlite_orm { cast_t(expression_type expression_) : expression(std::move(expression_)) {} }; + template + struct from_t { + using tuple_type = std::tuple; + }; + + template + struct is_from : std::false_type {}; + + template + struct is_from> : std::true_type {}; + } + + /** + * Explicit FROM function. Usage: + * `storage.select(&User::id, from());` + */ + template + internal::from_t from() { + static_assert(std::tuple_size>::value > 0, ""); + return {}; } template::value>::type> @@ -861,152 +899,152 @@ namespace sqlite_orm { */ template internal::lesser_than_t operator<(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template internal::lesser_than_t operator<(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template internal::lesser_or_equal_t operator<=(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template internal::lesser_or_equal_t operator<=(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template internal::greater_than_t operator>(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template internal::greater_than_t operator>(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template internal::greater_or_equal_t operator>=(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template internal::greater_or_equal_t operator>=(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template internal::is_equal_t operator==(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template internal::is_equal_t operator==(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template internal::is_not_equal_t operator!=(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template internal::is_not_equal_t operator!=(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template internal::conc_t operator||(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template internal::conc_t operator||(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template internal::conc_t operator||(internal::expression_t l, internal::expression_t r) { - return {std::move(l.t), std::move(r.t)}; + return {std::move(l.value), std::move(r.value)}; } template internal::add_t operator+(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template internal::add_t operator+(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template internal::add_t operator+(internal::expression_t l, internal::expression_t r) { - return {std::move(l.t), std::move(r.t)}; + return {std::move(l.value), std::move(r.value)}; } template internal::sub_t operator-(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template internal::sub_t operator-(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template internal::sub_t operator-(internal::expression_t l, internal::expression_t r) { - return {std::move(l.t), std::move(r.t)}; + return {std::move(l.value), std::move(r.value)}; } template internal::mul_t operator*(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template internal::mul_t operator*(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template internal::mul_t operator*(internal::expression_t l, internal::expression_t r) { - return {std::move(l.t), std::move(r.t)}; + return {std::move(l.value), std::move(r.value)}; } template internal::div_t operator/(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template internal::div_t operator/(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template internal::div_t operator/(internal::expression_t l, internal::expression_t r) { - return {std::move(l.t), std::move(r.t)}; + return {std::move(l.value), std::move(r.value)}; } template internal::mod_t operator%(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; + return {std::move(expr.value), std::move(r)}; } template internal::mod_t operator%(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; + return {std::move(l), std::move(expr.value)}; } template internal::mod_t operator%(internal::expression_t l, internal::expression_t r) { - return {std::move(l.t), std::move(r.t)}; + return {std::move(l.value), std::move(r.value)}; } template @@ -1074,16 +1112,30 @@ namespace sqlite_orm { class R, typename = typename std::enable_if::value || std::is_base_of::value>::type> - internal::and_condition_t operator&&(L l, R r) { - return {std::move(l), std::move(r)}; + auto operator&&(L l, R r) { + using internal::get_from_expression; + return internal::make_and_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); + } + + template + auto and_(L l, R r) { + using internal::get_from_expression; + return internal::make_and_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); } template::value || std::is_base_of::value>::type> - internal::or_condition_t operator||(L l, R r) { - return {std::move(l), std::move(r)}; + auto operator||(L l, R r) { + using internal::get_from_expression; + return internal::make_or_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); + } + + template + auto or_(L l, R r) { + using internal::get_from_expression; + return internal::make_or_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); } template @@ -1097,32 +1149,32 @@ namespace sqlite_orm { } template - internal::in_t> in(L l, std::vector values) { + internal::dynamic_in_t> in(L l, std::vector values) { return {std::move(l), std::move(values), false}; } template - internal::in_t> in(L l, std::initializer_list values) { + internal::dynamic_in_t> in(L l, std::initializer_list values) { return {std::move(l), std::move(values), false}; } template - internal::in_t in(L l, A arg) { + internal::dynamic_in_t in(L l, A arg) { return {std::move(l), std::move(arg), false}; } template - internal::in_t> not_in(L l, std::vector values) { + internal::dynamic_in_t> not_in(L l, std::vector values) { return {std::move(l), std::move(values), true}; } template - internal::in_t> not_in(L l, std::initializer_list values) { + internal::dynamic_in_t> not_in(L l, std::initializer_list values) { return {std::move(l), std::move(values), true}; } template - internal::in_t not_in(L l, A arg) { + internal::dynamic_in_t not_in(L l, A arg) { return {std::move(l), std::move(arg), true}; } @@ -1186,11 +1238,6 @@ namespace sqlite_orm { return {std::move(l), std::move(r)}; } - template - internal::where_t where(C c) { - return {std::move(c)}; - } - /** * ORDER BY column * Example: storage.select(&User::name, order_by(&User::id)) @@ -1205,7 +1252,7 @@ namespace sqlite_orm { * Example: storage.get_all(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) */ template - internal::multi_order_by_t multi_order_by(Args &&... args) { + internal::multi_order_by_t multi_order_by(Args&&... args) { return {std::make_tuple(std::forward(args)...)}; } @@ -1223,7 +1270,7 @@ namespace sqlite_orm { */ template internal::dynamic_order_by_t> - dynamic_order_by(const S &storage) { + dynamic_order_by(const S& storage) { internal::serializator_context_builder builder(storage); return builder(); } @@ -1233,7 +1280,7 @@ namespace sqlite_orm { * Example: storage.get_all(group_by(&Employee::name)) */ template - internal::group_by_t group_by(Args &&... args) { + internal::group_by_t group_by(Args&&... args) { return {std::make_tuple(std::forward(args)...)}; } diff --git a/dev/connection_holder.h b/dev/connection_holder.h index d76e82279..e1857a759 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -36,7 +36,7 @@ namespace sqlite_orm { } } - sqlite3 *get() const { + sqlite3* get() const { return this->db; } @@ -47,20 +47,20 @@ namespace sqlite_orm { const std::string filename; protected: - sqlite3 *db = nullptr; + sqlite3* db = nullptr; int _retain_count = 0; }; struct connection_ref { - connection_ref(connection_holder &holder_) : holder(holder_) { + connection_ref(connection_holder& holder_) : holder(holder_) { this->holder.retain(); } - connection_ref(const connection_ref &other) : holder(other.holder) { + connection_ref(const connection_ref& other) : holder(other.holder) { this->holder.retain(); } - connection_ref(connection_ref &&other) : holder(other.holder) { + connection_ref(connection_ref&& other) : holder(other.holder) { this->holder.retain(); } @@ -68,12 +68,12 @@ namespace sqlite_orm { this->holder.release(); } - sqlite3 *get() const { + sqlite3* get() const { return this->holder.get(); } protected: - connection_holder &holder; + connection_holder& holder; }; } } diff --git a/dev/constraints.h b/dev/constraints.h index 1b21964a1..92f93ce64 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -6,9 +6,12 @@ #include // std::is_base_of, std::false_type, std::true_type #include // std::ostream +#include "table_type.h" +#include "tuple_helper/tuple_helper.h" + namespace sqlite_orm { - namespace constraints { + namespace internal { /** * AUTOINCREMENT constraint class. @@ -126,7 +129,7 @@ namespace sqlite_orm { cascade, }; - inline std::ostream &operator<<(std::ostream &os, foreign_key_action action) { + inline std::ostream& operator<<(std::ostream& os, foreign_key_action action) { switch(action) { case decltype(action)::no_action: os << "NO ACTION"; @@ -168,7 +171,7 @@ namespace sqlite_orm { struct on_update_delete_t : on_update_delete_base { using foreign_key_type = F; - const foreign_key_type &fk; + const foreign_key_type& fk; on_update_delete_t(decltype(fk) fk_, decltype(update) update_, foreign_key_action action_) : on_update_delete_base{update_}, fk(fk_), _action(action_) {} @@ -236,6 +239,16 @@ namespace sqlite_orm { using references_type = std::tuple; using self = foreign_key_t; + /** + * Holds obect type of all referenced columns. + */ + using target_type = typename same_or_void::type...>::type; + + /** + * Holds obect type of all source columns. + */ + using source_type = typename same_or_void::type...>::type; + columns_type columns; references_type references; @@ -244,16 +257,17 @@ namespace sqlite_orm { static_assert(std::tuple_size::value == std::tuple_size::value, "Columns size must be equal to references tuple"); + static_assert(!std::is_same::value, "All references must have the same type"); foreign_key_t(columns_type columns_, references_type references_) : columns(std::move(columns_)), references(std::move(references_)), on_update(*this, true, foreign_key_action::none), on_delete(*this, false, foreign_key_action::none) {} - foreign_key_t(const self &other) : + foreign_key_t(const self& other) : columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), on_delete(*this, false, other.on_delete._action) {} - self &operator=(const self &other) { + self& operator=(const self& other) { this->columns = other.columns; this->references = other.references; this->on_update = {*this, true, other.on_update._action}; @@ -262,7 +276,7 @@ namespace sqlite_orm { } template - void for_each_column(const L &) {} + void for_each_column(const L&) {} template constexpr bool has_every() const { @@ -290,10 +304,10 @@ namespace sqlite_orm { }; #endif - struct collate_t { + struct collate_constraint_t { internal::collate_argument argument = internal::collate_argument::binary; - collate_t(internal::collate_argument argument_) : argument(argument_) {} + collate_constraint_t(internal::collate_argument argument_) : argument(argument_) {} operator std::string() const { std::string res = "COLLATE " + this->string_from_collate_argument(this->argument); @@ -347,7 +361,7 @@ namespace sqlite_orm { struct is_constraint> : std::true_type {}; template<> - struct is_constraint : std::true_type {}; + struct is_constraint : std::true_type {}; template struct is_constraint> : std::true_type {}; @@ -373,7 +387,7 @@ namespace sqlite_orm { * Available in SQLite 3.6.19 or higher */ template - constraints::foreign_key_intermediate_t foreign_key(Cs... columns) { + internal::foreign_key_intermediate_t foreign_key(Cs... columns) { return {std::make_tuple(std::forward(columns)...)}; } #endif @@ -382,46 +396,46 @@ namespace sqlite_orm { * UNIQUE constraint builder function. */ template - constraints::unique_t unique(Args... args) { + internal::unique_t unique(Args... args) { return {std::make_tuple(std::forward(args)...)}; } - inline constraints::unique_t<> unique() { + inline internal::unique_t<> unique() { return {{}}; } - inline constraints::autoincrement_t autoincrement() { + inline internal::autoincrement_t autoincrement() { return {}; } template - constraints::primary_key_t primary_key(Cs... cs) { + internal::primary_key_t primary_key(Cs... cs) { return {std::make_tuple(std::forward(cs)...)}; } - inline constraints::primary_key_t<> primary_key() { + inline internal::primary_key_t<> primary_key() { return {{}}; } template - constraints::default_t default_value(T t) { + internal::default_t default_value(T t) { return {std::move(t)}; } - inline constraints::collate_t collate_nocase() { + inline internal::collate_constraint_t collate_nocase() { return {internal::collate_argument::nocase}; } - inline constraints::collate_t collate_binary() { + inline internal::collate_constraint_t collate_binary() { return {internal::collate_argument::binary}; } - inline constraints::collate_t collate_rtrim() { + inline internal::collate_constraint_t collate_rtrim() { return {internal::collate_argument::rtrim}; } template - constraints::check_t check(T t) { + internal::check_t check(T t) { return {std::move(t)}; } @@ -437,7 +451,7 @@ namespace sqlite_orm { * FOREIGN KEY traits. Specialized case */ template - struct is_foreign_key> : std::true_type {}; + struct is_foreign_key> : std::true_type {}; /** * PRIMARY KEY traits. Common case @@ -449,7 +463,24 @@ namespace sqlite_orm { * PRIMARY KEY traits. Specialized case */ template - struct is_primary_key> : public std::true_type {}; + struct is_primary_key> : public std::true_type {}; + + /** + * PRIMARY KEY INSERTABLE traits. + */ + template + struct is_primary_key_insertable { + using field_type = typename T::field_type; + using constraints_type = typename T::constraints_type; + + static_assert((tuple_helper::tuple_contains_type, constraints_type>::value), + "an unexpected type was passed"); + + static constexpr bool value = + (tuple_helper::tuple_contains_some_type::value || + tuple_helper::tuple_contains_type::value || + std::is_base_of>::value); + }; } } diff --git a/dev/core_functions.h b/dev/core_functions.h index 27454be20..b2402c213 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -9,11 +9,18 @@ #include "conditions.h" #include "operators.h" #include "is_base_of_template.h" +#include "serialize_result_type.h" namespace sqlite_orm { + using int64 = sqlite_int64; + using uint64 = sqlite_uint64; + namespace internal { + template + struct is_into; + template struct unique_ptr_result_of {}; @@ -24,7 +31,7 @@ namespace sqlite_orm { * Args - function arguments types */ template - struct core_function_t : S, internal::arithmetic_t { + struct built_in_function_t : S, arithmetic_t { using return_type = R; using string_type = S; using args_type = std::tuple; @@ -33,89 +40,113 @@ namespace sqlite_orm { args_type args; - core_function_t(args_type &&args_) : args(std::move(args_)) {} + built_in_function_t(args_type&& args_) : args(std::move(args_)) {} + }; + + struct typeof_string { + serialize_result_type serialize() const { + return "TYPEOF"; + } + }; + + struct unicode_string { + serialize_result_type serialize() const { + return "UNICODE"; + } }; struct length_string { - operator std::string() const { + serialize_result_type serialize() const { return "LENGTH"; } }; struct abs_string { - operator std::string() const { + serialize_result_type serialize() const { return "ABS"; } }; struct lower_string { - operator std::string() const { + serialize_result_type serialize() const { return "LOWER"; } }; struct upper_string { - operator std::string() const { + serialize_result_type serialize() const { return "UPPER"; } }; + struct last_insert_rowid_string { + serialize_result_type serialize() const { + return "LAST_INSERT_ROWID"; + } + }; + + struct total_changes_string { + serialize_result_type serialize() const { + return "TOTAL_CHANGES"; + } + }; + struct changes_string { - operator std::string() const { + serialize_result_type serialize() const { return "CHANGES"; } }; struct trim_string { - operator std::string() const { + serialize_result_type serialize() const { return "TRIM"; } }; struct ltrim_string { - operator std::string() const { + serialize_result_type serialize() const { return "LTRIM"; } }; struct rtrim_string { - operator std::string() const { + serialize_result_type serialize() const { return "RTRIM"; } }; struct hex_string { - operator std::string() const { + serialize_result_type serialize() const { return "HEX"; } }; struct quote_string { - operator std::string() const { + serialize_result_type serialize() const { return "QUOTE"; } }; struct randomblob_string { - operator std::string() const { + serialize_result_type serialize() const { return "RANDOMBLOB"; } }; struct instr_string { - operator std::string() const { + serialize_result_type serialize() const { return "INSTR"; } }; struct replace_string { - operator std::string() const { + serialize_result_type serialize() const { return "REPLACE"; } }; struct round_string { - operator std::string() const { + serialize_result_type serialize() const { return "ROUND"; } }; @@ -123,13 +154,13 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3007016 struct char_string { - operator std::string() const { + serialize_result_type serialize() const { return "CHAR"; } }; struct random_string { - operator std::string() const { + serialize_result_type serialize() const { return "RANDOM"; } }; @@ -137,73 +168,79 @@ namespace sqlite_orm { #endif struct coalesce_string { - operator std::string() const { + serialize_result_type serialize() const { return "COALESCE"; } }; + struct ifnull_string { + serialize_result_type serialize() const { + return "IFNULL"; + } + }; + struct date_string { - operator std::string() const { + serialize_result_type serialize() const { return "DATE"; } }; struct time_string { - operator std::string() const { + serialize_result_type serialize() const { return "TIME"; } }; struct datetime_string { - operator std::string() const { + serialize_result_type serialize() const { return "DATETIME"; } }; struct julianday_string { - operator std::string() const { + serialize_result_type serialize() const { return "JULIANDAY"; } }; struct strftime_string { - operator std::string() const { + serialize_result_type serialize() const { return "STRFTIME"; } }; struct zeroblob_string { - operator std::string() const { + serialize_result_type serialize() const { return "ZEROBLOB"; } }; struct substr_string { - operator std::string() const { + serialize_result_type serialize() const { return "SUBSTR"; } }; #ifdef SQLITE_SOUNDEX struct soundex_string { - operator std::string() const { + serialize_result_type serialize() const { return "SOUNDEX"; } }; #endif struct total_string { - operator std::string() const { + serialize_result_type serialize() const { return "TOTAL"; } }; struct sum_string { - operator std::string() const { + serialize_result_type serialize() const { return "SUM"; } }; struct count_string { - operator std::string() const { + serialize_result_type serialize() const { return "COUNT"; } }; @@ -241,123 +278,1254 @@ namespace sqlite_orm { struct count_asterisk_without_type : count_string {}; struct avg_string { - operator std::string() const { + serialize_result_type serialize() const { return "AVG"; } }; struct max_string { - operator std::string() const { + serialize_result_type serialize() const { return "MAX"; } }; struct min_string { - operator std::string() const { + serialize_result_type serialize() const { return "MIN"; } }; struct group_concat_string { - operator std::string() const { + serialize_result_type serialize() const { return "GROUP_CONCAT"; } }; +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + struct acos_string { + serialize_result_type serialize() const { + return "ACOS"; + } + }; + + struct acosh_string { + serialize_result_type serialize() const { + return "ACOSH"; + } + }; + + struct asin_string { + serialize_result_type serialize() const { + return "ASIN"; + } + }; + + struct asinh_string { + serialize_result_type serialize() const { + return "ASINH"; + } + }; + + struct atan_string { + serialize_result_type serialize() const { + return "ATAN"; + } + }; + + struct atan2_string { + serialize_result_type serialize() const { + return "ATAN2"; + } + }; + + struct atanh_string { + serialize_result_type serialize() const { + return "ATANH"; + } + }; + + struct ceil_string { + serialize_result_type serialize() const { + return "CEIL"; + } + }; + + struct ceiling_string { + serialize_result_type serialize() const { + return "CEILING"; + } + }; + + struct cos_string { + serialize_result_type serialize() const { + return "COS"; + } + }; + + struct cosh_string { + serialize_result_type serialize() const { + return "COSH"; + } + }; + + struct degrees_string { + serialize_result_type serialize() const { + return "DEGREES"; + } + }; + + struct exp_string { + serialize_result_type serialize() const { + return "EXP"; + } + }; + + struct floor_string { + serialize_result_type serialize() const { + return "FLOOR"; + } + }; + + struct ln_string { + serialize_result_type serialize() const { + return "LN"; + } + }; + + struct log_string { + serialize_result_type serialize() const { + return "LOG"; + } + }; + + struct log10_string { + serialize_result_type serialize() const { + return "LOG10"; + } + }; + + struct log2_string { + serialize_result_type serialize() const { + return "LOG2"; + } + }; + + struct mod_string { + serialize_result_type serialize() const { + return "MOD"; + } + }; + + struct pi_string { + serialize_result_type serialize() const { + return "PI"; + } + }; + + struct pow_string { + serialize_result_type serialize() const { + return "POW"; + } + }; + + struct power_string { + serialize_result_type serialize() const { + return "POWER"; + } + }; + + struct radians_string { + serialize_result_type serialize() const { + return "RADIANS"; + } + }; + + struct sin_string { + serialize_result_type serialize() const { + return "SIN"; + } + }; + + struct sinh_string { + serialize_result_type serialize() const { + return "SINH"; + } + }; + + struct sqrt_string { + serialize_result_type serialize() const { + return "SQRT"; + } + }; + struct tan_string { + serialize_result_type serialize() const { + return "TAN"; + } + }; + + struct tanh_string { + serialize_result_type serialize() const { + return "TANH"; + } + }; + + struct trunc_string { + serialize_result_type serialize() const { + return "TRUNC"; + } + }; + +#endif // SQLITE_ENABLE_MATH_FUNCTIONS +#ifdef SQLITE_ENABLE_JSON1 + struct json_string { + serialize_result_type serialize() const { + return "JSON"; + } + }; + + struct json_array_string { + serialize_result_type serialize() const { + return "JSON_ARRAY"; + } + }; + + struct json_array_length_string { + serialize_result_type serialize() const { + return "JSON_ARRAY_LENGTH"; + } + }; + + struct json_extract_string { + serialize_result_type serialize() const { + return "JSON_EXTRACT"; + } + }; + + struct json_insert_string { + serialize_result_type serialize() const { + return "JSON_INSERT"; + } + }; + + struct json_replace_string { + serialize_result_type serialize() const { + return "JSON_REPLACE"; + } + }; + + struct json_set_string { + serialize_result_type serialize() const { + return "JSON_SET"; + } + }; + + struct json_object_string { + serialize_result_type serialize() const { + return "JSON_OBJECT"; + } + }; + + struct json_patch_string { + serialize_result_type serialize() const { + return "JSON_PATCH"; + } + }; + + struct json_remove_string { + serialize_result_type serialize() const { + return "JSON_REMOVE"; + } + }; + + struct json_type_string { + serialize_result_type serialize() const { + return "JSON_TYPE"; + } + }; + + struct json_valid_string { + serialize_result_type serialize() const { + return "JSON_VALID"; + } + }; + + struct json_quote_string { + serialize_result_type serialize() const { + return "JSON_QUOTE"; + } + }; + + struct json_group_array_string { + serialize_result_type serialize() const { + return "JSON_GROUP_ARRAY"; + } + }; + + struct json_group_object_string { + serialize_result_type serialize() const { + return "JSON_GROUP_OBJECT"; + } + }; +#endif // SQLITE_ENABLE_JSON1 } /** * Cute operators for core functions */ - - template< - class F, - class R, - typename = typename std::enable_if::value>::type> + template::value>::type> internal::lesser_than_t operator<(F f, R r) { return {std::move(f), std::move(r)}; } - template< - class F, - class R, - typename = typename std::enable_if::value>::type> + template::value>::type> internal::lesser_or_equal_t operator<=(F f, R r) { return {std::move(f), std::move(r)}; } - template< - class F, - class R, - typename = typename std::enable_if::value>::type> + template::value>::type> internal::greater_than_t operator>(F f, R r) { return {std::move(f), std::move(r)}; } - template< - class F, - class R, - typename = typename std::enable_if::value>::type> + template::value>::type> internal::greater_or_equal_t operator>=(F f, R r) { return {std::move(f), std::move(r)}; } - template< - class F, - class R, - typename = typename std::enable_if::value>::type> + template::value>::type> internal::is_equal_t operator==(F f, R r) { return {std::move(f), std::move(r)}; } - template< - class F, - class R, - typename = typename std::enable_if::value>::type> + template::value>::type> internal::is_not_equal_t operator!=(F f, R r) { return {std::move(f), std::move(r)}; } +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + + /** + * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acos(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t acos(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acos>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t acos(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acosh(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t acosh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acosh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t acosh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asin(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t asin(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asin>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t asin(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asinh(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t asinh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asinh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t asinh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan(1)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t atan(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan>(1)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t atan(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan2(1, 3)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t atan2(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan2>(1, 3)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t atan2(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atanh(1)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t atanh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atanh>(1)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t atanh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceil(&User::rating)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t ceil(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceil>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t ceil(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceiling(&User::rating)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t ceiling(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceiling>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t ceiling(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cos(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t cos(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cos>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t cos(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cosh(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t cosh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cosh>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t cosh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees + * + * Example: + * + * auto rows = storage.select(sqlite_orm::degrees(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t degrees(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::degrees>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t degrees(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp + * + * Example: + * + * auto rows = storage.select(sqlite_orm::exp(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t exp(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::exp>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t exp(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor + * + * Example: + * + * auto rows = storage.select(sqlite_orm::floor(&User::rating)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t floor(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::floor>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t floor(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ln(200)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t ln(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ln>(200)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t ln(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log(100)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log>(100)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log10(100)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log10(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log10>(100)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log10(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log(10, 100)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log(B b, X x) { + return {std::tuple{std::forward(b), std::forward(x)}}; + } + + /** + * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log>(10, 100)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log(B b, X x) { + return {std::tuple{std::forward(b), std::forward(x)}}; + } + + /** + * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log2(64)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log2(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log2>(64)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log2(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod + * + * Example: + * + * auto rows = storage.select(sqlite_orm::mod_f(6, 5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t mod_f(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::mod_f>(6, 5)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t mod_f(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * PI() function https://www.sqlite.org/lang_mathfunc.html#pi + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pi()); // decltype(rows) is std::vector + */ + inline internal::built_in_function_t pi() { + return {{}}; + } + + /** + * PI() function https://www.sqlite.org/lang_mathfunc.html#pi + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, etc. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pi()); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t pi() { + return {{}}; + } + + /** + * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pow(2, 5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t pow(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pow>(2, 5)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t pow(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Example: + * + * auto rows = storage.select(sqlite_orm::power(2, 5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t power(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::power>(2, 5)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t power(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians + * + * Example: + * + * auto rows = storage.select(sqlite_orm::radians(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t radians(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::radians>(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t radians(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sin(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sin(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sin>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t sin(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sinh(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sinh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sinh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t sinh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sqrt(25)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sqrt(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sqrt(25)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sqrt(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tan(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tan(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tan(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tan(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tanh(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tanh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tanh(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tanh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc + * + * Example: + * + * auto rows = storage.select(sqlite_orm::trunc(5.5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t trunc(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::trunc(5.5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t trunc(X x) { + return {std::tuple{std::forward(x)}}; + } +#endif // SQLITE_ENABLE_MATH_FUNCTIONS + /** + * TYPEOF(x) function https://sqlite.org/lang_corefunc.html#typeof + */ + template + internal::built_in_function_t typeof_(T t) { + return {std::tuple{std::forward(t)}}; + } + + /** + * UNICODE(x) function https://sqlite.org/lang_corefunc.html#unicode + */ + template + internal::built_in_function_t unicode(T t) { + return {std::tuple{std::forward(t)}}; + } /** * LENGTH(x) function https://sqlite.org/lang_corefunc.html#length */ template - internal::core_function_t length(T t) { - std::tuple args{std::forward(t)}; - return {move(args)}; + internal::built_in_function_t length(T t) { + return {std::tuple{std::forward(t)}}; } /** * ABS(x) function https://sqlite.org/lang_corefunc.html#abs */ template - internal::core_function_t, internal::abs_string, T> abs(T t) { - std::tuple args{std::forward(t)}; - return {move(args)}; + internal::built_in_function_t, internal::abs_string, T> abs(T t) { + return {std::tuple{std::forward(t)}}; } /** * LOWER(x) function https://sqlite.org/lang_corefunc.html#lower */ template - internal::core_function_t lower(T t) { - std::tuple args{std::forward(t)}; - return {move(args)}; + internal::built_in_function_t lower(T t) { + return {std::tuple{std::forward(t)}}; } /** * UPPER(x) function https://sqlite.org/lang_corefunc.html#upper */ template - internal::core_function_t upper(T t) { - std::tuple args{std::forward(t)}; - return {move(args)}; + internal::built_in_function_t upper(T t) { + return {std::tuple{std::forward(t)}}; + } + + /** + * LAST_INSERT_ROWID(x) function https://www.sqlite.org/lang_corefunc.html#last_insert_rowid + */ + inline internal::built_in_function_t last_insert_rowid() { + return {{}}; + } + + /** + * TOTAL_CHANGES() function https://sqlite.org/lang_corefunc.html#total_changes + */ + inline internal::built_in_function_t total_changes() { + return {{}}; } /** * CHANGES() function https://sqlite.org/lang_corefunc.html#changes */ - inline internal::core_function_t changes() { + inline internal::built_in_function_t changes() { return {{}}; } @@ -365,117 +1533,106 @@ namespace sqlite_orm { * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim */ template - internal::core_function_t trim(T t) { - std::tuple args{std::forward(t)}; - return {move(args)}; + internal::built_in_function_t trim(T t) { + return {std::tuple{std::forward(t)}}; } /** * TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim */ template - internal::core_function_t trim(X x, Y y) { - std::tuple args{std::forward(x), std::forward(y)}; - return {move(args)}; + internal::built_in_function_t trim(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } /** * LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim */ template - internal::core_function_t ltrim(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t ltrim(X x) { + return {std::tuple{std::forward(x)}}; } /** * LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim */ template - internal::core_function_t ltrim(X x, Y y) { - std::tuple args{std::forward(x), std::forward(y)}; - return {move(args)}; + internal::built_in_function_t ltrim(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } /** * RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim */ template - internal::core_function_t rtrim(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t rtrim(X x) { + return {std::tuple{std::forward(x)}}; } /** * RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim */ template - internal::core_function_t rtrim(X x, Y y) { - std::tuple args{std::forward(x), std::forward(y)}; - return {move(args)}; + internal::built_in_function_t rtrim(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } /** * HEX(X) function https://sqlite.org/lang_corefunc.html#hex */ template - internal::core_function_t hex(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t hex(X x) { + return {std::tuple{std::forward(x)}}; } /** * QUOTE(X) function https://sqlite.org/lang_corefunc.html#quote */ template - internal::core_function_t quote(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t quote(X x) { + return {std::tuple{std::forward(x)}}; } /** * RANDOMBLOB(X) function https://sqlite.org/lang_corefunc.html#randomblob */ template - internal::core_function_t, internal::randomblob_string, X> randomblob(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t, internal::randomblob_string, X> randomblob(X x) { + return {std::tuple{std::forward(x)}}; } /** * INSTR(X) function https://sqlite.org/lang_corefunc.html#instr */ template - internal::core_function_t instr(X x, Y y) { - std::tuple args{std::forward(x), std::forward(y)}; - return {move(args)}; + internal::built_in_function_t instr(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } /** * REPLACE(X) function https://sqlite.org/lang_corefunc.html#replace */ template - internal::core_function_t replace(X x, Y y, Z z) { - std::tuple args{std::forward(x), std::forward(y), std::forward(z)}; - return {move(args)}; + typename std::enable_if, internal::is_into>::value == 0, + internal::built_in_function_t>::type + replace(X x, Y y, Z z) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(z)}}; } /** * ROUND(X) function https://sqlite.org/lang_corefunc.html#round */ template - internal::core_function_t round(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t round(X x) { + return {std::tuple{std::forward(x)}}; } /** * ROUND(X, Y) function https://sqlite.org/lang_corefunc.html#round */ template - internal::core_function_t round(X x, Y y) { - std::tuple args{std::forward(x), std::forward(y)}; - return {move(args)}; + internal::built_in_function_t round(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } #if SQLITE_VERSION_NUMBER >= 3007016 @@ -484,14 +1641,14 @@ namespace sqlite_orm { * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char */ template - internal::core_function_t char_(Args... args) { + internal::built_in_function_t char_(Args... args) { return {std::make_tuple(std::forward(args)...)}; } /** * RANDOM() function https://www.sqlite.org/lang_corefunc.html#random */ - inline internal::core_function_t random() { + inline internal::built_in_function_t random() { return {{}}; } @@ -501,80 +1658,80 @@ namespace sqlite_orm { * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce */ template - internal::core_function_t coalesce(Args... args) { + internal::built_in_function_t coalesce(Args... args) { return {std::make_tuple(std::forward(args)...)}; } + /** + * IFNULL(X,Y) function https://www.sqlite.org/lang_corefunc.html#ifnull + */ + template + internal::built_in_function_t ifnull(X x, Y y) { + return {std::make_tuple(std::move(x), std::move(y))}; + } + /** * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - internal::core_function_t date(Args... args) { - std::tuple t{std::forward(args)...}; - return {move(t)}; + internal::built_in_function_t date(Args... args) { + return {std::tuple{std::forward(args)...}}; } /** * TIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - internal::core_function_t time(Args... args) { - std::tuple t{std::forward(args)...}; - return {move(t)}; + internal::built_in_function_t time(Args... args) { + return {std::tuple{std::forward(args)...}}; } /** * DATETIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - internal::core_function_t datetime(Args... args) { - std::tuple t{std::forward(args)...}; - return {move(t)}; + internal::built_in_function_t datetime(Args... args) { + return {std::tuple{std::forward(args)...}}; } /** * JULIANDAY(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - internal::core_function_t julianday(Args... args) { - std::tuple t{std::forward(args)...}; - return {move(t)}; + internal::built_in_function_t julianday(Args... args) { + return {std::tuple{std::forward(args)...}}; } /** * STRFTIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - internal::core_function_t strftime(Args... args) { - std::tuple t{std::forward(args)...}; - return {move(t)}; + internal::built_in_function_t strftime(Args... args) { + return {std::tuple{std::forward(args)...}}; } /** * ZEROBLOB(N) function https://www.sqlite.org/lang_corefunc.html#zeroblob */ template - internal::core_function_t, internal::zeroblob_string, N> zeroblob(N n) { - std::tuple args{std::forward(n)}; - return {move(args)}; + internal::built_in_function_t, internal::zeroblob_string, N> zeroblob(N n) { + return {std::tuple{std::forward(n)}}; } /** * SUBSTR(X,Y) function https://www.sqlite.org/lang_corefunc.html#substr */ template - internal::core_function_t substr(X x, Y y) { - std::tuple args{std::forward(x), std::forward(y)}; - return {move(args)}; + internal::built_in_function_t substr(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } /** * SUBSTR(X,Y,Z) function https://www.sqlite.org/lang_corefunc.html#substr */ template - internal::core_function_t substr(X x, Y y, Z z) { - std::tuple args{std::forward(x), std::forward(y), std::forward(z)}; - return {move(args)}; + internal::built_in_function_t substr(X x, Y y, Z z) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(z)}}; } #ifdef SQLITE_SOUNDEX @@ -583,8 +1740,7 @@ namespace sqlite_orm { */ template internal::core_function_t soundex(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + return {std::tuple{std::forward(x)}}; } #endif @@ -592,27 +1748,24 @@ namespace sqlite_orm { * TOTAL(X) aggregate function. */ template - internal::core_function_t total(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t total(X x) { + return {std::tuple{std::forward(x)}}; } /** * SUM(X) aggregate function. */ template - internal::core_function_t, internal::sum_string, X> sum(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t, internal::sum_string, X> sum(X x) { + return {std::tuple{std::forward(x)}}; } /** * COUNT(X) aggregate function. */ template - internal::core_function_t count(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t count(X x) { + return {std::tuple{std::forward(x)}}; } /** @@ -635,47 +1788,164 @@ namespace sqlite_orm { * AVG(X) aggregate function. */ template - internal::core_function_t avg(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t avg(X x) { + return {std::tuple{std::forward(x)}}; } /** * MAX(X) aggregate function. */ template - internal::core_function_t, internal::max_string, X> max(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t, internal::max_string, X> max(X x) { + return {std::tuple{std::forward(x)}}; } /** * MIN(X) aggregate function. */ template - internal::core_function_t, internal::min_string, X> min(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t, internal::min_string, X> min(X x) { + return {std::tuple{std::forward(x)}}; } /** * GROUP_CONCAT(X) aggregate function. */ template - internal::core_function_t group_concat(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t group_concat(X x) { + return {std::tuple{std::forward(x)}}; } /** * GROUP_CONCAT(X, Y) aggregate function. */ template - internal::core_function_t group_concat(X x, Y y) { - std::tuple args{std::forward(x), std::forward(y)}; - return {move(args)}; + internal::built_in_function_t group_concat(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } +#ifdef SQLITE_ENABLE_JSON1 + template + internal::built_in_function_t json(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_array(Args... args) { + return {std::tuple{std::forward(args)...}}; + } + + template + internal::built_in_function_t json_array_length(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_array_length(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_array_length(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_array_length(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_extract(X x, Args... args) { + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_insert(X x, + Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_insert must be odd"); + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_replace(X x, + Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_replace must be odd"); + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_set(X x, Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_set must be odd"); + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_object(Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_object must be even"); + return {std::tuple{std::forward(args)...}}; + } + + template + internal::built_in_function_t json_patch(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_remove(X x, + Args... args) { + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_remove(X x, Args... args) { + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_type(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_type(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_type(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_type(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_valid(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_quote(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_group_array(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_group_object(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } +#endif // SQLITE_ENABLE_JSON1 template::value + diff --git a/dev/default_value_extractor.h b/dev/default_value_extractor.h index 1c231e140..d5ceb7a42 100644 --- a/dev/default_value_extractor.h +++ b/dev/default_value_extractor.h @@ -11,9 +11,6 @@ namespace sqlite_orm { namespace internal { - template - std::string serialize(const T &t); - /** * This class is used in tuple interation to know whether tuple constains `default_value_t` * constraint class and what it's value if it is @@ -21,12 +18,12 @@ namespace sqlite_orm { struct default_value_extractor { template - std::unique_ptr operator()(const A &) { + std::unique_ptr operator()(const A&) { return {}; } template - std::unique_ptr operator()(const constraints::default_t &t) { + std::unique_ptr operator()(const default_t& t) { serializator_context_base context; return std::make_unique(serialize(t.value, context)); } diff --git a/dev/error_code.h b/dev/error_code.h index 706bc4d26..08a527d34 100644 --- a/dev/error_code.h +++ b/dev/error_code.h @@ -23,6 +23,10 @@ namespace sqlite_orm { failed_to_init_a_backup, unknown_member_value, incorrect_order, + cannot_use_default_value, + arguments_count_does_not_match, + function_not_found, + index_is_out_of_bounds, }; } @@ -31,7 +35,7 @@ namespace sqlite_orm { class orm_error_category : public std::error_category { public: - const char *name() const noexcept override final { + const char* name() const noexcept override final { return "ORM error"; } @@ -63,6 +67,14 @@ namespace sqlite_orm { return "Unknown member value"; case orm_error_code::incorrect_order: return "Incorrect order"; + case orm_error_code::cannot_use_default_value: + return "The statement 'INSERT INTO * DEFAULT VALUES' can be used with only one row"; + case orm_error_code::arguments_count_does_not_match: + return "Arguments count does not match"; + case orm_error_code::function_not_found: + return "Function not found"; + case orm_error_code::index_is_out_of_bounds: + return "Index is out of bounds"; default: return "unknown error"; } @@ -71,7 +83,7 @@ namespace sqlite_orm { class sqlite_error_category : public std::error_category { public: - const char *name() const noexcept override final { + const char* name() const noexcept override final { return "SQLite error"; } @@ -80,18 +92,18 @@ namespace sqlite_orm { } }; - inline const orm_error_category &get_orm_error_category() { + inline const orm_error_category& get_orm_error_category() { static orm_error_category res; return res; } - inline const sqlite_error_category &get_sqlite_error_category() { + inline const sqlite_error_category& get_sqlite_error_category() { static sqlite_error_category res; return res; } template - std::string get_error_message(sqlite3 *db, T &&... args) { + std::string get_error_message(sqlite3* db, T&&... args) { std::ostringstream stream; using unpack = int[]; static_cast(unpack{0, (static_cast(static_cast(stream << args)), 0)...}); @@ -100,7 +112,7 @@ namespace sqlite_orm { } template - [[noreturn]] void throw_error(sqlite3 *db, T &&... args) { + [[noreturn]] void throw_error(sqlite3* db, T&&... args) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), get_error_message(db, std::forward(args)...)); } diff --git a/dev/expression.h b/dev/expression.h new file mode 100644 index 000000000..f060298e7 --- /dev/null +++ b/dev/expression.h @@ -0,0 +1,75 @@ +#include "operators.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct and_condition_t; + + template + struct or_condition_t; + + /** + * Is not an operator but a result of c(...) function. Has operator= overloaded which returns assign_t + */ + template + struct expression_t : condition_t { + T value; + + expression_t(T value_) : value(std::move(value_)) {} + + template + assign_t operator=(R r) const { + return {this->value, std::move(r)}; + } + + assign_t operator=(std::nullptr_t) const { + return {this->value, nullptr}; + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + assign_t operator=(std::nullopt_t) const { + return {this->value, std::nullopt}; + } +#endif + template + in_t in(Args... args) const { + return {this->value, std::make_tuple(std::forward(args)...), false}; + } + + template + in_t not_in(Args... args) const { + return {this->value, std::make_tuple(std::forward(args)...), true}; + } + + template + and_condition_t and_(R right) const { + return {this->value, std::move(right)}; + } + + template + or_condition_t or_(R right) const { + return {this->value, std::move(right)}; + } + }; + + template + T get_from_expression(T value) { + return std::move(value); + } + + template + T get_from_expression(expression_t expression) { + return std::move(expression.value); + } + } + + /** + * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or + * `storage.update(set(c(&User::name) = "Dua Lipa")); + */ + template + internal::expression_t c(T value) { + return {std::move(value)}; + } +} diff --git a/dev/expression_object_type.h b/dev/expression_object_type.h index 83e69d5da..d9803ef59 100644 --- a/dev/expression_object_type.h +++ b/dev/expression_object_type.h @@ -32,6 +32,16 @@ namespace sqlite_orm { using type = typename std::decay::type; }; + template + struct expression_object_type> { + using type = typename replace_range_t::object_type; + }; + + template + struct expression_object_type, L, O>> { + using type = typename replace_range_t, L, O>::object_type; + }; + template struct expression_object_type> { using type = typename std::decay::type; @@ -42,6 +52,17 @@ namespace sqlite_orm { using type = typename std::decay::type; }; + template + struct expression_object_type> { + using transformer_type = L; + using type = typename insert_range_t::object_type; + }; + + template + struct expression_object_type, L, O>> { + using type = typename insert_range_t, L, O>::object_type; + }; + template struct expression_object_type> { using type = typename std::decay::type; @@ -56,7 +77,7 @@ namespace sqlite_orm { struct get_ref_t { template - auto &operator()(O &t) const { + auto& operator()(O& t) const { return t; } }; @@ -65,13 +86,13 @@ namespace sqlite_orm { struct get_ref_t> { template - auto &operator()(O &t) const { + auto& operator()(O& t) const { return t.get(); } }; template - auto &get_ref(T &t) { + auto& get_ref(T& t) { using arg_type = typename std::decay::type; get_ref_t g; return g(t); @@ -84,7 +105,7 @@ namespace sqlite_orm { struct get_object_t : get_object_t {}; template - auto &get_object(T &t) { + auto& get_object(T& t) { using expression_type = typename std::decay::type; get_object_t obj; return obj(t); @@ -95,7 +116,7 @@ namespace sqlite_orm { using expression_type = replace_t; template - auto &operator()(O &e) const { + auto& operator()(O& e) const { return get_ref(e.obj); } }; @@ -105,7 +126,7 @@ namespace sqlite_orm { using expression_type = insert_t; template - auto &operator()(O &e) const { + auto& operator()(O& e) const { return get_ref(e.obj); } }; @@ -115,7 +136,7 @@ namespace sqlite_orm { using expression_type = update_t; template - auto &operator()(O &e) const { + auto& operator()(O& e) const { return get_ref(e.obj); } }; diff --git a/dev/field_printer.h b/dev/field_printer.h index e79ac8d1d..888d4ba85 100644 --- a/dev/field_printer.h +++ b/dev/field_printer.h @@ -15,9 +15,9 @@ namespace sqlite_orm { * Is used to print members mapped to objects in storage_t::dump member function. * Other developers can create own specialization to map custom types */ - template + template struct field_printer { - std::string operator()(const T &t) const { + std::string operator()(const T& t) const { std::stringstream stream; stream << t; return stream.str(); @@ -28,8 +28,8 @@ namespace sqlite_orm { * Upgrade to integer is required when using unsigned char(uint8_t) */ template<> - struct field_printer { - std::string operator()(const unsigned char &t) const { + struct field_printer { + std::string operator()(const unsigned char& t) const { std::stringstream stream; stream << +t; return stream.str(); @@ -40,8 +40,8 @@ namespace sqlite_orm { * Upgrade to integer is required when using signed char(int8_t) */ template<> - struct field_printer { - std::string operator()(const signed char &t) const { + struct field_printer { + std::string operator()(const signed char& t) const { std::stringstream stream; stream << +t; return stream.str(); @@ -52,8 +52,8 @@ namespace sqlite_orm { * char is neigher signer char nor unsigned char so it has its own specialization */ template<> - struct field_printer { - std::string operator()(const char &t) const { + struct field_printer { + std::string operator()(const char& t) const { std::stringstream stream; stream << +t; return stream.str(); @@ -61,15 +61,15 @@ namespace sqlite_orm { }; template<> - struct field_printer { - std::string operator()(const std::string &t) const { + struct field_printer { + std::string operator()(const std::string& t) const { return t; } }; template<> - struct field_printer> { - std::string operator()(const std::vector &t) const { + struct field_printer, void> { + std::string operator()(const std::vector& t) const { std::stringstream ss; ss << std::hex; for(auto c: t) { @@ -80,15 +80,22 @@ namespace sqlite_orm { }; template<> - struct field_printer { - std::string operator()(const std::nullptr_t &) const { + struct field_printer { + std::string operator()(const std::nullptr_t&) const { return "null"; } }; - +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<> + struct field_printer { + std::string operator()(const std::nullopt_t&) const { + return "null"; + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - struct field_printer> { - std::string operator()(const std::shared_ptr &t) const { + struct field_printer, void> { + std::string operator()(const std::shared_ptr& t) const { if(t) { return field_printer()(*t); } else { @@ -98,8 +105,8 @@ namespace sqlite_orm { }; template - struct field_printer> { - std::string operator()(const std::unique_ptr &t) const { + struct field_printer, void> { + std::string operator()(const std::unique_ptr& t) const { if(t) { return field_printer()(*t); } else { @@ -110,8 +117,8 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - struct field_printer> { - std::string operator()(const std::optional &t) const { + struct field_printer, void> { + std::string operator()(const std::optional& t) const { if(t.has_value()) { return field_printer()(*t); } else { diff --git a/dev/field_value_holder.h b/dev/field_value_holder.h index 80432dd1d..6dd96ad07 100644 --- a/dev/field_value_holder.h +++ b/dev/field_value_holder.h @@ -14,7 +14,7 @@ namespace sqlite_orm { struct field_value_holder::returns_lvalue>::type> { using type = typename getter_traits::field_type; - const type &value; + const type& value; }; template diff --git a/dev/function.h b/dev/function.h new file mode 100644 index 000000000..10d6a5c5b --- /dev/null +++ b/dev/function.h @@ -0,0 +1,183 @@ +#pragma once + +#include // std::string +#include +#include // std::tuple +#include // std::function + +namespace sqlite_orm { + + struct arg_values; + + namespace internal { + + struct function_base { + using func_call = std::function< + void(sqlite3_context *context, void *functionPointer, int argsCount, sqlite3_value **values)>; + using final_call = std::function; + + std::string name; + int argumentsCount = 0; + std::function create; + void (*destroy)(int *) = nullptr; + + function_base(decltype(name) name_, + decltype(argumentsCount) argumentsCount_, + decltype(create) create_, + decltype(destroy) destroy_) : + name(move(name_)), + argumentsCount(argumentsCount_), create(move(create_)), destroy(destroy_) {} + }; + + struct scalar_function_t : function_base { + func_call run; + + scalar_function_t(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(run) run_, + decltype(destroy) destroy_) : + function_base{move(name_), argumentsCount_, move(create_), destroy_}, + run(move(run_)) {} + }; + + struct aggregate_function_t : function_base { + func_call step; + final_call finalCall; + + aggregate_function_t(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(step) step_, + decltype(finalCall) finalCall_, + decltype(destroy) destroy_) : + function_base{move(name_), argumentsCount_, move(create_), destroy_}, + step(move(step_)), finalCall(move(finalCall_)) {} + }; + + // got it from here https://stackoverflow.com/questions/87372/check-if-a-class-has-a-member-function-of-a-given-signature + template + struct is_scalar_function_impl { + + template + struct SFINAE; + + template + struct SFINAE {}; + + template + static char test(SFINAE *); + + template + static int test(...); + + static constexpr bool has = sizeof(test(0)) == sizeof(char); + }; + + template + struct is_aggregate_function_impl { + + template + struct SFINAE; + + template + struct SFINAE {}; + + template + static char test(SFINAE *); + + template + static int test(...); + + template + static char test2(SFINAE *); + + template + static int test2(...); + + static constexpr bool has = sizeof(test(0)) == sizeof(char); + static constexpr bool has2 = sizeof(test2(0)) == sizeof(char); + static constexpr bool has_both = has && has2; + }; + + template + struct is_scalar_function : std::integral_constant::has> {}; + + template + struct is_aggregate_function : std::integral_constant::has_both> {}; + + template + struct scalar_run_member_pointer { + using type = decltype(&F::operator()); + }; + + template + struct aggregate_run_member_pointer { + using step_type = decltype(&F::step); + using fin_type = decltype(&F::fin); + }; + + template + struct member_function_arguments; + + template + struct member_function_arguments { + using member_function_type = R (O::*)(Args...) const; + using tuple_type = std::tuple::type...>; + using return_type = R; + }; + + template + struct member_function_arguments { + using member_function_type = R (O::*)(Args...); + using tuple_type = std::tuple::type...>; + using return_type = R; + }; + + template + struct callable_arguments_impl; + + template + struct callable_arguments_impl { + using function_member_pointer = typename scalar_run_member_pointer::type; + using args_tuple = typename member_function_arguments::tuple_type; + using return_type = typename member_function_arguments::return_type; + }; + + template + struct callable_arguments_impl { + using step_function_member_pointer = typename aggregate_run_member_pointer::step_type; + using fin_function_member_pointer = typename aggregate_run_member_pointer::fin_type; + using args_tuple = typename member_function_arguments::tuple_type; + using return_type = typename member_function_arguments::return_type; + }; + + template + struct callable_arguments : callable_arguments_impl::value> {}; + + template + struct function_call { + using function_type = F; + using args_tuple = std::tuple; + + args_tuple args; + }; + } + + /** + * Used to call user defined function: `func(...);` + */ + template + internal::function_call func(Args... args) { + using args_tuple = std::tuple; + using function_args_tuple = typename internal::callable_arguments::args_tuple; + constexpr auto argsCount = std::tuple_size::value; + constexpr auto functionArgsCount = std::tuple_size::value; + static_assert( + (argsCount == functionArgsCount && !std::is_same>::value) || + std::is_same>::value, + "Arguments amount does not match"); + return {std::make_tuple(std::forward(args)...)}; + } + +} diff --git a/dev/get_prepared_statement.h b/dev/get_prepared_statement.h index 1b2ee1e08..f561f76d6 100644 --- a/dev/get_prepared_statement.h +++ b/dev/get_prepared_statement.h @@ -9,132 +9,132 @@ namespace sqlite_orm { - template - auto &get(internal::prepared_statement_t> &statement) { + template + auto& get(internal::prepared_statement_t>& statement) { return std::get(statement.t.range); } - template - const auto &get(const internal::prepared_statement_t> &statement) { + template + const auto& get(const internal::prepared_statement_t>& statement) { return std::get(statement.t.range); } - template - auto &get(internal::prepared_statement_t> &statement) { + template + auto& get(internal::prepared_statement_t>& statement) { return std::get(statement.t.range); } - template - const auto &get(const internal::prepared_statement_t> &statement) { + template + const auto& get(const internal::prepared_statement_t>& statement) { return std::get(statement.t.range); } template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for update statement"); return internal::get_ref(statement.t.obj); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for update statement"); return internal::get_ref(statement.t.obj); } template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.t.obj); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.t.obj); } template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for replace statement"); return internal::get_ref(statement.t.obj); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for replace statement"); return internal::get_ref(statement.t.obj); } template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.t.obj); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.t.obj); } template - const auto &get(const internal::prepared_statement_t &statement) { + const auto& get(const internal::prepared_statement_t& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; using node_tuple = typename internal::node_tuple::type; using bind_tuple = typename internal::bindable_filter::type; - using result_tupe = typename std::tuple_element::type; - const result_tupe *result = nullptr; + using result_tupe = typename std::tuple_element(N), bind_tuple>::type; + const result_tupe* result = nullptr; auto index = -1; - internal::iterate_ast(statement.t, [&result, &index](auto &node) { + internal::iterate_ast(statement.t, [&result, &index](auto& node) { using node_type = typename std::decay::type; if(internal::is_bindable::value) { ++index; } if(index == N) { - internal::static_if{}>([](auto &r, auto &n) { + internal::static_if{}>([](auto& r, auto& n) { r = const_cast::type>(&n); })(result, node); } @@ -143,21 +143,21 @@ namespace sqlite_orm { } template - auto &get(internal::prepared_statement_t &statement) { + auto& get(internal::prepared_statement_t& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; using node_tuple = typename internal::node_tuple::type; using bind_tuple = typename internal::bindable_filter::type; - using result_tupe = typename std::tuple_element::type; - result_tupe *result = nullptr; + using result_tupe = typename std::tuple_element(N), bind_tuple>::type; + result_tupe* result = nullptr; auto index = -1; - internal::iterate_ast(statement.t, [&result, &index](auto &node) { + internal::iterate_ast(statement.t, [&result, &index](auto& node) { using node_type = typename std::decay::type; if(internal::is_bindable::value) { ++index; } if(index == N) { - internal::static_if{}>([](auto &r, auto &n) { + internal::static_if{}>([](auto& r, auto& n) { r = const_cast::type>(&n); })(result, node); } diff --git a/dev/getter_traits.h b/dev/getter_traits.h deleted file mode 100644 index 07b1b7585..000000000 --- a/dev/getter_traits.h +++ /dev/null @@ -1,184 +0,0 @@ -#pragma once - -namespace sqlite_orm { - - namespace internal { - - template - struct is_field_member_pointer : std::false_type {}; - - template - struct is_field_member_pointer::value && - !std::is_member_function_pointer::value>::type> - : std::true_type {}; - - template - struct field_member_traits; - - template - struct field_member_traits::value>::type> { - using object_type = O; - using field_type = F; - }; - - /** - * Getters aliases - */ - template - using getter_by_value_const = T (O::*)() const; - - template - using getter_by_value = T (O::*)(); - - template - using getter_by_ref_const = T &(O::*)() const; - - template - using getter_by_ref = T &(O::*)(); - - template - using getter_by_const_ref_const = const T &(O::*)() const; - - template - using getter_by_const_ref = const T &(O::*)(); - - /** - * Setters aliases - */ - template - using setter_by_value = void (O::*)(T); - - template - using setter_by_ref = void (O::*)(T &); - - template - using setter_by_const_ref = void (O::*)(const T &); - - template - struct is_getter : std::false_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_setter : std::false_type {}; - - template - struct is_setter> : std::true_type {}; - - template - struct is_setter> : std::true_type {}; - - template - struct is_setter> : std::true_type {}; - - template - struct getter_traits; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = false; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = false; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; - }; - - template - struct setter_traits; - - template - struct setter_traits> { - using object_type = O; - using field_type = T; - }; - - template - struct setter_traits> { - using object_type = O; - using field_type = T; - }; - - template - struct setter_traits> { - using object_type = O; - using field_type = T; - }; - - template - struct member_traits; - - template - struct member_traits::value>::type> { - using object_type = typename field_member_traits::object_type; - using field_type = typename field_member_traits::field_type; - }; - - template - struct member_traits::value>::type> { - using object_type = typename getter_traits::object_type; - using field_type = typename getter_traits::field_type; - }; - - template - struct member_traits::value>::type> { - using object_type = typename setter_traits::object_type; - using field_type = typename setter_traits::field_type; - }; - } -} diff --git a/dev/index.h b/dev/index.h index b509ede47..dec22ab33 100644 --- a/dev/index.h +++ b/dev/index.h @@ -28,13 +28,13 @@ namespace sqlite_orm { } template - internal::index_t::type...> make_index(const std::string &name, + internal::index_t::type...> make_index(const std::string& name, Cols... cols) { return {name, false, std::make_tuple(internal::make_indexed_column(cols)...)}; } template - internal::index_t::type...> make_unique_index(const std::string &name, + internal::index_t::type...> make_unique_index(const std::string& name, Cols... cols) { return {name, true, std::make_tuple(internal::make_indexed_column(cols)...)}; } diff --git a/dev/indexed_column.h b/dev/indexed_column.h index 7b19e3b43..37237de0c 100644 --- a/dev/indexed_column.h +++ b/dev/indexed_column.h @@ -10,6 +10,9 @@ namespace sqlite_orm { struct indexed_column_t { using column_type = C; + indexed_column_t(column_type _column_or_expression) : + column_or_expression(std::move(_column_or_expression)) {} + column_type column_or_expression; std::string _collation_name; int _order = 0; // -1 = desc, 1 = asc, 0 = not specified diff --git a/dev/is_base_of_template.h b/dev/is_base_of_template.h index b349d77b2..87a0540c9 100644 --- a/dev/is_base_of_template.h +++ b/dev/is_base_of_template.h @@ -14,11 +14,11 @@ namespace sqlite_orm { template class Base, typename Derived> struct is_base_of_template_impl { template - static constexpr std::true_type test(const Base *); + static constexpr std::true_type test(const Base*); static constexpr std::false_type test(...); - using type = decltype(test(std::declval())); + using type = decltype(test(std::declval())); }; template class Base> @@ -26,13 +26,13 @@ namespace sqlite_orm { #else template class C, typename... Ts> - std::true_type is_base_of_template_impl(const C *); + std::true_type is_base_of_template_impl(const C*); template class C> std::false_type is_base_of_template_impl(...); template class C> - using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); + using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); #endif } } diff --git a/dev/is_std_ptr.h b/dev/is_std_ptr.h index 42302c76f..31003cdf2 100644 --- a/dev/is_std_ptr.h +++ b/dev/is_std_ptr.h @@ -12,7 +12,7 @@ namespace sqlite_orm { struct is_std_ptr> : std::true_type { using element_type = T; - static std::shared_ptr make(const T &v) { + static std::shared_ptr make(const T& v) { return std::make_shared(v); } }; @@ -21,7 +21,7 @@ namespace sqlite_orm { struct is_std_ptr> : std::true_type { using element_type = T; - static std::unique_ptr make(const T &v) { + static std::unique_ptr make(const T& v) { return std::make_unique(v); } }; diff --git a/dev/iterator.h b/dev/iterator.h index 187d082e9..22d3dc0d5 100644 --- a/dev/iterator.h +++ b/dev/iterator.h @@ -30,8 +30,10 @@ namespace sqlite_orm { * call. When one finishes iterating it the pointer * inside the shared_ptr is nulled out in all copies. */ - std::shared_ptr stmt; - view_type &view; + std::shared_ptr stmt; + + // only null for the default constructed iterator + view_type* view; /** * shared_ptr is used over unique_ptr here @@ -39,68 +41,53 @@ namespace sqlite_orm { */ std::shared_ptr current; - void extract_value(std::unique_ptr &temp) { - temp = std::make_unique(); - auto &storage = this->view.storage; - auto &impl = storage.template get_impl(); - object_from_column_builder builder{*temp, *this->stmt}; + void extract_value() { + auto& storage = this->view->storage; + auto& impl = storage.template get_impl(); + this->current = std::make_shared(); + object_from_column_builder builder{*this->current, this->stmt->get()}; impl.table.for_each_column(builder); } public: using difference_type = std::ptrdiff_t; - using pointer = value_type *; - using reference = value_type &; + using pointer = value_type*; + using reference = value_type&; using iterator_category = std::input_iterator_tag; - iterator_t(sqlite3_stmt *stmt_, view_type &view_) : - stmt(std::make_shared(stmt_)), view(view_) { - this->operator++(); - } - - iterator_t(const iterator_t &) = default; - - iterator_t(iterator_t &&) = default; + iterator_t() : view(nullptr){}; - iterator_t &operator=(iterator_t &&) = default; - - iterator_t &operator=(const iterator_t &) = default; - - ~iterator_t() { - if(this->stmt) { - statement_finalizer f{*this->stmt}; - } + iterator_t(sqlite3_stmt* stmt_, view_type& view_) : + stmt(std::make_shared(stmt_)), view(&view_) { + next(); } - value_type &operator*() { - if(!this->stmt) { + const value_type& operator*() const { + if(!this->stmt || !this->current) { throw std::system_error(std::make_error_code(orm_error_code::trying_to_dereference_null_iterator)); } - if(!this->current) { - std::unique_ptr value; - this->extract_value(value); - this->current = move(value); - } return *this->current; } - value_type *operator->() { + const value_type* operator->() const { return &(this->operator*()); } - void operator++() { - if(this->stmt && *this->stmt) { - auto ret = sqlite3_step(*this->stmt); + private: + void next() { + this->current.reset(); + if(this->stmt) { + auto statementPointer = this->stmt->get(); + auto ret = sqlite3_step(statementPointer); switch(ret) { case SQLITE_ROW: - this->current = nullptr; + this->extract_value(); + break; + case SQLITE_DONE: + this->stmt.reset(); break; - case SQLITE_DONE: { - statement_finalizer f{*this->stmt}; - *this->stmt = nullptr; - } break; default: { - auto db = this->view.connection.get(); + auto db = this->view->connection.get(); throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } @@ -108,23 +95,21 @@ namespace sqlite_orm { } } + public: + iterator_t& operator++() { + next(); + return *this; + } + void operator++(int) { this->operator++(); } - bool operator==(const iterator_t &other) const { - if(this->stmt && other.stmt) { - return *this->stmt == *other.stmt; - } else { - if(!this->stmt && !other.stmt) { - return true; - } else { - return false; - } - } + bool operator==(const iterator_t& other) const { + return this->current == other.current; } - bool operator!=(const iterator_t &other) const { + bool operator!=(const iterator_t& other) const { return !(*this == other); } }; diff --git a/dev/join_iterator.h b/dev/join_iterator.h index 4d9777d1a..e4f2f75a4 100644 --- a/dev/join_iterator.h +++ b/dev/join_iterator.h @@ -7,19 +7,13 @@ namespace sqlite_orm { namespace internal { template - struct join_iterator { - - template - void operator()(const L &) { - //.. - } - }; + struct join_iterator; template<> struct join_iterator<> { template - void operator()(const L &) { + void operator()(const L&) const { //.. } }; @@ -29,7 +23,7 @@ namespace sqlite_orm { using super = join_iterator; template - void operator()(const L &l) { + void operator()(const L& l) const { this->super::operator()(l); } }; @@ -40,7 +34,7 @@ namespace sqlite_orm { using join_type = cross_join_t; template - void operator()(const L &l) { + void operator()(const L& l) const { l(*this); this->super::operator()(l); } @@ -52,7 +46,7 @@ namespace sqlite_orm { using join_type = natural_join_t; template - void operator()(const L &l) { + void operator()(const L& l) const { l(*this); this->super::operator()(l); } @@ -64,7 +58,7 @@ namespace sqlite_orm { using join_type = left_join_t; template - void operator()(const L &l) { + void operator()(const L& l) const { l(*this); this->super::operator()(l); } @@ -76,7 +70,7 @@ namespace sqlite_orm { using join_type = join_t; template - void operator()(const L &l) { + void operator()(const L& l) const { l(*this); this->super::operator()(l); } @@ -88,7 +82,7 @@ namespace sqlite_orm { using join_type = left_outer_join_t; template - void operator()(const L &l) { + void operator()(const L& l) const { l(*this); this->super::operator()(l); } @@ -100,7 +94,7 @@ namespace sqlite_orm { using join_type = inner_join_t; template - void operator()(const L &l) { + void operator()(const L& l) const { l(*this); this->super::operator()(l); } diff --git a/dev/journal_mode.h b/dev/journal_mode.h index 6226a7d81..70b0f6c08 100644 --- a/dev/journal_mode.h +++ b/dev/journal_mode.h @@ -25,7 +25,7 @@ namespace sqlite_orm { namespace internal { - inline const std::string &to_string(journal_mode j) { + inline const std::string& to_string(journal_mode j) { static std::string res[] = { "DELETE", "TRUNCATE", @@ -37,7 +37,7 @@ namespace sqlite_orm { return res[static_cast(j)]; } - inline std::unique_ptr journal_mode_from_string(const std::string &str) { + inline std::unique_ptr journal_mode_from_string(const std::string& str) { std::string upper_str; std::transform(str.begin(), str.end(), std::back_inserter(upper_str), [](char c) { return static_cast(std::toupper(static_cast(c))); diff --git a/dev/mapped_row_extractor.h b/dev/mapped_row_extractor.h index 5a50e7d14..c6976b925 100644 --- a/dev/mapped_row_extractor.h +++ b/dev/mapped_row_extractor.h @@ -9,27 +9,27 @@ namespace sqlite_orm { namespace internal { /** - * This is a private row extractor class. It is used for extracting rows as objects instead of tuple. - * Main difference from regular `row_extractor` is that this class takes table info which is required - * for constructing objects by member pointers. To construct please use `row_extractor_builder` class - * Type arguments: - * V is value type just like regular `row_extractor` has - * T is table info class `table_t` - */ + * This is a private row extractor class. It is used for extracting rows as objects instead of tuple. + * Main difference from regular `row_extractor` is that this class takes table info which is required + * for constructing objects by member pointers. To construct please use `row_extractor_builder` class + * Type arguments: + * V is value type just like regular `row_extractor` has + * T is table info class `table_t` + */ template struct mapped_row_extractor { using table_info_t = T; - mapped_row_extractor(const table_info_t &tableInfo_) : tableInfo(tableInfo_) {} + mapped_row_extractor(const table_info_t& tableInfo_) : tableInfo(tableInfo_) {} - V extract(sqlite3_stmt *stmt, int /*columnIndex*/) { + V extract(sqlite3_stmt* stmt, int /*columnIndex*/) { V res; object_from_column_builder builder{res, stmt}; this->tableInfo.for_each_column(builder); return res; } - const table_info_t &tableInfo; + const table_info_t& tableInfo; }; } diff --git a/dev/member_traits/field_member_traits.h b/dev/member_traits/field_member_traits.h new file mode 100644 index 000000000..40ec9ca84 --- /dev/null +++ b/dev/member_traits/field_member_traits.h @@ -0,0 +1,19 @@ +#pragma once + +#include // std::enable_if + +#include "is_field_member_pointer.h" + +namespace sqlite_orm { + namespace internal { + + template + struct field_member_traits; + + template + struct field_member_traits::value>::type> { + using object_type = O; + using field_type = F; + }; + } +} diff --git a/dev/member_traits/getter_traits.h b/dev/member_traits/getter_traits.h new file mode 100644 index 000000000..99c37ba2d --- /dev/null +++ b/dev/member_traits/getter_traits.h @@ -0,0 +1,108 @@ +#pragma once + +#include "getters.h" + +namespace sqlite_orm { + namespace internal { + + template + struct getter_traits; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = false; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = false; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = false; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = false; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; +#endif + } +} diff --git a/dev/member_traits/getters.h b/dev/member_traits/getters.h new file mode 100644 index 000000000..cfc305e41 --- /dev/null +++ b/dev/member_traits/getters.h @@ -0,0 +1,43 @@ +#pragma once + +namespace sqlite_orm { + namespace internal { + + template + using getter_by_value_const = T (O::*)() const; + + template + using getter_by_value = T (O::*)(); + + template + using getter_by_ref_const = T& (O::*)() const; + + template + using getter_by_ref = T& (O::*)(); + + template + using getter_by_const_ref_const = const T& (O::*)() const; + + template + using getter_by_const_ref = const T& (O::*)(); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + using getter_by_value_const_noexcept = T (O::*)() const noexcept; + + template + using getter_by_value_noexcept = T (O::*)() noexcept; + + template + using getter_by_ref_const_noexcept = T& (O::*)() const noexcept; + + template + using getter_by_ref_noexcept = T& (O::*)() noexcept; + + template + using getter_by_const_ref_const_noexcept = const T& (O::*)() const noexcept; + + template + using getter_by_const_ref_noexcept = const T& (O::*)() noexcept; +#endif + } +} diff --git a/dev/member_traits/is_field_member_pointer.h b/dev/member_traits/is_field_member_pointer.h new file mode 100644 index 000000000..75135ef65 --- /dev/null +++ b/dev/member_traits/is_field_member_pointer.h @@ -0,0 +1,17 @@ +#pragma once + +#include // std::false_type, std::true_type, std::is_member_pointer, std::is_member_function_pointer + +namespace sqlite_orm { + namespace internal { + + template + struct is_field_member_pointer : std::false_type {}; + + template + struct is_field_member_pointer::value && + !std::is_member_function_pointer::value>::type> + : std::true_type {}; + } +} diff --git a/dev/member_traits/is_getter.h b/dev/member_traits/is_getter.h new file mode 100644 index 000000000..616146b6f --- /dev/null +++ b/dev/member_traits/is_getter.h @@ -0,0 +1,50 @@ +#pragma once + +#include // std::false_type, std::true_type + +#include "getters.h" + +namespace sqlite_orm { + namespace internal { + + template + struct is_getter : std::false_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; +#endif + } +} diff --git a/dev/member_traits/is_setter.h b/dev/member_traits/is_setter.h new file mode 100644 index 000000000..a47961c29 --- /dev/null +++ b/dev/member_traits/is_setter.h @@ -0,0 +1,30 @@ +#pragma once + +#include "setters.h" + +namespace sqlite_orm { + namespace internal { + + template + struct is_setter : std::false_type {}; + + template + struct is_setter> : std::true_type {}; + + template + struct is_setter> : std::true_type {}; + + template + struct is_setter> : std::true_type {}; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct is_setter> : std::true_type {}; + + template + struct is_setter> : std::true_type {}; + + template + struct is_setter> : std::true_type {}; +#endif + } +} diff --git a/dev/member_traits/member_traits.h b/dev/member_traits/member_traits.h new file mode 100644 index 000000000..d02ab87cc --- /dev/null +++ b/dev/member_traits/member_traits.h @@ -0,0 +1,36 @@ +#pragma once + +#include // std::enable_if + +#include "is_field_member_pointer.h" +#include "is_getter.h" +#include "field_member_traits.h" +#include "is_setter.h" +#include "getter_traits.h" +#include "setter_traits.h" + +namespace sqlite_orm { + namespace internal { + + template + struct member_traits; + + template + struct member_traits::value>::type> { + using object_type = typename field_member_traits::object_type; + using field_type = typename field_member_traits::field_type; + }; + + template + struct member_traits::value>::type> { + using object_type = typename getter_traits::object_type; + using field_type = typename getter_traits::field_type; + }; + + template + struct member_traits::value>::type> { + using object_type = typename setter_traits::object_type; + using field_type = typename setter_traits::field_type; + }; + } +} diff --git a/dev/member_traits/setter_traits.h b/dev/member_traits/setter_traits.h new file mode 100644 index 000000000..9d54e413f --- /dev/null +++ b/dev/member_traits/setter_traits.h @@ -0,0 +1,48 @@ +#pragma once + +#include "setters.h" + +namespace sqlite_orm { + namespace internal { + + template + struct setter_traits; + + template + struct setter_traits> { + using object_type = O; + using field_type = T; + }; + + template + struct setter_traits> { + using object_type = O; + using field_type = T; + }; + + template + struct setter_traits> { + using object_type = O; + using field_type = T; + }; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct setter_traits> { + using object_type = O; + using field_type = T; + }; + + template + struct setter_traits> { + using object_type = O; + using field_type = T; + }; + + template + struct setter_traits> { + using object_type = O; + using field_type = T; + }; +#endif + } +} diff --git a/dev/member_traits/setters.h b/dev/member_traits/setters.h new file mode 100644 index 000000000..b69e5c4cb --- /dev/null +++ b/dev/member_traits/setters.h @@ -0,0 +1,25 @@ +#pragma once + +namespace sqlite_orm { + namespace internal { + + template + using setter_by_value = void (O::*)(T); + + template + using setter_by_ref = void (O::*)(T&); + + template + using setter_by_const_ref = void (O::*)(const T&); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + using setter_by_value_noexcept = void (O::*)(T) noexcept; + + template + using setter_by_ref_noexcept = void (O::*)(T&) noexcept; + + template + using setter_by_const_ref_noexcept = void (O::*)(const T&) noexcept; +#endif + } +} diff --git a/dev/negatable.h b/dev/negatable.h deleted file mode 100644 index 8bdb7de6b..000000000 --- a/dev/negatable.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -namespace sqlite_orm { - namespace internal { - struct negatable_t {}; - } -} diff --git a/dev/node_tuple.h b/dev/node_tuple.h index 66e34d478..973466d45 100644 --- a/dev/node_tuple.h +++ b/dev/node_tuple.h @@ -3,6 +3,9 @@ #include // std::tuple #include // std::pair #include // std::reference_wrapper +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED #include "conditions.h" #include "operators.h" @@ -10,6 +13,10 @@ #include "prepared_statement.h" #include "optional_container.h" #include "core_functions.h" +#include "function.h" +#include "ast/excluded.h" +#include "ast/upsert_clause.h" +#include "ast/where.h" namespace sqlite_orm { @@ -24,12 +31,32 @@ namespace sqlite_orm { struct node_tuple { using type = std::tuple<>; }; - +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct node_tuple, void> { + using type = typename node_tuple::type; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct node_tuple, void> { using type = typename node_tuple::type; }; + template + struct node_tuple, std::tuple>, void> { + using type = typename node_tuple>::type; + }; + + template + struct node_tuple, void> { + using type = typename conc_tuple::type...>::type; + }; + + template + struct node_tuple, void> { + using type = typename node_tuple::type; + }; + template struct node_tuple, void> { using node_type = where_t; @@ -63,13 +90,21 @@ namespace sqlite_orm { }; template - struct node_tuple, void> { - using node_type = in_t; + struct node_tuple, void> { + using node_type = dynamic_in_t; using left_tuple = typename node_tuple::type; using right_tuple = typename node_tuple::type; using type = typename conc_tuple::type; }; + template + struct node_tuple, void> { + using node_type = in_t; + using left_tuple = typename node_tuple::type; + using right_tuple = typename conc_tuple::type...>::type; + using type = typename conc_tuple::type; + }; + template struct node_tuple::value>::type> { using node_type = T; @@ -88,6 +123,36 @@ namespace sqlite_orm { using type = typename conc_tuple::type; }; + template + struct node_tuple, void> { + using node_type = insert_raw_t; + using type = typename conc_tuple::type...>::type; + }; + + template + struct node_tuple, void> { + using node_type = replace_raw_t; + using type = typename conc_tuple::type...>::type; + }; + + template + struct node_tuple, void> { + using node_type = into_t; + using type = std::tuple<>; + }; + + template + struct node_tuple, void> { + using node_type = values_t; + using type = typename conc_tuple::type...>::type; + }; + + template + struct node_tuple, void> { + using node_type = std::tuple; + using type = typename conc_tuple::type...>::type; + }; + template struct node_tuple, void> { using node_type = get_all_t; @@ -203,8 +268,14 @@ namespace sqlite_orm { }; template - struct node_tuple, void> { - using node_type = core_function_t; + struct node_tuple, void> { + using node_type = built_in_function_t; + using type = typename conc_tuple::type...>::type; + }; + + template + struct node_tuple, void> { + using node_type = function_call; using type = typename conc_tuple::type...>::type; }; diff --git a/dev/object_from_column_builder.h b/dev/object_from_column_builder.h index 6c45e2739..eb86437e6 100644 --- a/dev/object_from_column_builder.h +++ b/dev/object_from_column_builder.h @@ -9,7 +9,7 @@ namespace sqlite_orm { namespace internal { struct object_from_column_builder_base { - sqlite3_stmt *stmt = nullptr; + sqlite3_stmt* stmt = nullptr; mutable int index = 0; }; @@ -20,13 +20,13 @@ namespace sqlite_orm { struct object_from_column_builder : object_from_column_builder_base { using object_type = O; - object_type &object; + object_type& object; - object_from_column_builder(object_type &object_, sqlite3_stmt *stmt_) : + object_from_column_builder(object_type& object_, sqlite3_stmt* stmt_) : object_from_column_builder_base{stmt_}, object(object_) {} template - void operator()(const C &c) const { + void operator()(const C& c) const { using field_type = typename C::field_type; auto value = row_extractor().extract(this->stmt, this->index++); if(c.member_pointer) { diff --git a/dev/operators.h b/dev/operators.h index 9136e6a2c..43c6bb3d0 100644 --- a/dev/operators.h +++ b/dev/operators.h @@ -1,8 +1,10 @@ #pragma once #include // std::false_type, std::true_type - -#include "negatable.h" +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::nullopt +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +#include "tags.h" namespace sqlite_orm { @@ -84,7 +86,7 @@ namespace sqlite_orm { template using div_t = binary_operator; - struct mod_string { + struct mod_operator_string { operator std::string() const { return "%"; } @@ -94,7 +96,7 @@ namespace sqlite_orm { * Result of mod % operator */ template - using mod_t = binary_operator; + using mod_t = binary_operator; struct bitwise_shift_left_string { operator std::string() const { @@ -103,8 +105,8 @@ namespace sqlite_orm { }; /** - * Result of bitwise shift left << operator - */ + * Result of bitwise shift left << operator + */ template using bitwise_shift_left_t = binary_operator; @@ -115,8 +117,8 @@ namespace sqlite_orm { }; /** - * Result of bitwise shift right >> operator - */ + * Result of bitwise shift right >> operator + */ template using bitwise_shift_right_t = binary_operator; @@ -127,8 +129,8 @@ namespace sqlite_orm { }; /** - * Result of bitwise and & operator - */ + * Result of bitwise and & operator + */ template using bitwise_and_t = binary_operator; @@ -139,8 +141,8 @@ namespace sqlite_orm { }; /** - * Result of bitwise or | operator - */ + * Result of bitwise or | operator + */ template using bitwise_or_t = binary_operator; @@ -151,8 +153,8 @@ namespace sqlite_orm { }; /** - * Result of bitwise not ~ operator - */ + * Result of bitwise not ~ operator + */ template struct bitwise_not_t : bitwise_not_string, arithmetic_t, negatable_t { using argument_type = T; @@ -167,6 +169,7 @@ namespace sqlite_orm { return "="; } }; + /** * Result of assign = operator */ @@ -185,34 +188,9 @@ namespace sqlite_orm { template struct is_assign_t> : public std::true_type {}; - /** - * Is not an operator but a result of c(...) function. Has operator= overloaded which returns assign_t - */ - template - struct expression_t { - T t; - - expression_t(T t_) : t(std::move(t_)) {} + template + struct in_t; - template - assign_t operator=(R r) const { - return {this->t, std::move(r)}; - } - - assign_t operator=(std::nullptr_t) const { - return {this->t, nullptr}; - } - }; - - } - - /** - * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or - * `storage.update(set(c(&User::name) = "Dua Lipa")); - */ - template - internal::expression_t c(T t) { - return {std::move(t)}; } /** @@ -233,23 +211,34 @@ namespace sqlite_orm { } /** - * Public interface for - operator. Example: `select(add(&User::age, 1));` => SELECT age - 1 FROM users + * Public interface for - operator. Example: `select(sub(&User::age, 1));` => SELECT age - 1 FROM users */ template internal::sub_t sub(L l, R r) { return {std::move(l), std::move(r)}; } + /** + * Public interface for * operator. Example: `select(mul(&User::salary, 2));` => SELECT salary * 2 FROM users + */ template internal::mul_t mul(L l, R r) { return {std::move(l), std::move(r)}; } + /** + * Public interface for / operator. Example: `select(div(&User::salary, 3));` => SELECT salary / 3 FROM users + * @note Please notice that ::div function already exists in pure C standard library inside header. + * If you use `using namespace sqlite_orm` directive you an specify which `div` you call explicitly using `::div` or `sqlite_orm::div` statements. + */ template internal::div_t div(L l, R r) { return {std::move(l), std::move(r)}; } + /** + * Public interface for % operator. Example: `select(mod(&User::age, 5));` => SELECT age % 5 FROM users + */ template internal::mod_t mod(L l, R r) { return {std::move(l), std::move(r)}; diff --git a/dev/optional_container.h b/dev/optional_container.h index 12b5b2446..adc6a10eb 100644 --- a/dev/optional_container.h +++ b/dev/optional_container.h @@ -15,7 +15,7 @@ namespace sqlite_orm { type field; template - void apply(const L &l) const { + void apply(const L& l) const { l(this->field); } }; @@ -25,7 +25,7 @@ namespace sqlite_orm { using type = void; template - void apply(const L &) const { + void apply(const L&) const { //.. } }; diff --git a/dev/order_by_serializator.h b/dev/order_by_serializator.h index 082b61647..037d560b9 100644 --- a/dev/order_by_serializator.h +++ b/dev/order_by_serializator.h @@ -12,7 +12,7 @@ namespace sqlite_orm { struct order_by_serializator; template - std::string serialize_order_by(const T &t, const C &context) { + std::string serialize_order_by(const T& t, const C& context) { order_by_serializator serializator; return serializator(t, context); } @@ -22,11 +22,11 @@ namespace sqlite_orm { using statement_type = order_by_t; template - std::string operator()(const statement_type &orderBy, const C &context) const { + std::string operator()(const statement_type& orderBy, const C& context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; - auto columnName = serialize(orderBy.o, newContext); + auto columnName = serialize(orderBy.expression, newContext); ss << columnName << " "; if(orderBy._collate_argument.length()) { ss << "COLLATE " << orderBy._collate_argument << " "; @@ -48,9 +48,9 @@ namespace sqlite_orm { using statement_type = dynamic_order_by_t; template - std::string operator()(const statement_type &orderBy, const C &) const { + std::string operator()(const statement_type& orderBy, const C&) const { std::vector expressions; - for(auto &entry: orderBy) { + for(auto& entry: orderBy) { std::string entryString; { std::stringstream ss; diff --git a/dev/pragma.h b/dev/pragma.h index 8e456c581..f33c62cc4 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -4,8 +4,10 @@ #include #include // std::function #include // std::shared_ptr +#include // std::vector #include "error_code.h" +#include "util.h" #include "row_extractor.h" #include "journal_mode.h" #include "connection_holder.h" @@ -14,124 +16,139 @@ namespace sqlite_orm { namespace internal { struct storage_base; - } - - struct pragma_t { - using get_connection_t = std::function; - - pragma_t(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} - void busy_timeout(int value) { - this->set_pragma("busy_timeout", value); + template + int getPragmaCallback(void* data, int argc, char** argv, char**) { + auto& res = *(T*)data; + if(argc) { + res = row_extractor().extract(argv[0]); + } + return 0; } - int busy_timeout() { - return this->get_pragma("busy_timeout"); + template<> + inline int getPragmaCallback>(void* data, int argc, char** argv, char**) { + auto& res = *(std::vector*)data; + res.reserve(argc); + for(decltype(argc) i = 0; i < argc; ++i) { + auto rowString = row_extractor().extract(argv[i]); + res.push_back(move(rowString)); + } + return 0; } - sqlite_orm::journal_mode journal_mode() { - return this->get_pragma("journal_mode"); - } + struct pragma_t { + using get_connection_t = std::function; - void journal_mode(sqlite_orm::journal_mode value) { - this->_journal_mode = -1; - this->set_pragma("journal_mode", value); - this->_journal_mode = static_cast_journal_mode)>(value); - } + pragma_t(get_connection_t get_connection_) : get_connection(move(get_connection_)) {} - int synchronous() { - return this->get_pragma("synchronous"); - } + void busy_timeout(int value) { + this->set_pragma("busy_timeout", value); + } - void synchronous(int value) { - this->_synchronous = -1; - this->set_pragma("synchronous", value); - this->_synchronous = value; - } + int busy_timeout() { + return this->get_pragma("busy_timeout"); + } - int user_version() { - return this->get_pragma("user_version"); - } + sqlite_orm::journal_mode journal_mode() { + return this->get_pragma("journal_mode"); + } - void user_version(int value) { - this->set_pragma("user_version", value); - } + void journal_mode(sqlite_orm::journal_mode value) { + this->_journal_mode = -1; + this->set_pragma("journal_mode", value); + this->_journal_mode = static_cast_journal_mode)>(value); + } - int auto_vacuum() { - return this->get_pragma("auto_vacuum"); - } + int synchronous() { + return this->get_pragma("synchronous"); + } - void auto_vacuum(int value) { - this->set_pragma("auto_vacuum", value); - } + void synchronous(int value) { + this->_synchronous = -1; + this->set_pragma("synchronous", value); + this->_synchronous = value; + } - protected: - friend struct storage_base; + int user_version() { + return this->get_pragma("user_version"); + } - public: - int _synchronous = -1; - signed char _journal_mode = -1; // if != -1 stores static_cast(journal_mode) - get_connection_t get_connection; + void user_version(int value) { + this->set_pragma("user_version", value); + } - template - T get_pragma(const std::string &name) { - auto connection = this->get_connection(); - auto query = "PRAGMA " + name; - T result; - auto db = connection.get(); - auto rc = sqlite3_exec( - db, - query.c_str(), - [](void *data, int argc, char **argv, char **) -> int { - auto &res = *(T *)data; - if(argc) { - res = row_extractor().extract(argv[0]); - } - return 0; - }, - &result, - nullptr); - if(rc == SQLITE_OK) { - return result; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + int auto_vacuum() { + return this->get_pragma("auto_vacuum"); } - } - /** - * Yevgeniy Zakharov: I wanted to refactore this function with statements and value bindings - * but it turns out that bindings in pragma statements are not supported. - */ - template - void set_pragma(const std::string &name, const T &value, sqlite3 *db = nullptr) { - auto con = this->get_connection(); - if(!db) { - db = con.get(); - } - std::stringstream ss; - ss << "PRAGMA " << name << " = " << value; - auto query = ss.str(); - auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + void auto_vacuum(int value) { + this->set_pragma("auto_vacuum", value); } - } - void set_pragma(const std::string &name, const sqlite_orm::journal_mode &value, sqlite3 *db = nullptr) { - auto con = this->get_connection(); - if(!db) { - db = con.get(); + std::vector integrity_check() { + return this->get_pragma>("integrity_check"); } - std::stringstream ss; - ss << "PRAGMA " << name << " = " << internal::to_string(value); - auto query = ss.str(); - auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + + template + std::vector integrity_check(T table_name) { + std::ostringstream oss; + oss << "integrity_check(" << table_name << ")"; + return this->get_pragma>(oss.str()); } - } - }; + + std::vector integrity_check(int n) { + std::ostringstream oss; + oss << "integrity_check(" << n << ")"; + return this->get_pragma>(oss.str()); + } + + private: + friend struct storage_base; + + int _synchronous = -1; + signed char _journal_mode = -1; // if != -1 stores static_cast(journal_mode) + get_connection_t get_connection; + + template + T get_pragma(const std::string& name) { + auto connection = this->get_connection(); + auto query = "PRAGMA " + name; + T result; + auto db = connection.get(); + auto rc = sqlite3_exec(db, query.c_str(), getPragmaCallback, &result, nullptr); + if(rc == SQLITE_OK) { + return result; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + /** + * Yevgeniy Zakharov: I wanted to refactor this function with statements and value bindings + * but it turns out that bindings in pragma statements are not supported. + */ + template + void set_pragma(const std::string& name, const T& value, sqlite3* db = nullptr) { + auto con = this->get_connection(); + if(!db) { + db = con.get(); + } + std::stringstream ss; + ss << "PRAGMA " << name << " = " << value; + internal::perform_void_exec(db, ss.str()); + } + + void set_pragma(const std::string& name, const sqlite_orm::journal_mode& value, sqlite3* db = nullptr) { + auto con = this->get_connection(); + if(!db) { + db = con.get(); + } + std::stringstream ss; + ss << "PRAGMA " << name << " = " << internal::to_string(value); + internal::perform_void_exec(db, ss.str()); + } + }; + } } diff --git a/dev/prepared_statement.h b/dev/prepared_statement.h index 630a1d27b..9a78b599c 100644 --- a/dev/prepared_statement.h +++ b/dev/prepared_statement.h @@ -8,13 +8,14 @@ #include "connection_holder.h" #include "select_constraints.h" +#include "values.h" namespace sqlite_orm { namespace internal { struct prepared_statement_base { - sqlite3_stmt *stmt = nullptr; + sqlite3_stmt* stmt = nullptr; connection_ref con; ~prepared_statement_base() { @@ -72,7 +73,7 @@ namespace sqlite_orm { expression_type t; - prepared_statement_t(T t_, sqlite3_stmt *stmt_, connection_ref con_) : + prepared_statement_t(T t_, sqlite3_stmt* stmt_, connection_ref con_) : prepared_statement_base{stmt_, std::move(con_)}, t(std::move(t_)) {} }; @@ -185,6 +186,12 @@ namespace sqlite_orm { type obj; }; + template + struct is_insert : std::false_type {}; + + template + struct is_insert> : std::true_type {}; + template struct insert_explicit { using type = T; @@ -201,39 +208,354 @@ namespace sqlite_orm { type obj; }; - template + template + struct is_replace : std::false_type {}; + + template + struct is_replace> : std::true_type {}; + + template struct insert_range_t { using iterator_type = It; - using object_type = typename std::iterator_traits::value_type; + using container_object_type = typename std::iterator_traits::value_type; + using transformer_type = L; + using object_type = O; std::pair range; + transformer_type transformer; }; - template + template + struct is_insert_range : std::false_type {}; + + template + struct is_insert_range> : std::true_type {}; + + template struct replace_range_t { using iterator_type = It; - using object_type = typename std::iterator_traits::value_type; + using container_object_type = typename std::iterator_traits::value_type; + using transformer_type = L; + using object_type = O; std::pair range; + transformer_type transformer; + }; + + template + struct is_replace_range : std::false_type {}; + + template + struct is_replace_range> : std::true_type {}; + + template + struct insert_raw_t { + using args_tuple = std::tuple; + + args_tuple args; + }; + + template + struct is_insert_raw : std::false_type {}; + + template + struct is_insert_raw> : std::true_type {}; + + template + struct replace_raw_t { + using args_tuple = std::tuple; + + args_tuple args; + }; + + template + struct is_replace_raw : std::false_type {}; + + template + struct is_replace_raw> : std::true_type {}; + + template + struct into_t { + using type = T; + }; + + template + struct is_into : std::false_type {}; + + template + struct is_into> : std::true_type {}; + + struct default_transformer { + + template + const T& operator()(const T& object) const { + return object; + } + }; + + struct default_values_t {}; + + template + using is_default_values = std::is_same; + + enum class insert_constraint { + abort, + fail, + ignore, + replace, + rollback, }; + + template + using is_insert_constraint = std::is_same; + } + + inline internal::insert_constraint or_rollback() { + return internal::insert_constraint::rollback; + } + + inline internal::insert_constraint or_replace() { + return internal::insert_constraint::replace; + } + + inline internal::insert_constraint or_ignore() { + return internal::insert_constraint::ignore; + } + + inline internal::insert_constraint or_fail() { + return internal::insert_constraint::fail; + } + + inline internal::insert_constraint or_abort() { + return internal::insert_constraint::abort; + } + + /** + * Use this function to add `DEFAULT VALUES` modifier to raw `INSERT`. + * + * @example + * ``` + * storage.insert(into(), default_values()); + * ``` + */ + inline internal::default_values_t default_values() { + return {}; + } + + template + internal::into_t into() { + return {}; + } + + /** + * Raw insert statement creation routine. Use this if `insert` with object does not fit you. This insert is designed to be able + * to call any type of `INSERT` query with no limitations. + * @example + * ```sql + * INSERT INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(insert(into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * storage.execute(statement)); + * ``` + * One more example: + * ```sql + * INSERT INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(insert(into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * storage.execute(statement)); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * INSERT INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(insert(into(), default_values())); + * storage.execute(statement)); + * ``` + * Also one can use `INSERT OR ABORT`/`INSERT OR FAIL`/`INSERT OR IGNORE`/`INSERT OR REPLACE`/`INSERT ROLLBACK`: + * ```c++ + * auto statement = storage.prepare(insert(or_ignore(), into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * auto statement2 = storage.prepare(insert(or_rollback(), into(), default_values())); + * auto statement3 = storage.prepare(insert(or_abort(), into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * ``` + */ + template + internal::insert_raw_t insert(Args... args) { + using args_tuple = std::tuple; + using internal::count_tuple; + using internal::is_columns; + using internal::is_insert_constraint; + using internal::is_into; + using internal::is_select; + using internal::is_upsert_clause; + using internal::is_values; + + constexpr int orArgsCount = count_tuple::value; + static_assert(orArgsCount < 2, "Raw insert must have only one OR... argument"); + + constexpr int intoArgsCount = count_tuple::value; + static_assert(intoArgsCount != 0, "Raw insert must have into argument"); + static_assert(intoArgsCount < 2, "Raw insert must have only one into argument"); + + constexpr int columnsArgsCount = count_tuple::value; + static_assert(columnsArgsCount < 2, "Raw insert must have only one columns(...) argument"); + + constexpr int valuesArgsCount = count_tuple::value; + static_assert(valuesArgsCount < 2, "Raw insert must have only one values(...) argument"); + + constexpr int defaultValuesCount = count_tuple::value; + static_assert(defaultValuesCount < 2, "Raw insert must have only one default_values() argument"); + + constexpr int selectsArgsCount = count_tuple::value; + static_assert(selectsArgsCount < 2, "Raw insert must have only one select(...) argument"); + + constexpr int upsertClausesCount = count_tuple::value; + static_assert(upsertClausesCount <= 2, "Raw insert can contain 2 instances of upsert clause maximum"); + + constexpr int argsCount = int(std::tuple_size::value); + static_assert(argsCount == intoArgsCount + columnsArgsCount + valuesArgsCount + defaultValuesCount + + selectsArgsCount + orArgsCount + upsertClausesCount, + "Raw insert has invalid arguments"); + + return {{std::forward(args)...}}; + } + + /** + * Raw replace statement creation routine. Use this if `replace` with object does not fit you. This replace is designed to be able + * to call any type of `REPLACE` query with no limitations. Actually this is the same query as raw insert except `OR...` option existance. + * @example + * ```sql + * REPLACE INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(replace(into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * storage.execute(statement)); + * ``` + * One more example: + * ```sql + * REPLACE INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(replace(into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * storage.execute(statement)); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * REPLACE INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(replace(into(), default_values())); + * storage.execute(statement)); + * ``` + */ + template + internal::replace_raw_t replace(Args... args) { + using args_tuple = std::tuple; + using internal::count_tuple; + using internal::is_columns; + using internal::is_into; + using internal::is_values; + + constexpr int intoArgsCount = count_tuple::value; + static_assert(intoArgsCount != 0, "Raw replace must have into argument"); + static_assert(intoArgsCount < 2, "Raw replace must have only one into argument"); + + constexpr int columnsArgsCount = count_tuple::value; + static_assert(columnsArgsCount < 2, "Raw replace must have only one columns(...) argument"); + + constexpr int valuesArgsCount = count_tuple::value; + static_assert(valuesArgsCount < 2, "Raw replace must have only one values(...) argument"); + + constexpr int defaultValuesCount = count_tuple::value; + static_assert(defaultValuesCount < 2, "Raw replace must have only one default_values() argument"); + + constexpr int selectsArgsCount = count_tuple::value; + static_assert(selectsArgsCount < 2, "Raw replace must have only one select(...) argument"); + + constexpr int argsCount = int(std::tuple_size::value); + static_assert(argsCount == + intoArgsCount + columnsArgsCount + valuesArgsCount + defaultValuesCount + selectsArgsCount, + "Raw replace has invalid arguments"); + + return {{std::forward(args)...}}; } /** * Create a replace range statement + * + * @example + * ``` + * std::vector users; + * users.push_back(User{1, "Leony"}); + * auto statement = storage.prepare(replace_range(users.begin(), users.end())); + * storage.execute(statement); + * ``` */ template - internal::replace_range_t replace_range(It from, It to) { + internal::replace_range_t::value_type> + replace_range(It from, It to) { return {{std::move(from), std::move(to)}}; } + /** + * Create an replace range statement with explicit transformer. Transformer is used to apply containers with no strict objects with other kind of objects like pointers, + * optionals or whatever. + * @example + * ``` + * std::vector> userPointers; + * userPointers.push_back(std::make_unique(1, "Eneli")); + * auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), [](const std::unique_ptr &userPointer) -> const User & { + * return *userPointer; + * })); + * storage.execute(statement); + * ``` + */ + template + internal::replace_range_t replace_range(It from, It to, L transformer) { + return {{std::move(from), std::move(to)}, std::move(transformer)}; + } + /** * Create an insert range statement + * @example + * ``` + * std::vector users; + * users.push_back(User{1, "Leony"}); + * auto statement = storage.prepare(insert_range(users.begin(), users.end())); + * storage.execute(statement); + * ``` */ template - internal::insert_range_t insert_range(It from, It to) { - return {{std::move(from), std::move(to)}}; + internal::insert_range_t::value_type> + insert_range(It from, It to) { + return {{std::move(from), std::move(to)}, internal::default_transformer{}}; } + /** + * Create an insert range statement with explicit transformer. Transformer is used to apply containers with no strict objects with other kind of objects like pointers, + * optionals or whatever. + * @example + * ``` + * std::vector> userPointers; + * userPointers.push_back(std::make_unique(1, "Eneli")); + * auto statement = storage.prepare(insert_range(userPointers.begin(), userPointers.end(), [](const std::unique_ptr &userPointer) -> const User & { + * return *userPointer; + * })); + * storage.execute(statement); + * ``` + */ + template + internal::insert_range_t insert_range(It from, It to, L transformer) { + return {{std::move(from), std::move(to)}, std::move(transformer)}; + } /** * Create a replace statement. * T is an object type mapped to a storage. diff --git a/dev/row_extractor.h b/dev/row_extractor.h index 41027d0ee..f0d5237a8 100644 --- a/dev/row_extractor.h +++ b/dev/row_extractor.h @@ -2,7 +2,7 @@ #include #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if -#include // atof, atoi, atoll +#include // atof, atoi, atoll #include // std::string, std::wstring #ifndef SQLITE_ORM_OMITS_CODECVT #include // std::wstring_convert, std::codecvt_utf8_utf16 @@ -27,10 +27,13 @@ namespace sqlite_orm { template struct row_extractor { // used in sqlite3_exec (select) - V extract(const char *row_value); + V extract(const char* row_value) const; // used in sqlite_column (iteration, get_all) - V extract(sqlite3_stmt *stmt, int columnIndex); + V extract(sqlite3_stmt* stmt, int columnIndex) const; + + // used in user defined functions + V extract(sqlite3_value* value) const; }; /** @@ -38,40 +41,56 @@ namespace sqlite_orm { */ template struct row_extractor::value>> { - V extract(const char *row_value) { - return extract(row_value, tag()); + V extract(const char* row_value) const { + return this->extract(row_value, tag()); + } + + V extract(sqlite3_stmt* stmt, int columnIndex) const { + return this->extract(stmt, columnIndex, tag()); } - V extract(sqlite3_stmt *stmt, int columnIndex) { - return extract(stmt, columnIndex, tag()); + V extract(sqlite3_value* value) const { + return this->extract(value, tag()); } private: using tag = arithmetic_tag_t; - V extract(const char *row_value, const int_or_smaller_tag &) { + V extract(const char* row_value, const int_or_smaller_tag&) const { return static_cast(atoi(row_value)); } - V extract(sqlite3_stmt *stmt, int columnIndex, const int_or_smaller_tag &) { + V extract(sqlite3_stmt* stmt, int columnIndex, const int_or_smaller_tag&) const { return static_cast(sqlite3_column_int(stmt, columnIndex)); } - V extract(const char *row_value, const bigint_tag &) { + V extract(sqlite3_value* value, const int_or_smaller_tag&) const { + return static_cast(sqlite3_value_int(value)); + } + + V extract(const char* row_value, const bigint_tag&) const { return static_cast(atoll(row_value)); } - V extract(sqlite3_stmt *stmt, int columnIndex, const bigint_tag &) { + V extract(sqlite3_stmt* stmt, int columnIndex, const bigint_tag&) const { return static_cast(sqlite3_column_int64(stmt, columnIndex)); } - V extract(const char *row_value, const real_tag &) { + V extract(sqlite3_value* value, const bigint_tag&) const { + return static_cast(sqlite3_value_int64(value)); + } + + V extract(const char* row_value, const real_tag&) const { return static_cast(atof(row_value)); } - V extract(sqlite3_stmt *stmt, int columnIndex, const real_tag &) { + V extract(sqlite3_stmt* stmt, int columnIndex, const real_tag&) const { return static_cast(sqlite3_column_double(stmt, columnIndex)); } + + V extract(sqlite3_value* value, const real_tag&) const { + return static_cast(sqlite3_value_double(value)); + } }; /** @@ -79,7 +98,7 @@ namespace sqlite_orm { */ template<> struct row_extractor { - std::string extract(const char *row_value) { + std::string extract(const char* row_value) const { if(row_value) { return row_value; } else { @@ -87,9 +106,16 @@ namespace sqlite_orm { } } - std::string extract(sqlite3_stmt *stmt, int columnIndex) { - auto cStr = (const char *)sqlite3_column_text(stmt, columnIndex); - if(cStr) { + std::string extract(sqlite3_stmt* stmt, int columnIndex) const { + if(auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex)) { + return cStr; + } else { + return {}; + } + } + + std::string extract(sqlite3_value* value) const { + if(auto cStr = (const char*)sqlite3_value_text(value)) { return cStr; } else { return {}; @@ -102,7 +128,7 @@ namespace sqlite_orm { */ template<> struct row_extractor { - std::wstring extract(const char *row_value) { + std::wstring extract(const char* row_value) const { if(row_value) { std::wstring_convert> converter; return converter.from_bytes(row_value); @@ -111,8 +137,8 @@ namespace sqlite_orm { } } - std::wstring extract(sqlite3_stmt *stmt, int columnIndex) { - auto cStr = (const char *)sqlite3_column_text(stmt, columnIndex); + std::wstring extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); if(cStr) { std::wstring_convert> converter; return converter.from_bytes(cStr); @@ -120,6 +146,14 @@ namespace sqlite_orm { return {}; } } + + std::wstring extract(sqlite3_value* value) const { + if(auto cStr = (const wchar_t*)sqlite3_value_text16(value)) { + return cStr; + } else { + return {}; + } + } }; #endif // SQLITE_ORM_OMITS_CODECVT @@ -127,7 +161,7 @@ namespace sqlite_orm { struct row_extractor::value>> { using value_type = typename is_std_ptr::element_type; - V extract(const char *row_value) { + V extract(const char* row_value) const { if(row_value) { return is_std_ptr::make(row_extractor().extract(row_value)); } else { @@ -135,7 +169,7 @@ namespace sqlite_orm { } } - V extract(sqlite3_stmt *stmt, int columnIndex) { + V extract(sqlite3_stmt* stmt, int columnIndex) const { auto type = sqlite3_column_type(stmt, columnIndex); if(type != SQLITE_NULL) { return is_std_ptr::make(row_extractor().extract(stmt, columnIndex)); @@ -143,6 +177,15 @@ namespace sqlite_orm { return {}; } } + + V extract(sqlite3_value* value) const { + auto type = sqlite3_value_type(value); + if(type != SQLITE_NULL) { + return is_std_ptr::make(row_extractor().extract(value)); + } else { + return {}; + } + } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -150,7 +193,7 @@ namespace sqlite_orm { struct row_extractor, void> { using value_type = T; - std::optional extract(const char *row_value) { + std::optional extract(const char* row_value) const { if(row_value) { return std::make_optional(row_extractor().extract(row_value)); } else { @@ -158,7 +201,7 @@ namespace sqlite_orm { } } - std::optional extract(sqlite3_stmt *stmt, int columnIndex) { + std::optional extract(sqlite3_stmt* stmt, int columnIndex) const { auto type = sqlite3_column_type(stmt, columnIndex); if(type != SQLITE_NULL) { return std::make_optional(row_extractor().extract(stmt, columnIndex)); @@ -166,6 +209,15 @@ namespace sqlite_orm { return std::nullopt; } } + + std::optional extract(sqlite3_value* value) const { + auto type = sqlite3_value_type(value); + if(type != SQLITE_NULL) { + return std::make_optional(row_extractor().extract(value)); + } else { + return std::nullopt; + } + } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED /** @@ -173,7 +225,7 @@ namespace sqlite_orm { */ template<> struct row_extractor> { - std::vector extract(const char *row_value) { + std::vector extract(const char* row_value) const { if(row_value) { auto len = ::strlen(row_value); return this->go(row_value, len); @@ -182,14 +234,20 @@ namespace sqlite_orm { } } - std::vector extract(sqlite3_stmt *stmt, int columnIndex) { - auto bytes = static_cast(sqlite3_column_blob(stmt, columnIndex)); + std::vector extract(sqlite3_stmt* stmt, int columnIndex) const { + auto bytes = static_cast(sqlite3_column_blob(stmt, columnIndex)); auto len = static_cast(sqlite3_column_bytes(stmt, columnIndex)); return this->go(bytes, len); } + std::vector extract(sqlite3_value* value) const { + auto bytes = static_cast(sqlite3_value_blob(value)); + auto len = static_cast(sqlite3_value_bytes(value)); + return this->go(bytes, len); + } + protected: - std::vector go(const char *bytes, size_t len) { + std::vector go(const char* bytes, size_t len) const { if(len) { std::vector res; res.reserve(len); @@ -204,40 +262,40 @@ namespace sqlite_orm { template struct row_extractor> { - std::tuple extract(char **argv) { + std::tuple extract(char** argv) const { std::tuple res; this->extract::value>(res, argv); return res; } - std::tuple extract(sqlite3_stmt *stmt, int /*columnIndex*/) { + std::tuple extract(sqlite3_stmt* stmt, int /*columnIndex*/) const { std::tuple res; this->extract::value>(res, stmt); return res; } protected: - template::type * = nullptr> - void extract(std::tuple &t, sqlite3_stmt *stmt) { + template::type* = nullptr> + void extract(std::tuple& t, sqlite3_stmt* stmt) const { using tuple_type = typename std::tuple_element>::type; std::get(t) = row_extractor().extract(stmt, I - 1); this->extract(t, stmt); } - template::type * = nullptr> - void extract(std::tuple &, sqlite3_stmt *) { + template::type* = nullptr> + void extract(std::tuple&, sqlite3_stmt*) const { //.. } - template::type * = nullptr> - void extract(std::tuple &t, char **argv) { + template::type* = nullptr> + void extract(std::tuple& t, char** argv) const { using tuple_type = typename std::tuple_element>::type; std::get(t) = row_extractor().extract(argv[I - 1]); this->extract(t, argv); } - template::type * = nullptr> - void extract(std::tuple &, char **) { + template::type* = nullptr> + void extract(std::tuple&, char**) const { //.. } }; @@ -247,7 +305,7 @@ namespace sqlite_orm { */ template<> struct row_extractor { - journal_mode extract(const char *row_value) { + journal_mode extract(const char* row_value) const { if(row_value) { if(auto res = internal::journal_mode_from_string(row_value)) { return std::move(*res); @@ -259,8 +317,8 @@ namespace sqlite_orm { } } - journal_mode extract(sqlite3_stmt *stmt, int columnIndex) { - auto cStr = (const char *)sqlite3_column_text(stmt, columnIndex); + journal_mode extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); return this->extract(cStr); } }; diff --git a/dev/row_extractor_builder.h b/dev/row_extractor_builder.h index 20c20b764..580a2389a 100644 --- a/dev/row_extractor_builder.h +++ b/dev/row_extractor_builder.h @@ -8,18 +8,18 @@ namespace sqlite_orm { namespace internal { /** - * This builder is used to construct different row extractors depending on type. - * It has two specializations: for mapped to storage types (e.g. User, Visit etc) and - * for non-mapped (e.g. std::string, QString, int etc). For non mapped its operator() returns - * generic `row_extractor`, for mapped it returns `mapped_row_extractor` instance. - */ + * This builder is used to construct different row extractors depending on type. + * It has two specializations: for mapped to storage types (e.g. User, Visit etc) and + * for non-mapped (e.g. std::string, QString, int etc). For non mapped its operator() returns + * generic `row_extractor`, for mapped it returns `mapped_row_extractor` instance. + */ template struct row_extractor_builder; template struct row_extractor_builder { - row_extractor operator()(const I * /*tableInfo*/) const { + row_extractor operator()(const I* /*tableInfo*/) const { return {}; } }; @@ -27,13 +27,13 @@ namespace sqlite_orm { template struct row_extractor_builder { - mapped_row_extractor operator()(const I *tableInfo) const { + mapped_row_extractor operator()(const I* tableInfo) const { return {*tableInfo}; } }; template - auto make_row_extractor(const I *tableInfo) { + auto make_row_extractor(const I* tableInfo) { using builder_t = row_extractor_builder; return builder_t{}(tableInfo); } diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 406ded4b1..2f99558d4 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -3,24 +3,48 @@ #include // std::string #include // std::declval #include // std::tuple, std::get, std::tuple_size +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED #include "is_base_of_template.h" -#include "tuple_helper.h" +#include "tuple_helper/tuple_helper.h" #include "optional_container.h" +#include "ast/where.h" namespace sqlite_orm { namespace internal { +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct as_optional_t { + using value_type = T; + + value_type value; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + struct distinct_string { + operator std::string() const { + return "DISTINCT"; + } + }; /** * DISCTINCT generic container. */ template - struct distinct_t { - T t; + struct distinct_t : distinct_string { + using value_type = T; + + value_type value; + + distinct_t(value_type value_) : value(std::move(value_)) {} + }; + struct all_string { operator std::string() const { - return "DISTINCT"; + return "ALL"; } }; @@ -28,12 +52,10 @@ namespace sqlite_orm { * ALL generic container. */ template - struct all_t { - T t; + struct all_t : all_string { + T value; - operator std::string() const { - return "ALL"; - } + all_t(T value_) : value(std::move(value_)) {} }; template @@ -46,6 +68,12 @@ namespace sqlite_orm { static constexpr const int count = std::tuple_size::value; }; + template + struct is_columns : std::false_type {}; + + template + struct is_columns> : std::true_type {}; + struct set_string { operator std::string() const { return "SET"; @@ -67,10 +95,41 @@ namespace sqlite_orm { */ template struct column_pointer { + using self = column_pointer; using type = T; using field_type = F; field_type field; + + template + internal::is_equal_t operator==(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + internal::is_not_equal_t operator!=(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + internal::lesser_than_t operator<(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + internal::lesser_or_equal_t operator<=(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + internal::greater_than_t operator>(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + internal::greater_or_equal_t operator>=(R rhs) const { + return {*this, std::move(rhs)}; + } }; /** @@ -86,6 +145,12 @@ namespace sqlite_orm { bool highest_level = false; }; + template + struct is_select : std::false_type {}; + + template + struct is_select> : std::true_type {}; + /** * Base for UNION, UNION ALL, EXCEPT and INTERSECT */ @@ -129,48 +194,51 @@ namespace sqlite_orm { union_t(left_type l, right_type r) : union_t(std::move(l), std::move(r), false) {} }; + struct except_string { + operator std::string() const { + return "EXCEPT"; + } + }; + /** * EXCEPT object type. */ template - struct except_t : public compound_operator { + struct except_t : compound_operator, except_string { using super = compound_operator; using left_type = typename super::left_type; using right_type = typename super::right_type; using super::super; + }; + struct intersect_string { operator std::string() const { - return "EXCEPT"; + return "INTERSECT"; } }; - /** * INTERSECT object type. */ template - struct intersect_t : public compound_operator { + struct intersect_t : compound_operator, intersect_string { using super = compound_operator; using left_type = typename super::left_type; using right_type = typename super::right_type; using super::super; - - operator std::string() const { - return "INTERSECT"; - } }; /** * Generic way to get DISTINCT value from any type. */ template - bool get_distinct(const T &) { + bool get_distinct(const T&) { return false; } template - bool get_distinct(const columns_t &cols) { + bool get_distinct(const columns_t& cols) { return cols.distinct; } @@ -245,9 +313,16 @@ namespace sqlite_orm { static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 GROUP BY blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 ORDER BY blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 LIMIT blocks"); + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 FROM blocks"); } } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + internal::as_optional_t as_optional(T value) { + return {std::move(value)}; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED template internal::then_t then(T t) { return {std::move(t)}; diff --git a/dev/serializator_context.h b/dev/serializator_context.h index e568eed75..70ed0b58a 100644 --- a/dev/serializator_context.h +++ b/dev/serializator_context.h @@ -10,8 +10,8 @@ namespace sqlite_orm { bool use_parentheses = true; template - std::string column_name(F O::*) const { - return {}; + const std::string* column_name(F O::*) const { + return nullptr; } }; @@ -19,12 +19,12 @@ namespace sqlite_orm { struct serializator_context : serializator_context_base { using impl_type = I; - const impl_type &impl; + const impl_type& impl; - serializator_context(const impl_type &impl_) : impl(impl_) {} + serializator_context(const impl_type& impl_) : impl(impl_) {} template - std::string column_name(F O::*m) const { + const std::string* column_name(F O::*m) const { return this->impl.column_name(m); } }; @@ -34,13 +34,13 @@ namespace sqlite_orm { using storage_type = S; using impl_type = typename storage_type::impl_type; - serializator_context_builder(const storage_type &storage_) : storage(storage_) {} + serializator_context_builder(const storage_type& storage_) : storage(storage_) {} serializator_context operator()() const { return {this->storage.impl}; } - const storage_type &storage; + const storage_type& storage; }; } diff --git a/dev/serialize_result_type.h b/dev/serialize_result_type.h new file mode 100644 index 000000000..e315bc05d --- /dev/null +++ b/dev/serialize_result_type.h @@ -0,0 +1,17 @@ +#pragma once + +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // string_view +#else +#include // std::string +#endif + +namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + using serialize_result_type = std::string_view; +#else + using serialize_result_type = std::string; +#endif + } +} diff --git a/dev/start_macros.h b/dev/start_macros.h index 00f7f3c92..d3609275b 100644 --- a/dev/start_macros.h +++ b/dev/start_macros.h @@ -18,4 +18,6 @@ __pragma(push_macro("min")) #if __cplusplus >= 201703L // use of C++17 or higher // Enables use of std::optional in SQLITE_ORM. #define SQLITE_ORM_OPTIONAL_SUPPORTED +#define SQLITE_ORM_STRING_VIEW_SUPPORTED +#define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED #endif diff --git a/dev/statement_binder.h b/dev/statement_binder.h index b9895702e..c9dc64615 100644 --- a/dev/statement_binder.h +++ b/dev/statement_binder.h @@ -10,6 +10,7 @@ #include // std::nullptr_t #include // std::declval #include // std::wstring_convert +#include // ::strncpy, ::strlen #include "is_std_ptr.h" @@ -26,24 +27,41 @@ namespace sqlite_orm { */ template struct statement_binder::value>> { - int bind(sqlite3_stmt *stmt, int index, const V &value) { - return bind(stmt, index, value, tag()); + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + return this->bind(stmt, index, value, tag()); + } + + void result(sqlite3_context* context, const V& value) const { + this->result(context, value, tag()); } private: using tag = arithmetic_tag_t; - int bind(sqlite3_stmt *stmt, int index, const V &value, const int_or_smaller_tag &) { + int bind(sqlite3_stmt* stmt, int index, const V& value, const int_or_smaller_tag&) const { return sqlite3_bind_int(stmt, index, static_cast(value)); } - int bind(sqlite3_stmt *stmt, int index, const V &value, const bigint_tag &) { + void result(sqlite3_context* context, const V& value, const int_or_smaller_tag&) const { + sqlite3_result_int(context, static_cast(value)); + } + + int bind(sqlite3_stmt* stmt, int index, const V& value, const bigint_tag&) const { return sqlite3_bind_int64(stmt, index, static_cast(value)); } - int bind(sqlite3_stmt *stmt, int index, const V &value, const real_tag &) { + void result(sqlite3_context* context, const V& value, const bigint_tag&) const { + sqlite3_result_int64(context, static_cast(value)); + } + + int bind(sqlite3_stmt* stmt, int index, const V& value, const real_tag&) const { return sqlite3_bind_double(stmt, index, static_cast(value)); } + + void result(sqlite3_context* context, const V& value, const real_tag&) const { + sqlite3_result_double(context, static_cast(value)); + } }; /** @@ -52,18 +70,33 @@ namespace sqlite_orm { template struct statement_binder< V, - std::enable_if_t::value || std::is_same::value>> { - int bind(sqlite3_stmt *stmt, int index, const V &value) { - return sqlite3_bind_text(stmt, index, string_data(value), -1, SQLITE_TRANSIENT); + std::enable_if_t::value || std::is_same::value>> { + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + auto stringData = this->string_data(value); + return sqlite3_bind_text(stmt, index, std::get<0>(stringData), std::get<1>(stringData), SQLITE_TRANSIENT); + } + + void result(sqlite3_context* context, const V& value) const { + auto stringData = this->string_data(value); + auto stringDataLength = std::get<1>(stringData); + auto dataCopy = new char[stringDataLength + 1]; + auto stringChars = std::get<0>(stringData); + ::strncpy(dataCopy, stringChars, stringDataLength + 1); + sqlite3_result_text(context, dataCopy, stringDataLength, [](void* pointer) { + auto charPointer = (char*)pointer; + delete[] charPointer; + }); } private: - const char *string_data(const std::string &s) const { - return s.c_str(); + std::tuple string_data(const std::string& s) const { + return {s.c_str(), int(s.size())}; } - const char *string_data(const char *s) const { - return s; + std::tuple string_data(const char* s) const { + auto length = int(::strlen(s)); + return {s, length}; } }; @@ -74,12 +107,17 @@ namespace sqlite_orm { template struct statement_binder< V, - std::enable_if_t::value || std::is_same::value>> { - int bind(sqlite3_stmt *stmt, int index, const V &value) { + std::enable_if_t::value || std::is_same::value>> { + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { std::wstring_convert> converter; std::string utf8Str = converter.to_bytes(value); return statement_binder().bind(stmt, index, utf8Str); } + + void result(sqlite3_context* context, const V& value) const { + sqlite3_result_text16(context, (const void*)value.data(), int(value.length()), nullptr); + } }; #endif // SQLITE_ORM_OMITS_CODECVT @@ -88,17 +126,28 @@ namespace sqlite_orm { */ template<> struct statement_binder { - int bind(sqlite3_stmt *stmt, int index, const std::nullptr_t &) { + int bind(sqlite3_stmt* stmt, int index, const std::nullptr_t&) const { return sqlite3_bind_null(stmt, index); } + + void result(sqlite3_context* context, const std::nullptr_t&) const { + sqlite3_result_null(context); + } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * Specialization for std::nullopt_t. + */ template<> struct statement_binder { - int bind(sqlite3_stmt *stmt, int index, const std::nullopt_t &) { + int bind(sqlite3_stmt* stmt, int index, const std::nullopt_t&) const { return sqlite3_bind_null(stmt, index); } + + void result(sqlite3_context* context, const std::nullopt_t&) const { + sqlite3_result_null(context); + } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -106,13 +155,21 @@ namespace sqlite_orm { struct statement_binder::value>> { using value_type = typename is_std_ptr::element_type; - int bind(sqlite3_stmt *stmt, int index, const V &value) { + int bind(sqlite3_stmt* stmt, int index, const V& value) const { if(value) { return statement_binder().bind(stmt, index, *value); } else { return statement_binder().bind(stmt, index, nullptr); } } + + void result(sqlite3_context* context, const V& value) const { + if(value) { + statement_binder().result(context, value); + } else { + statement_binder().result(context, nullptr); + } + } }; /** @@ -120,17 +177,21 @@ namespace sqlite_orm { */ template<> struct statement_binder, void> { - int bind(sqlite3_stmt *stmt, int index, const std::vector &value) { + int bind(sqlite3_stmt* stmt, int index, const std::vector& value) const { if(value.size()) { - return sqlite3_bind_blob(stmt, - index, - (const void *)&value.front(), - int(value.size()), - SQLITE_TRANSIENT); + return sqlite3_bind_blob(stmt, index, (const void*)&value.front(), int(value.size()), SQLITE_TRANSIENT); } else { return sqlite3_bind_blob(stmt, index, "", 0, SQLITE_TRANSIENT); } } + + void result(sqlite3_context* context, const std::vector& value) const { + if(value.size()) { + sqlite3_result_blob(context, (const void*)&value.front(), int(value.size()), nullptr); + } else { + sqlite3_result_blob(context, "", 0, nullptr); + } + } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -138,13 +199,21 @@ namespace sqlite_orm { struct statement_binder, void> { using value_type = T; - int bind(sqlite3_stmt *stmt, int index, const std::optional &value) { + int bind(sqlite3_stmt* stmt, int index, const std::optional& value) const { if(value) { return statement_binder().bind(stmt, index, *value); } else { return statement_binder().bind(stmt, index, std::nullopt); } } + + void result(sqlite3_context* context, const std::optional& value) const { + if(value) { + statement_binder().result(context, value); + } else { + statement_binder().result(context, std::nullopt); + } + } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -154,8 +223,8 @@ namespace sqlite_orm { using is_bindable = std::integral_constant>::value>; struct conditional_binder_base { - sqlite3_stmt *stmt = nullptr; - int &index; + sqlite3_stmt* stmt = nullptr; + int& index; conditional_binder_base(decltype(stmt) stmt_, decltype(index) index_) : stmt(stmt_), index(index_) {} }; @@ -168,7 +237,7 @@ namespace sqlite_orm { using conditional_binder_base::conditional_binder_base; - int operator()(const T &t) const { + int operator()(const T& t) const { return statement_binder().bind(this->stmt, this->index++, t); } }; @@ -177,24 +246,35 @@ namespace sqlite_orm { struct conditional_binder : conditional_binder_base { using conditional_binder_base::conditional_binder_base; - int operator()(const T &) const { + int operator()(const T&) const { return SQLITE_OK; } }; - template - struct bindable_filter_single; + template class F, class SFINAE = void> + struct tuple_filter_single; - template - struct bindable_filter_single::value>::type> { + template class F> + struct tuple_filter_single::value>::type> { using type = std::tuple; }; - template - struct bindable_filter_single::value>::type> { + template class F> + struct tuple_filter_single::value>::type> { using type = std::tuple<>; }; + template class F> + struct tuple_filter; + + template class F> + struct tuple_filter, F> { + using type = typename conc_tuple::type...>::type; + }; + + template + struct bindable_filter_single : tuple_filter_single {}; + template struct bindable_filter; diff --git a/dev/statement_finalizer.h b/dev/statement_finalizer.h index cbeebe609..ede4e0da3 100644 --- a/dev/statement_finalizer.h +++ b/dev/statement_finalizer.h @@ -1,19 +1,15 @@ #pragma once +#include // std::unique_ptr #include +#include // std::integral_constant namespace sqlite_orm { /** * Guard class which finalizes `sqlite3_stmt` in dtor */ - struct statement_finalizer { - sqlite3_stmt *stmt = nullptr; + using statement_finalizer = + std::unique_ptr>; - statement_finalizer(decltype(stmt) stmt_) : stmt(stmt_) {} - - inline ~statement_finalizer() { - sqlite3_finalize(this->stmt); - } - }; } diff --git a/dev/statement_serializator.h b/dev/statement_serializator.h index a50d1c317..760b53d12 100644 --- a/dev/statement_serializator.h +++ b/dev/statement_serializator.h @@ -2,9 +2,12 @@ #include // std::stringstream #include // std::string -#include // std::enable_if +#include // std::enable_if, std::remove_pointer #include // std::vector #include // std::iter_swap +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED #include "core_functions.h" #include "constraints.h" @@ -18,6 +21,9 @@ #include "values.h" #include "table_type.h" #include "indexed_column.h" +#include "function.h" +#include "ast/upsert_clause.h" +#include "ast/excluded.h" namespace sqlite_orm { @@ -27,7 +33,7 @@ namespace sqlite_orm { struct statement_serializator; template - std::string serialize(const T &t, const C &context) { + std::string serialize(const T& t, const C& context) { statement_serializator serializator; return serializator(t, context); } @@ -37,7 +43,7 @@ namespace sqlite_orm { using statement_type = T; template - std::string operator()(const statement_type &statement, const C &context) { + std::string operator()(const statement_type& statement, const C& context) { if(context.replace_bindable_with_question) { return "?"; } else { @@ -46,12 +52,39 @@ namespace sqlite_orm { } }; + template + struct statement_serializator, void> { + using statement_type = excluded_t; + + template + std::string operator()(const statement_type& statement, const C& context) { + std::stringstream ss; + ss << "excluded."; + if(auto columnNamePointer = context.impl.column_name(statement.expression)) { + ss << "\"" << *columnNamePointer << "\""; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } + return ss.str(); + } + }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializator, void> { + using statement_type = as_optional_t; + + template + std::string operator()(const statement_type& statement, const C& context) { + return serialize(statement.value, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct statement_serializator, void> { using statement_type = std::reference_wrapper; template - std::string operator()(const statement_type &s, const C &context) { + std::string operator()(const statement_type& s, const C& context) { return serialize(s.get(), context); } }; @@ -61,33 +94,78 @@ namespace sqlite_orm { using statement_type = std::nullptr_t; template - std::string operator()(const statement_type &, const C &) { + std::string operator()(const statement_type&, const C&) { return "?"; } }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<> + struct statement_serializator { + using statement_type = std::nullopt_t; + template + std::string operator()(const statement_type&, const C&) { + return "?"; + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct statement_serializator, void> { using statement_type = alias_holder; template - std::string operator()(const statement_type &, const C &) { + std::string operator()(const statement_type&, const C&) { return T::get(); } }; + template + struct statement_serializator, std::tuple>, void> { + using statement_type = upsert_clause, std::tuple>; + + template + std::string operator()(const statement_type& statement, const C& context) const { + std::stringstream ss; + ss << "ON CONFLICT"; + iterate_tuple(statement.target_args, [&ss, &context](auto& value) { + using value_type = typename std::decay::type; + auto needParenthesis = std::is_member_pointer::value; + ss << ' '; + if(needParenthesis) { + ss << '('; + } + ss << serialize(value, context); + if(needParenthesis) { + ss << ')'; + } + }); + ss << ' ' << "DO"; + if(std::tuple_size::value == 0) { + ss << " NOTHING"; + } else { + ss << " UPDATE"; + auto updateContext = context; + updateContext.use_parentheses = false; + iterate_tuple(statement.actions, [&ss, &updateContext](auto& value) { + ss << ' ' << serialize(value, updateContext); + }); + } + return ss.str(); + } + }; + template - struct statement_serializator, void> { - using statement_type = core_function_t; + struct statement_serializator, void> { + using statement_type = built_in_function_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; - ss << static_cast(c) << "("; + ss << statement.serialize() << "("; std::vector args; - using args_type = typename std::decay::type::args_type; + using args_type = typename std::decay::type::args_type; args.reserve(std::tuple_size::value); - iterate_tuple(c.args, [&args, &context](auto &v) { + iterate_tuple(statement.args, [&args, &context](auto& v) { args.push_back(serialize(v, context)); }); for(size_t i = 0; i < args.size(); ++i) { @@ -101,12 +179,36 @@ namespace sqlite_orm { } }; + template + struct statement_serializator, void> { + using statement_type = function_call; + + template + std::string operator()(const statement_type& statement, const C& context) const { + using args_tuple = std::tuple; + + std::stringstream ss; + ss << F::name() << "("; + auto index = 0; + iterate_tuple(statement.args, [&context, &ss, &index](auto& v) { + auto value = serialize(v, context); + ss << value; + if(index < std::tuple_size::value - 1) { + ss << ", "; + } + ++index; + }); + ss << ")"; + return ss.str(); + } + }; + template struct statement_serializator, void> { using statement_type = as_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto tableAliasString = alias_extractor::get(); return serialize(c.expression, context) + " AS " + tableAliasString; } @@ -117,7 +219,7 @@ namespace sqlite_orm { using statement_type = alias_column_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << T::get() << "'."; @@ -134,21 +236,21 @@ namespace sqlite_orm { using statement_type = std::string; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { if(context.replace_bindable_with_question) { return "?"; } else { - return "\"" + c + "\""; + return "\'" + c + "\'"; } } }; template<> - struct statement_serializator { - using statement_type = const char *; + struct statement_serializator { + using statement_type = const char*; template - std::string operator()(const char *c, const C &context) const { + std::string operator()(const char* c, const C& context) const { if(context.replace_bindable_with_question) { return "?"; } else { @@ -162,12 +264,16 @@ namespace sqlite_orm { using statement_type = F O::*; template - std::string operator()(const statement_type &m, const C &context) const { + std::string operator()(const statement_type& m, const C& context) const { std::stringstream ss; if(!context.skip_table_name) { ss << "\"" << context.impl.find_table_name(typeid(O)) << "\"."; } - ss << "\"" << context.column_name(m) << "\""; + if(auto columnnamePointer = context.column_name(m)) { + ss << "\"" << *columnnamePointer << "\""; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } return ss.str(); } }; @@ -177,7 +283,7 @@ namespace sqlite_orm { using statement_type = rowid_t; template - std::string operator()(const statement_type &s, const C &) { + std::string operator()(const statement_type& s, const C&) { return static_cast(s); } }; @@ -187,7 +293,7 @@ namespace sqlite_orm { using statement_type = oid_t; template - std::string operator()(const statement_type &s, const C &) { + std::string operator()(const statement_type& s, const C&) { return static_cast(s); } }; @@ -197,7 +303,7 @@ namespace sqlite_orm { using statement_type = _rowid_t; template - std::string operator()(const statement_type &s, const C &) { + std::string operator()(const statement_type& s, const C&) { return static_cast(s); } }; @@ -207,7 +313,7 @@ namespace sqlite_orm { using statement_type = table_rowid_t; template - std::string operator()(const statement_type &s, const C &context) { + std::string operator()(const statement_type& s, const C& context) { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; @@ -222,7 +328,7 @@ namespace sqlite_orm { using statement_type = table_oid_t; template - std::string operator()(const statement_type &s, const C &context) { + std::string operator()(const statement_type& s, const C& context) { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; @@ -237,7 +343,7 @@ namespace sqlite_orm { using statement_type = table__rowid_t; template - std::string operator()(const statement_type &s, const C &context) { + std::string operator()(const statement_type& s, const C& context) { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; @@ -252,11 +358,17 @@ namespace sqlite_orm { using statement_type = binary_operator; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto lhs = serialize(c.lhs, context); auto rhs = serialize(c.rhs, context); std::stringstream ss; - ss << "(" << lhs << " " << static_cast(c) << " " << rhs << ")"; + if(context.use_parentheses) { + ss << '('; + } + ss << lhs << " " << static_cast(c) << " " << rhs; + if(context.use_parentheses) { + ss << ')'; + } return ss.str(); } }; @@ -266,7 +378,7 @@ namespace sqlite_orm { using statement_type = count_asterisk_t; template - std::string operator()(const statement_type &, const C &context) const { + std::string operator()(const statement_type&, const C& context) const { return serialize(count_asterisk_without_type{}, context); } }; @@ -276,9 +388,10 @@ namespace sqlite_orm { using statement_type = count_asterisk_without_type; template - std::string operator()(const statement_type &c, const C &) const { + std::string operator()(const statement_type& c, const C&) const { std::stringstream ss; - ss << static_cast(c) << "(*)"; + auto functionName = c.serialize(); + ss << functionName << "(*)"; return ss.str(); } }; @@ -288,9 +401,9 @@ namespace sqlite_orm { using statement_type = distinct_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; - auto expr = serialize(c.t, context); + auto expr = serialize(c.value, context); ss << static_cast(c) << "(" << expr << ")"; return ss.str(); } @@ -301,9 +414,9 @@ namespace sqlite_orm { using statement_type = all_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; - auto expr = serialize(c.t, context); + auto expr = serialize(c.value, context); ss << static_cast(c) << "(" << expr << ")"; return ss.str(); } @@ -314,12 +427,16 @@ namespace sqlite_orm { using statement_type = column_pointer; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(T)) << "'."; } - ss << "\"" << context.impl.column_name_simple(c.field) << "\""; + if(auto columnNamePointer = context.impl.column_name_simple(c.field)) { + ss << "\"" << *columnNamePointer << "\""; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } return ss.str(); } }; @@ -329,7 +446,7 @@ namespace sqlite_orm { using statement_type = cast_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast(c) << " ("; ss << serialize(c.expression, context) << " AS " << type_printer().print() << ")"; @@ -343,7 +460,7 @@ namespace sqlite_orm { using statement_type = T; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << serialize(c.left, context) << " "; ss << static_cast(c) << " "; @@ -357,17 +474,17 @@ namespace sqlite_orm { using statement_type = simple_case_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << "CASE "; - c.case_expression.apply([&ss, context](auto &c_) { + c.case_expression.apply([&ss, context](auto& c_) { ss << serialize(c_, context) << " "; }); - iterate_tuple(c.args, [&ss, context](auto &pair) { + iterate_tuple(c.args, [&ss, context](auto& pair) { ss << "WHEN " << serialize(pair.first, context) << " "; ss << "THEN " << serialize(pair.second, context) << " "; }); - c.else_expression.apply([&ss, context](auto &el) { + c.else_expression.apply([&ss, context](auto& el) { ss << "ELSE " << serialize(el, context) << " "; }); ss << "END"; @@ -380,7 +497,7 @@ namespace sqlite_orm { using statement_type = is_null_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << serialize(c.t, context) << " " << static_cast(c); return ss.str(); @@ -392,7 +509,7 @@ namespace sqlite_orm { using statement_type = is_not_null_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << serialize(c.t, context) << " " << static_cast(c); return ss.str(); @@ -404,7 +521,7 @@ namespace sqlite_orm { using statement_type = bitwise_not_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast(c) << " "; auto cString = serialize(c.argument, context); @@ -418,7 +535,7 @@ namespace sqlite_orm { using statement_type = negated_condition_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast(c) << " "; auto cString = serialize(c.c, context); @@ -433,7 +550,7 @@ namespace sqlite_orm { using statement_type = T; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto leftString = serialize(c.l, context); auto rightString = serialize(c.r, context); std::stringstream ss; @@ -453,7 +570,7 @@ namespace sqlite_orm { using statement_type = named_collate; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto newContext = context; newContext.use_parentheses = false; auto res = serialize(c.expr, newContext); @@ -466,7 +583,7 @@ namespace sqlite_orm { using statement_type = collate_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto newContext = context; newContext.use_parentheses = false; auto res = serialize(c.expr, newContext); @@ -475,44 +592,64 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { - using statement_type = in_t; + struct statement_serializator, void> { + using statement_type = dynamic_in_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; - auto leftString = serialize(c.l, context); + auto leftString = serialize(c.left, context); ss << leftString << " " << static_cast(c) << " "; auto newContext = context; newContext.use_parentheses = true; - ss << serialize(c.arg, newContext); + ss << serialize(c.argument, newContext); return ss.str(); } }; template - struct statement_serializator>, void> { - using statement_type = in_t>; + struct statement_serializator>, void> { + using statement_type = dynamic_in_t>; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; - auto leftString = serialize(c.l, context); - if(context.use_parentheses) { - ss << '('; - } - ss << leftString << " " << static_cast(c) << " ( "; - for(size_t index = 0; index < c.arg.size(); ++index) { - auto &value = c.arg[index]; - ss << " " << serialize(value, context); - if(index < c.arg.size() - 1) { + auto leftString = serialize(c.left, context); + ss << leftString << " " << static_cast(c) << " ("; + for(size_t index = 0; index < c.argument.size(); ++index) { + auto& value = c.argument[index]; + ss << serialize(value, context); + if(index < c.argument.size() - 1) { ss << ", "; } } - ss << " )"; - if(context.use_parentheses) { - ss << ')'; + ss << ")"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = in_t; + + template + std::string operator()(const statement_type& c, const C& context) const { + std::stringstream ss; + auto leftString = serialize(c.left, context); + ss << leftString << " " << static_cast(c) << " ("; + std::vector args; + using args_type = std::tuple; + args.reserve(std::tuple_size::value); + iterate_tuple(c.argument, [&args, &context](auto& v) { + args.push_back(serialize(v, context)); + }); + for(size_t i = 0; i < args.size(); ++i) { + ss << args[i]; + if(i < args.size() - 1) { + ss << ", "; + } } + ss << ")"; return ss.str(); } }; @@ -522,12 +659,12 @@ namespace sqlite_orm { using statement_type = like_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << serialize(c.arg, context) << " "; ss << static_cast(c) << " "; ss << serialize(c.pattern, context); - c.arg3.apply([&ss, &context](auto &value) { + c.arg3.apply([&ss, &context](auto& value) { ss << " ESCAPE " << serialize(value, context); }); return ss.str(); @@ -539,7 +676,7 @@ namespace sqlite_orm { using statement_type = glob_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << serialize(c.arg, context) << " "; ss << static_cast(c) << " "; @@ -553,7 +690,7 @@ namespace sqlite_orm { using statement_type = between_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; auto expr = serialize(c.expr, context); ss << expr << " " << static_cast(c) << " "; @@ -569,7 +706,7 @@ namespace sqlite_orm { using statement_type = exists_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast(c) << " "; ss << serialize(c.t, context); @@ -578,33 +715,37 @@ namespace sqlite_orm { }; template<> - struct statement_serializator { - using statement_type = constraints::autoincrement_t; + struct statement_serializator { + using statement_type = autoincrement_t; template - std::string operator()(const statement_type &c, const C &) const { + std::string operator()(const statement_type& c, const C&) const { return static_cast(c); } }; template - struct statement_serializator, void> { - using statement_type = constraints::primary_key_t; + struct statement_serializator, void> { + using statement_type = primary_key_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto res = static_cast(c); using columns_tuple = typename statement_type::columns_tuple; auto columnsCount = std::tuple_size::value; if(columnsCount) { res += "("; decltype(columnsCount) columnIndex = 0; - iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto &column) { - res += context.column_name(column); - if(columnIndex < columnsCount - 1) { - res += ", "; + iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto& column) { + if(auto columnNamePointer = context.column_name(column)) { + res += *columnNamePointer; + if(columnIndex < columnsCount - 1) { + res += ", "; + } + ++columnIndex; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); } - ++columnIndex; }); res += ")"; } @@ -613,23 +754,27 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { - using statement_type = constraints::unique_t; + struct statement_serializator, void> { + using statement_type = unique_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto res = static_cast(c); using columns_tuple = typename statement_type::columns_tuple; auto columnsCount = std::tuple_size::value; if(columnsCount) { res += "("; decltype(columnsCount) columnIndex = 0; - iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto &column) { - res += context.column_name(column); - if(columnIndex < columnsCount - 1) { - res += ", "; + iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto& column) { + if(auto columnNamePointer = context.column_name(column)) { + res += *columnNamePointer; + if(columnIndex < columnsCount - 1) { + res += ", "; + } + ++columnIndex; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); } - ++columnIndex; }); res += ")"; } @@ -638,38 +783,42 @@ namespace sqlite_orm { }; template<> - struct statement_serializator { - using statement_type = constraints::collate_t; + struct statement_serializator { + using statement_type = collate_constraint_t; template - std::string operator()(const statement_type &c, const C &) const { + std::string operator()(const statement_type& c, const C&) const { return static_cast(c); } }; template - struct statement_serializator, void> { - using statement_type = constraints::default_t; + struct statement_serializator, void> { + using statement_type = default_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { return static_cast(c) + " (" + serialize(c.value, context) + ")"; } }; template - struct statement_serializator, std::tuple>, void> { - using statement_type = constraints::foreign_key_t, std::tuple>; + struct statement_serializator, std::tuple>, void> { + using statement_type = foreign_key_t, std::tuple>; template - std::string operator()(const statement_type &fk, const C &context) const { + std::string operator()(const statement_type& fk, const C& context) const { std::stringstream ss; std::vector columnNames; using columns_type_t = typename std::decay::type::columns_type; constexpr const size_t columnsCount = std::tuple_size::value; columnNames.reserve(columnsCount); - iterate_tuple(fk.columns, [&columnNames, &context](auto &v) { - columnNames.push_back(context.impl.column_name(v)); + iterate_tuple(fk.columns, [&columnNames, &context](auto& v) { + if(auto columnNamePointer = context.impl.column_name(v)) { + columnNames.push_back(*columnNamePointer); + } else { + columnNames.push_back({}); + } }); ss << "FOREIGN KEY("; for(size_t i = 0; i < columnNames.size(); ++i) { @@ -689,8 +838,12 @@ namespace sqlite_orm { auto refTableName = context.impl.find_table_name(typeid(first_reference_mapped_type)); ss << '\'' << refTableName << '\''; } - iterate_tuple(fk.references, [&referencesNames, &context](auto &v) { - referencesNames.push_back(context.impl.column_name(v)); + iterate_tuple(fk.references, [&referencesNames, &context](auto& v) { + if(auto columnNamePointer = context.impl.column_name(v)) { + referencesNames.push_back(*columnNamePointer); + } else { + referencesNames.push_back({}); + } }); ss << "("; for(size_t i = 0; i < referencesNames.size(); ++i) { @@ -711,11 +864,11 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { - using statement_type = constraints::check_t; + struct statement_serializator, void> { + using statement_type = check_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { return static_cast(c) + " " + serialize(c.expression, context); } }; @@ -725,7 +878,7 @@ namespace sqlite_orm { using statement_type = column_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << "'" << c.name << "' "; using column_type = typename std::decay::type; @@ -741,12 +894,12 @@ namespace sqlite_orm { int tupleIndex = 0; iterate_tuple( c.constraints, - [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context](auto &v) { + [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context](auto& v) { using constraint_type = typename std::decay::type; constraintsStrings.push_back(serialize(v, context)); if(is_primary_key::value) { primaryKeyIndex = tupleIndex; - } else if(std::is_same::value) { + } else if(std::is_same::value) { autoincrementIndex = tupleIndex; } ++tupleIndex; @@ -755,7 +908,7 @@ namespace sqlite_orm { iter_swap(constraintsStrings.begin() + primaryKeyIndex, constraintsStrings.begin() + autoincrementIndex); } - for(auto &str: constraintsStrings) { + for(auto& str: constraintsStrings) { ss << str << ' '; } } @@ -771,11 +924,11 @@ namespace sqlite_orm { using statement_type = remove_all_t; template - std::string operator()(const statement_type &rem, const C &context) const { - auto &tImpl = context.impl.template get_impl(); + std::string operator()(const statement_type& rem, const C& context) const { + auto& tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "DELETE FROM '" << tImpl.table.name << "' "; - iterate_tuple(rem.conditions, [&context, &ss](auto &v) { + iterate_tuple(rem.conditions, [&context, &ss](auto& v) { ss << serialize(v, context); }); return ss.str(); @@ -787,33 +940,8 @@ namespace sqlite_orm { using statement_type = replace_t; template - std::string operator()(const statement_type &rep, const C &context) const { - using expression_type = typename std::decay::type; - using object_type = typename expression_object_type::type; - auto &tImpl = context.impl.template get_impl(); - std::stringstream ss; - ss << "REPLACE INTO '" << tImpl.table.name << "' ("; - auto columnNames = tImpl.table.column_names(); - auto columnNamesCount = columnNames.size(); - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - ss << "VALUES("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "?"; - if(i < columnNamesCount - 1) { - ss << ", "; - } else { - ss << ")"; - } - } - return ss.str(); + std::string operator()(const statement_type& rep, const C& context) const { + return serialize_replace_range_impl(rep, context, 1); } }; @@ -822,12 +950,12 @@ namespace sqlite_orm { using statement_type = insert_explicit; template - std::string operator()(const statement_type &ins, const C &context) const { + std::string operator()(const statement_type& ins, const C& context) const { constexpr const size_t colsCount = std::tuple_size>::value; static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); using expression_type = typename std::decay::type; using object_type = typename expression_object_type::type; - auto &tImpl = context.impl.template get_impl(); + auto& tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "INSERT INTO '" << tImpl.table.name << "' "; std::vector columnNames; @@ -835,7 +963,7 @@ namespace sqlite_orm { { auto columnsContext = context; columnsContext.skip_table_name = true; - iterate_tuple(ins.columns.columns, [&columnNames, &columnsContext](auto &m) { + iterate_tuple(ins.columns.columns, [&columnNames, &columnsContext](auto& m) { auto columnName = serialize(m, columnsContext); if(!columnName.empty()) { columnNames.push_back(columnName); @@ -873,16 +1001,16 @@ namespace sqlite_orm { using statement_type = update_t; template - std::string operator()(const statement_type &upd, const C &context) const { + std::string operator()(const statement_type& upd, const C& context) const { using expression_type = typename std::decay::type; using object_type = typename expression_object_type::type; - auto &tImpl = context.impl.template get_impl(); + auto& tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "UPDATE '" << tImpl.table.name << "' SET "; std::vector setColumnNames; - tImpl.table.for_each_column([&setColumnNames](auto &c) { - if(!c.template has>()) { + tImpl.table.for_each_column([&setColumnNames](auto& c) { + if(!c.template has>()) { setColumnNames.emplace_back(c.name); } }); @@ -908,17 +1036,43 @@ namespace sqlite_orm { } }; + template + struct statement_serializator, void> { + using statement_type = set_t; + + template + std::string operator()(const statement_type& statement, const C& context) const { + std::stringstream ss; + ss << static_cast(statement); + auto assignsCount = std::tuple_size::value; + decltype(assignsCount) assignIndex = 0; + auto leftContext = context; + leftContext.skip_table_name = true; + iterate_tuple(statement.assigns, + [&ss, &context, &leftContext, &assignIndex, assignsCount](auto& value) { + ss << ' ' << serialize(value.lhs, leftContext); + ss << ' ' << static_cast(value) << ' '; + ss << serialize(value.rhs, context); + if(assignIndex < assignsCount - 1) { + ss << ","; + } + ++assignIndex; + }); + return ss.str(); + } + }; + template struct statement_serializator, Wargs...>, void> { using statement_type = update_all_t, Wargs...>; template - std::string operator()(const statement_type &upd, const C &context) const { + std::string operator()(const statement_type& upd, const C& context) const { std::stringstream ss; ss << "UPDATE "; - table_name_collector collector{[&context](std::type_index ti) { + table_name_collector collector([&context](std::type_index ti) { return context.impl.find_table_name(ti); - }}; + }); iterate_ast(upd.set.assigns, collector); if(!collector.table_names.empty()) { if(collector.table_names.size() == 1) { @@ -927,7 +1081,7 @@ namespace sqlite_orm { std::vector setPairs; auto leftContext = context; leftContext.skip_table_name = true; - iterate_tuple(upd.set.assigns, [&context, &leftContext, &setPairs](auto &asgn) { + iterate_tuple(upd.set.assigns, [&context, &leftContext, &setPairs](auto& asgn) { std::stringstream sss; sss << serialize(asgn.lhs, leftContext); sss << " " << static_cast(asgn) << " "; @@ -941,7 +1095,7 @@ namespace sqlite_orm { ss << ", "; } } - iterate_tuple(upd.conditions, [&context, &ss](auto &v) { + iterate_tuple(upd.conditions, [&context, &ss](auto& v) { ss << serialize(v, context); }); return ss.str(); @@ -954,55 +1108,148 @@ namespace sqlite_orm { } }; + template + std::string serialize_insert_range_impl(const T& /*statement*/, const C& context, const int valuesCount) { + using object_type = typename expression_object_type::type; + auto& tImpl = context.impl.template get_impl(); + + std::stringstream ss; + ss << "INSERT INTO '" << tImpl.table.name << "' "; + std::vector columnNames; + auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); + + tImpl.table.for_each_column([&columnNames, &compositeKeyColumnNames](auto& c) { + using table_type = typename std::decay::type; + if(table_type::is_without_rowid || !c.template has>()) { + auto it = find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); + if(it == compositeKeyColumnNames.end()) { + columnNames.emplace_back(c.name); + } + } + }); + + const auto columnNamesCount = columnNames.size(); + if(columnNamesCount) { + ss << "("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNamesCount - 1) { + ss << ","; + } else { + ss << ")"; + } + ss << " "; + } + } else { + ss << "DEFAULT "; + } + ss << "VALUES "; + if(columnNamesCount) { + auto valuesString = [columnNamesCount] { + std::stringstream ss_; + ss_ << "("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss_ << "?"; + if(i < columnNamesCount - 1) { + ss_ << ", "; + } else { + ss_ << ")"; + } + } + return ss_.str(); + }(); + for(auto i = 0; i < valuesCount; ++i) { + ss << valuesString; + if(i < valuesCount - 1) { + ss << ","; + } + ss << " "; + } + } else if(valuesCount != 1) { + throw std::system_error(std::make_error_code(orm_error_code::cannot_use_default_value)); + } + + return ss.str(); + } + template struct statement_serializator, void> { using statement_type = insert_t; template - std::string operator()(const statement_type &, const C &context) const { - using object_type = typename expression_object_type::type; - auto &tImpl = context.impl.template get_impl(); + std::string operator()(const statement_type& statement, const C& context) const { + return serialize_insert_range_impl(statement, context, 1); + } + }; + + template + struct statement_serializator, void> { + using statement_type = into_t; + + template + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; - ss << "INSERT INTO '" << tImpl.table.name << "' "; - std::vector columnNames; - auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); + auto& tImpl = context.impl.template get_impl(); + ss << "INTO " << tImpl.table.name; + return ss.str(); + } + }; - tImpl.table.for_each_column([&tImpl, &columnNames, &compositeKeyColumnNames](auto &c) { - if(tImpl.table._without_rowid || !c.template has>()) { - auto it = find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); - if(it == compositeKeyColumnNames.end()) { - columnNames.emplace_back(c.name); - } + template + struct statement_serializator, void> { + using statement_type = columns_t; + + template + std::string operator()(const statement_type& statement, const C& context) const { + std::stringstream ss; + auto index = 0; + if(context.use_parentheses) { + ss << '('; + } + iterate_tuple(statement.columns, [&context, &ss, &index](auto& value) { + ss << serialize(value, context); + if(index < int(std::tuple_size>::value) - 1) { + ss << ", "; } + ++index; }); + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; - auto columnNamesCount = columnNames.size(); - if(columnNamesCount) { - ss << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } + template + struct statement_serializator< + T, + typename std::enable_if::value || is_replace_raw::value>::type> { + using statement_type = T; + + template + std::string operator()(const statement_type& statement, const C& context) const { + std::stringstream ss; + if(is_insert_raw::value) { + ss << "INSERT"; } else { - ss << "DEFAULT "; + ss << "REPLACE"; } - ss << "VALUES "; - if(columnNamesCount) { - ss << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "?"; - if(i < columnNamesCount - 1) { - ss << ", "; - } else { - ss << ")"; - } + iterate_tuple(statement.args, [&context, &ss](auto& value) { + using value_type = typename std::decay::type; + ss << ' '; + if(is_columns::value) { + auto newContext = context; + newContext.skip_table_name = true; + newContext.use_parentheses = true; + ss << serialize(value, newContext); + } else if(is_values::value || is_select::value) { + auto newContext = context; + newContext.use_parentheses = false; + ss << serialize(value, newContext); + } else { + ss << serialize(value, context); } - } + }); return ss.str(); } }; @@ -1012,8 +1259,8 @@ namespace sqlite_orm { using statement_type = remove_t; template - std::string operator()(const statement_type &, const C &context) const { - auto &tImpl = context.impl.template get_impl(); + std::string operator()(const statement_type&, const C& context) const { + auto& tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "DELETE FROM '" << tImpl.table.name << "' "; ss << "WHERE "; @@ -1029,110 +1276,71 @@ namespace sqlite_orm { } }; - template - struct statement_serializator, void> { - using statement_type = replace_range_t; - - template - std::string operator()(const statement_type &rep, const C &context) const { - using expression_type = typename std::decay::type; - using object_type = typename expression_type::object_type; - auto &tImpl = context.impl.template get_impl(); - std::stringstream ss; - ss << "REPLACE INTO '" << tImpl.table.name << "' ("; - auto columnNames = tImpl.table.column_names(); - auto columnNamesCount = columnNames.size(); + template + std::string serialize_replace_range_impl(const T& rep, const C& context, const int valuesCount) { + using expression_type = typename std::decay::type; + using object_type = typename expression_object_type::type; + auto& tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "REPLACE INTO '" << tImpl.table.name << "' ("; + auto columnNames = tImpl.table.column_names(); + auto columnNamesCount = columnNames.size(); + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNamesCount - 1) { + ss << ", "; + } else { + ss << ") "; + } + } + ss << "VALUES "; + auto valuesString = [columnNamesCount] { + std::stringstream ss_; + ss_ << "("; for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; + ss_ << "?"; if(i < columnNamesCount - 1) { - ss << ", "; + ss_ << ", "; } else { - ss << ") "; + ss_ << ")"; } } - ss << "VALUES "; - auto valuesString = [columnNamesCount] { - std::stringstream ss_; - ss_ << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss_ << "?"; - if(i < columnNamesCount - 1) { - ss_ << ", "; - } else { - ss_ << ")"; - } - } - return ss_.str(); - }(); - auto valuesCount = static_cast(std::distance(rep.range.first, rep.range.second)); - for(auto i = 0; i < valuesCount; ++i) { - ss << valuesString; - if(i < valuesCount - 1) { - ss << ","; - } - ss << " "; + return ss_.str(); + }(); + for(auto i = 0; i < valuesCount; ++i) { + ss << valuesString; + if(i < valuesCount - 1) { + ss << ","; } - return ss.str(); + ss << " "; } - }; + return ss.str(); + } - template - struct statement_serializator, void> { - using statement_type = insert_range_t; + template + struct statement_serializator, void> { + using statement_type = replace_range_t; template - std::string operator()(const statement_type &statement, const C &context) const { - using expression_type = typename std::decay::type; - using object_type = typename expression_type::object_type; - auto &tImpl = context.impl.template get_impl(); + std::string operator()(const statement_type& rep, const C& context) const { + auto valuesCount = static_cast(std::distance(rep.range.first, rep.range.second)); + return serialize_replace_range_impl(rep, context, valuesCount); + } + }; - std::stringstream ss; - ss << "INSERT INTO '" << tImpl.table.name << "' ("; - std::vector columnNames; - tImpl.table.for_each_column([&columnNames](auto &c) { - if(!c.template has>()) { - columnNames.emplace_back(c.name); - } - }); + template + struct statement_serializator, void> { + using statement_type = insert_range_t; - auto columnNamesCount = columnNames.size(); - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - ss << "VALUES "; - auto valuesString = [columnNamesCount] { - std::stringstream ss_; - ss_ << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss_ << "?"; - if(i < columnNamesCount - 1) { - ss_ << ", "; - } else { - ss_ << ")"; - } - } - return ss_.str(); - }(); - auto valuesCount = static_cast(std::distance(statement.range.first, statement.range.second)); - for(auto i = 0; i < valuesCount; ++i) { - ss << valuesString; - if(i < valuesCount - 1) { - ss << ","; - } - ss << " "; - } - return ss.str(); + template + std::string operator()(const statement_type& statement, const C& context) const { + const auto valuesCount = static_cast(std::distance(statement.range.first, statement.range.second)); + return serialize_insert_range_impl(statement, context, valuesCount); } }; template - std::string serialize_get_all_impl(const T &get, const C &context) { + std::string serialize_get_all_impl(const T& get, const C& context) { using primary_type = typename T::type; table_name_collector collector; @@ -1141,7 +1349,7 @@ namespace sqlite_orm { iterate_ast(get.conditions, collector); std::stringstream ss; ss << "SELECT "; - auto &tImpl = context.impl.template get_impl(); + auto& tImpl = context.impl.template get_impl(); auto columnNames = tImpl.table.column_names(); for(size_t i = 0; i < columnNames.size(); ++i) { ss << "\"" << tImpl.table.name << "\"." @@ -1156,7 +1364,7 @@ namespace sqlite_orm { std::vector> tableNames(collector.table_names.begin(), collector.table_names.end()); for(size_t i = 0; i < tableNames.size(); ++i) { - auto &tableNamePair = tableNames[i]; + auto& tableNamePair = tableNames[i]; ss << "'" << tableNamePair.first << "' "; if(!tableNamePair.second.empty()) { ss << tableNamePair.second << " "; @@ -1166,7 +1374,7 @@ namespace sqlite_orm { } ss << " "; } - iterate_tuple(get.conditions, [&context, &ss](auto &v) { + iterate_tuple(get.conditions, [&context, &ss](auto& v) { ss << serialize(v, context); }); return ss.str(); @@ -1178,7 +1386,7 @@ namespace sqlite_orm { using statement_type = get_all_optional_t; template - std::string operator()(const statement_type &get, const C &context) const { + std::string operator()(const statement_type& get, const C& context) const { return serialize_get_all_impl(get, context); } }; @@ -1189,7 +1397,7 @@ namespace sqlite_orm { using statement_type = get_all_pointer_t; template - std::string operator()(const statement_type &get, const C &context) const { + std::string operator()(const statement_type& get, const C& context) const { return serialize_get_all_impl(get, context); } }; @@ -1199,15 +1407,15 @@ namespace sqlite_orm { using statement_type = get_all_t; template - std::string operator()(const statement_type &get, const C &context) const { + std::string operator()(const statement_type& get, const C& context) const { return serialize_get_all_impl(get, context); } }; template - std::string serialize_get_impl(const T &, const C &context) { + std::string serialize_get_impl(const T&, const C& context) { using primary_type = typename T::type; - auto &tImpl = context.impl.template get_impl(); + auto& tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "SELECT "; auto columnNames = tImpl.table.column_names(); @@ -1240,7 +1448,7 @@ namespace sqlite_orm { using statement_type = get_t; template - std::string operator()(const statement_type &get, const C &context) const { + std::string operator()(const statement_type& get, const C& context) const { return serialize_get_impl(get, context); } }; @@ -1250,8 +1458,29 @@ namespace sqlite_orm { using statement_type = get_pointer_t; template - std::string operator()(const statement_type &get, const C &context) const { - return serialize_get_impl(get, context); + std::string operator()(const statement_type& statement, const C& context) const { + return serialize_get_impl(statement, context); + } + }; + + template<> + struct statement_serializator { + using statement_type = insert_constraint; + + template + std::string operator()(const statement_type& statement, const C& context) const { + switch(statement) { + case insert_constraint::abort: + return "OR ABORT"; + case insert_constraint::fail: + return "OR FAIL"; + case insert_constraint::ignore: + return "OR IGNORE"; + case insert_constraint::replace: + return "OR REPLACE"; + case insert_constraint::rollback: + return "OR ROLLBACK"; + } } }; @@ -1261,7 +1490,7 @@ namespace sqlite_orm { using statement_type = get_optional_t; template - std::string operator()(const statement_type &get, const C &context) const { + std::string operator()(const statement_type& get, const C& context) const { return serialize_get_impl(get, context); } }; @@ -1271,11 +1500,12 @@ namespace sqlite_orm { using statement_type = select_t; template - std::string operator()(const statement_type &sel, const C &context) const { + std::string operator()(const statement_type& sel, const C& context) const { std::stringstream ss; - if(!is_base_of_template::value) { - if(!sel.highest_level) { - ss << "( "; + const auto isCompoundOperator = is_base_of_template::value; + if(!isCompoundOperator) { + if(!sel.highest_level && context.use_parentheses) { + ss << "("; } ss << "SELECT "; } @@ -1286,46 +1516,47 @@ namespace sqlite_orm { for(size_t i = 0; i < columnNames.size(); ++i) { ss << columnNames[i]; if(i < columnNames.size() - 1) { - ss << ","; + ss << ", "; } - ss << " "; } - table_name_collector collector{[&context](std::type_index ti) { + table_name_collector collector([&context](std::type_index ti) { return context.impl.find_table_name(ti); - }}; - iterate_ast(sel.col, collector); - iterate_ast(sel.conditions, collector); - internal::join_iterator()([&collector, &context](const auto &c) { - using original_join_type = typename std::decay::type::join_type::type; - using cross_join_type = typename internal::mapped_type_proxy::type; - auto crossJoinedTableName = context.impl.find_table_name(typeid(cross_join_type)); - auto tableAliasString = alias_extractor::get(); - std::pair tableNameWithAlias(std::move(crossJoinedTableName), - std::move(tableAliasString)); - collector.table_names.erase(tableNameWithAlias); }); - if(!collector.table_names.empty()) { - ss << "FROM "; - std::vector> tableNames(collector.table_names.begin(), - collector.table_names.end()); - for(size_t i = 0; i < tableNames.size(); ++i) { - auto &tableNamePair = tableNames[i]; - ss << "'" << tableNamePair.first << "' "; - if(!tableNamePair.second.empty()) { - ss << tableNamePair.second << " "; - } - if(int(i) < int(tableNames.size()) - 1) { - ss << ","; + const auto explicitFromItemsCount = count_tuple, is_from>::value; + if(!explicitFromItemsCount) { + iterate_ast(sel.col, collector); + iterate_ast(sel.conditions, collector); + join_iterator()([&collector, &context](const auto& c) { + using original_join_type = typename std::decay::type::join_type::type; + using cross_join_type = typename internal::mapped_type_proxy::type; + auto crossJoinedTableName = context.impl.find_table_name(typeid(cross_join_type)); + auto tableAliasString = alias_extractor::get(); + std::pair tableNameWithAlias(std::move(crossJoinedTableName), + std::move(tableAliasString)); + collector.table_names.erase(tableNameWithAlias); + }); + if(!collector.table_names.empty() && !isCompoundOperator) { + ss << " FROM "; + std::vector> tableNames(collector.table_names.begin(), + collector.table_names.end()); + for(size_t i = 0; i < tableNames.size(); ++i) { + auto& tableNamePair = tableNames[i]; + ss << "'" << tableNamePair.first << "'"; + if(!tableNamePair.second.empty()) { + ss << ' ' << tableNamePair.second; + } + if(int(i) < int(tableNames.size()) - 1) { + ss << ", "; + } } - ss << " "; } } - iterate_tuple(sel.conditions, [&context, &ss](auto &v) { - ss << serialize(v, context); + iterate_tuple(sel.conditions, [&context, &ss](auto& v) { + ss << ' ' << serialize(v, context); }); if(!is_base_of_template::value) { - if(!sel.highest_level) { - ss << ") "; + if(!sel.highest_level && context.use_parentheses) { + ss << ")"; } } return ss.str(); @@ -1337,7 +1568,7 @@ namespace sqlite_orm { using statement_type = indexed_column_t; template - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; ss << serialize(statement.column_or_expression, context); if(!statement._collation_name.empty()) { @@ -1364,7 +1595,7 @@ namespace sqlite_orm { using statement_type = index_t; template - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; ss << "CREATE "; if(statement.unique) { @@ -1376,11 +1607,12 @@ namespace sqlite_orm { ss << "INDEX IF NOT EXISTS '" << statement.name << "' ON '" << context.impl.find_table_name(typeid(indexed_type)) << "' ("; std::vector columnNames; - iterate_tuple(statement.columns, [&columnNames, &context](auto &v) { - columnNames.push_back(context.column_name(v.column_or_expression)); + iterate_tuple(statement.columns, [&columnNames, &context](auto& v) { + auto columnName = serialize(v, context); + columnNames.push_back(move(columnName)); }); for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "'" << columnNames[i] << "'"; + ss << columnNames[i]; if(i < columnNames.size() - 1) { ss << ", "; } @@ -1390,15 +1622,44 @@ namespace sqlite_orm { } }; + template + struct statement_serializator, void> { + using statement_type = from_t; + + template + std::string operator()(const statement_type& statement, const C& context) const { + using tuple = std::tuple; + + std::stringstream ss; + ss << "FROM "; + size_t index = 0; + iterate_tuple([&context, &ss, &index](auto* itemPointer) { + using mapped_type = typename std::remove_pointer::type; + + auto aliasString = alias_extractor::get(); + ss << "'" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) + << "'"; + if(aliasString.length()) { + ss << " '" << aliasString << "'"; + } + if(index < std::tuple_size::value - 1) { + ss << ", "; + } + ++index; + }); + return ss.str(); + } + }; + template struct statement_serializator, void> { using statement_type = where_t; template - std::string operator()(const statement_type &w, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; - ss << static_cast(w) << " "; - auto whereString = serialize(w.c, context); + ss << statement.serialize() << " "; + auto whereString = serialize(statement.expression, context); ss << "( " << whereString << ") "; return ss.str(); } @@ -1409,7 +1670,7 @@ namespace sqlite_orm { using statement_type = order_by_t; template - std::string operator()(const statement_type &orderBy, const C &context) const { + std::string operator()(const statement_type& orderBy, const C& context) const { std::stringstream ss; ss << static_cast(orderBy) << " "; auto orderByString = serialize_order_by(orderBy, context); @@ -1423,7 +1684,7 @@ namespace sqlite_orm { using statement_type = dynamic_order_by_t; template - std::string operator()(const statement_type &orderBy, const CC &context) const { + std::string operator()(const statement_type& orderBy, const CC& context) const { return serialize_order_by(orderBy, context); } }; @@ -1433,10 +1694,10 @@ namespace sqlite_orm { using statement_type = multi_order_by_t; template - std::string operator()(const statement_type &orderBy, const C &context) const { + std::string operator()(const statement_type& orderBy, const C& context) const { std::stringstream ss; std::vector expressions; - iterate_tuple(orderBy.args, [&expressions, &context](auto &v) { + iterate_tuple(orderBy.args, [&expressions, &context](auto& v) { auto expression = serialize_order_by(v, context); expressions.push_back(move(expression)); }); @@ -1457,7 +1718,7 @@ namespace sqlite_orm { using statement_type = cross_join_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast(c) << " "; ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; @@ -1470,7 +1731,7 @@ namespace sqlite_orm { using statement_type = inner_join_t; template - std::string operator()(const statement_type &l, const C &context) const { + std::string operator()(const statement_type& l, const C& context) const { std::stringstream ss; ss << static_cast(l) << " "; auto aliasString = alias_extractor::get(); @@ -1488,7 +1749,7 @@ namespace sqlite_orm { using statement_type = on_t; template - std::string operator()(const statement_type &t, const C &context) const { + std::string operator()(const statement_type& t, const C& context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; @@ -1502,10 +1763,14 @@ namespace sqlite_orm { using statement_type = join_t; template - std::string operator()(const statement_type &l, const C &context) const { + std::string operator()(const statement_type& l, const C& context) const { std::stringstream ss; ss << static_cast(l) << " "; - ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + auto aliasString = alias_extractor::get(); + ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) << "' "; + if(aliasString.length()) { + ss << "'" << aliasString << "' "; + } ss << serialize(l.constraint, context); return ss.str(); } @@ -1516,10 +1781,14 @@ namespace sqlite_orm { using statement_type = left_join_t; template - std::string operator()(const statement_type &l, const C &context) const { + std::string operator()(const statement_type& l, const C& context) const { std::stringstream ss; ss << static_cast(l) << " "; - ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + auto aliasString = alias_extractor::get(); + ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) << "' "; + if(aliasString.length()) { + ss << "'" << aliasString << "' "; + } ss << serialize(l.constraint, context); return ss.str(); } @@ -1530,10 +1799,14 @@ namespace sqlite_orm { using statement_type = left_outer_join_t; template - std::string operator()(const statement_type &l, const C &context) const { + std::string operator()(const statement_type& l, const C& context) const { std::stringstream ss; ss << static_cast(l) << " "; - ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + auto aliasString = alias_extractor::get(); + ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) << "' "; + if(aliasString.length()) { + ss << "'" << aliasString << "' "; + } ss << serialize(l.constraint, context); return ss.str(); } @@ -1544,7 +1817,7 @@ namespace sqlite_orm { using statement_type = natural_join_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast(c) << " "; ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; @@ -1557,12 +1830,12 @@ namespace sqlite_orm { using statement_type = group_by_t; template - std::string operator()(const statement_type &groupBy, const C &context) const { + std::string operator()(const statement_type& groupBy, const C& context) const { std::stringstream ss; std::vector expressions; auto newContext = context; newContext.skip_table_name = false; - iterate_tuple(groupBy.args, [&expressions, &newContext](auto &v) { + iterate_tuple(groupBy.args, [&expressions, &newContext](auto& v) { auto expression = serialize(v, newContext); expressions.push_back(expression); }); @@ -1583,7 +1856,7 @@ namespace sqlite_orm { using statement_type = having_t; template - std::string operator()(const statement_type &hav, const C &context) const { + std::string operator()(const statement_type& hav, const C& context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; @@ -1602,21 +1875,21 @@ namespace sqlite_orm { using statement_type = limit_t; template - std::string operator()(const statement_type &limt, const C &context) const { + std::string operator()(const statement_type& limt, const C& context) const { auto newContext = context; newContext.skip_table_name = false; std::stringstream ss; ss << static_cast(limt) << " "; if(HO) { if(OI) { - limt.off.apply([&newContext, &ss](auto &value) { + limt.off.apply([&newContext, &ss](auto& value) { ss << serialize(value, newContext); }); ss << ", "; ss << serialize(limt.lim, newContext); } else { ss << serialize(limt.lim, newContext) << " OFFSET "; - limt.off.apply([&newContext, &ss](auto &value) { + limt.off.apply([&newContext, &ss](auto& value) { ss << serialize(value, newContext); }); } @@ -1627,12 +1900,22 @@ namespace sqlite_orm { } }; + template<> + struct statement_serializator { + using statement_type = default_values_t; + + template + std::string operator()(const statement_type& statement, const C& context) const { + return "DEFAULT VALUES"; + } + }; + template struct statement_serializator, void> { using statement_type = using_t; template - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { auto newContext = context; newContext.skip_table_name = true; return static_cast(statement) + " (" + serialize(statement.column, newContext) + " )"; @@ -1644,12 +1927,12 @@ namespace sqlite_orm { using statement_type = std::tuple; template - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; ss << '('; auto index = 0; using TupleSize = std::tuple_size; - iterate_tuple(statement, [&context, &index, &ss](auto &value) { + iterate_tuple(statement, [&context, &index, &ss](auto& value) { ss << serialize(value, context); if(index < TupleSize::value - 1) { ss << ", "; @@ -1666,7 +1949,7 @@ namespace sqlite_orm { using statement_type = values_t; template - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; if(context.use_parentheses) { ss << '('; @@ -1674,10 +1957,10 @@ namespace sqlite_orm { ss << "VALUES "; { auto index = 0; - auto &tuple = statement.tuple; + auto& tuple = statement.tuple; using tuple_type = typename std::decay::type; using TupleSize = std::tuple_size; - iterate_tuple(tuple, [&context, &index, &ss](auto &value) { + iterate_tuple(tuple, [&context, &index, &ss](auto& value) { ss << serialize(value, context); if(index < TupleSize::value - 1) { ss << ", "; @@ -1697,7 +1980,7 @@ namespace sqlite_orm { using statement_type = dynamic_values_t; template - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; if(context.use_parentheses) { ss << '('; @@ -1706,7 +1989,7 @@ namespace sqlite_orm { { auto vectorSize = statement.vector.size(); for(decltype(vectorSize) index = 0; index < vectorSize; ++index) { - auto &value = statement.vector[index]; + auto& value = statement.vector[index]; ss << serialize(value, context); if(index < vectorSize - 1) { ss << ", "; diff --git a/dev/static_magic.h b/dev/static_magic.h index 23fe1b84d..369bdc694 100644 --- a/dev/static_magic.h +++ b/dev/static_magic.h @@ -9,27 +9,27 @@ namespace sqlite_orm { namespace internal { static inline decltype(auto) empty_callable() { - static auto res = [](auto &&...) {}; + static auto res = [](auto&&...) {}; return (res); } template - decltype(auto) static_if(std::true_type, const T &t, const F &) { + decltype(auto) static_if(std::true_type, const T& t, const F&) { return (t); } template - decltype(auto) static_if(std::false_type, const T &, const F &f) { + decltype(auto) static_if(std::false_type, const T&, const F& f) { return (f); } template - decltype(auto) static_if(const T &t, const F &f) { + decltype(auto) static_if(const T& t, const F& f) { return static_if(std::integral_constant{}, t, f); } template - decltype(auto) static_if(const T &t) { + decltype(auto) static_if(const T& t) { return static_if(std::integral_constant{}, t, empty_callable()); } diff --git a/dev/storage.h b/dev/storage.h index e85255df1..8391c62bf 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -22,7 +22,7 @@ #include "row_extractor_builder.h" #include "error_code.h" #include "type_printer.h" -#include "tuple_helper.h" +#include "tuple_helper/tuple_helper.h" #include "constraints.h" #include "type_is_nullable.h" #include "field_printer.h" @@ -47,6 +47,8 @@ #include "statement_serializator.h" #include "table_name_collector.h" #include "object_from_column_builder.h" +#include "table.h" +#include "column.h" namespace sqlite_orm { @@ -65,10 +67,10 @@ namespace sqlite_orm { * @param filename database filename. * @param impl_ storage_impl head */ - storage_t(const std::string &filename, impl_type impl_) : + storage_t(const std::string& filename, impl_type impl_) : storage_base{filename, foreign_keys_count(impl_)}, impl(std::move(impl_)) {} - storage_t(const storage_t &other) : storage_base(other), impl(other.impl) {} + storage_t(const storage_t& other) : storage_base(other), impl(other.impl) {} protected: impl_type impl; @@ -86,42 +88,30 @@ namespace sqlite_orm { friend struct serializator_context_builder; template - void create_table(sqlite3 *db, const std::string &tableName, const I &tableImpl) { + void create_table(sqlite3* db, const std::string& tableName, const I& tableImpl) { + using table_type = typename std::decay::type; std::stringstream ss; ss << "CREATE TABLE '" << tableName << "' ( "; auto columnsCount = tableImpl.table.columns_count; auto index = 0; using context_t = serializator_context; context_t context{this->impl}; - iterate_tuple(tableImpl.table.columns, [columnsCount, &index, &ss, &context](auto &c) { + iterate_tuple(tableImpl.table.columns, [columnsCount, &index, &ss, &context](auto& c) { ss << serialize(c, context); if(index < columnsCount - 1) { ss << ", "; } index++; }); - ss << ") "; - if(tableImpl.table._without_rowid) { - ss << "WITHOUT ROWID "; - } - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + ss << ")"; + if(table_type::is_without_rowid) { + ss << " WITHOUT ROWID "; } + perform_void_exec(db, ss.str()); } template - void backup_table(sqlite3 *db, const I &tableImpl, const std::vector &columnsToIgnore) { + void backup_table(sqlite3* db, const I& tableImpl, const std::vector& columnsToIgnore) { // here we copy source table to another with a name with '_backup' suffix, but in case table with such // a name already exists we append suffix 1, then 2, etc until we find a free name.. @@ -156,18 +146,49 @@ namespace sqlite_orm { } template - auto &get_impl() const { + void assert_insertable_type() const { + auto& tImpl = this->get_impl(); + using table_type = typename std::decay::type; + using columns_type = typename std::decay::type; + + using bool_type = std::integral_constant; + + static_if( + [](auto& tImpl) { + std::ignore = tImpl; + + // all right. it's a "without_rowid" table + }, + [](auto& tImpl) { + std::ignore = tImpl; + static_assert( + count_tuple::value <= 1, + "Attempting to execute 'insert' request into an noninsertable table was detected. " + "Insertable table cannot contain > 1 primary keys. Please use 'replace' instead of " + "'insert', or you can use 'insert' with explicit column listing."); + static_assert( + count_tuple::value == 0, + "Attempting to execute 'insert' request into an noninsertable table was detected. " + "Insertable table cannot contain non-standard primary keys. Please use 'replace' instead " + "of 'insert', or you can use 'insert' with explicit column listing."); + + // unfortunately, this static_assert's can't see an composite keys(( + })(tImpl); + } + + template + auto& get_impl() const { return this->impl.template get_impl(); } template - auto &get_impl() { + auto& get_impl() { return this->impl.template get_impl(); } public: template - view_t iterate(Args &&... args) { + view_t iterate(Args&&... args) { this->assert_mapped_type(); auto con = this->get_connection(); @@ -182,7 +203,7 @@ namespace sqlite_orm { * @example: storage.remove_all(where(in(&User::id, {5, 6, 7}))); - DELETE FROM users WHERE id IN (5, 6, 7) */ template - void remove_all(Args &&... args) { + void remove_all(Args&&... args) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::remove_all(std::forward(args)...)); this->execute(statement); @@ -207,7 +228,7 @@ namespace sqlite_orm { * @param o object to be updated. */ template - void update(const O &o) { + void update(const O& o) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::update(std::ref(o))); this->execute(statement); @@ -221,7 +242,7 @@ namespace sqlite_orm { protected: template - std::string group_concat_internal(F O::*m, std::unique_ptr y, Args &&... args) { + std::string group_concat_internal(F O::*m, std::unique_ptr y, Args&&... args) { this->assert_mapped_type(); std::vector rows; if(y) { @@ -246,7 +267,7 @@ namespace sqlite_orm { * @example: storage.get_all(where(like(&User::name, "N%")), order_by(&User::id)); - SELECT * FROM users WHERE name LIKE 'N%' ORDER BY id */ template - auto get_all(Args &&... args) { + auto get_all(Args&&... args) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::get_all(std::forward(args)...)); return this->execute(statement); @@ -261,7 +282,7 @@ namespace sqlite_orm { * @example: storage.get_all>(where(like(&User::name, "N%")), order_by(&User::id)); - SELECT * FROM users WHERE name LIKE 'N%' ORDER BY id */ template - auto get_all(Args &&... args) { + auto get_all(Args&&... args) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::get_all(std::forward(args)...)); return this->execute(statement); @@ -276,7 +297,7 @@ namespace sqlite_orm { * @example: storage.get_all_pointer(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 */ template - auto get_all_pointer(Args &&... args) { + auto get_all_pointer(Args&&... args) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::get_all_pointer(std::forward(args)...)); return this->execute(statement); @@ -291,7 +312,7 @@ namespace sqlite_orm { * @example: storage.get_all_pointer>(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 */ template - auto get_all_pointer(Args &&... args) { + auto get_all_pointer(Args&&... args) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::get_all_pointer(std::forward(args)...)); return this->execute(statement); @@ -359,7 +380,7 @@ namespace sqlite_orm { * @return Number of O object in table. */ template::type> - int count(Args &&... args) { + int count(Args&&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::count(), std::forward(args)...); if(!rows.empty()) { @@ -372,9 +393,10 @@ namespace sqlite_orm { /** * SELECT COUNT(X) https://www.sqlite.org/lang_aggfunc.html#count * @param m member pointer to class mapped to the storage. + * @return count of `m` values from database. */ template - int count(F O::*m, Args &&... args) { + int count(F O::*m, Args&&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::count(m), std::forward(args)...); if(!rows.empty()) { @@ -387,10 +409,10 @@ namespace sqlite_orm { /** * AVG(X) query. https://www.sqlite.org/lang_aggfunc.html#avg * @param m is a class member pointer (the same you passed into make_column). - * @return average value from db. + * @return average value from database. */ template - double avg(F O::*m, Args &&... args) { + double avg(F O::*m, Args&&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::avg(m), std::forward(args)...); if(!rows.empty()) { @@ -415,7 +437,7 @@ namespace sqlite_orm { class... Args, class Tuple = std::tuple, typename sfinae = typename std::enable_if::value >= 1>::type> - std::string group_concat(F O::*m, Args &&... args) { + std::string group_concat(F O::*m, Args&&... args) { return this->group_concat_internal(m, {}, std::forward(args)...); } @@ -425,14 +447,14 @@ namespace sqlite_orm { * @return group_concat query result. */ template - std::string group_concat(F O::*m, std::string y, Args &&... args) { + std::string group_concat(F O::*m, std::string y, Args&&... args) { return this->group_concat_internal(m, std::make_unique(move(y)), std::forward(args)...); } template - std::string group_concat(F O::*m, const char *y, Args &&... args) { + std::string group_concat(F O::*m, const char* y, Args&&... args) { std::unique_ptr str; if(y) { str = std::make_unique(y); @@ -448,7 +470,7 @@ namespace sqlite_orm { * @return std::unique_ptr with max value or null if sqlite engine returned null. */ template::type> - std::unique_ptr max(F O::*m, Args &&... args) { + std::unique_ptr max(F O::*m, Args&&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::max(m), std::forward(args)...); if(!rows.empty()) { @@ -464,7 +486,7 @@ namespace sqlite_orm { * @return std::unique_ptr with min value or null if sqlite engine returned null. */ template::type> - std::unique_ptr min(F O::*m, Args &&... args) { + std::unique_ptr min(F O::*m, Args&&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::min(m), std::forward(args)...); if(!rows.empty()) { @@ -480,7 +502,7 @@ namespace sqlite_orm { * @return std::unique_ptr with sum value or null if sqlite engine returned null. */ template::type> - std::unique_ptr sum(F O::*m, Args &&... args) { + std::unique_ptr sum(F O::*m, Args&&... args) { this->assert_mapped_type(); std::vector> rows = this->select(sqlite_orm::sum(m), std::forward(args)...); @@ -502,7 +524,7 @@ namespace sqlite_orm { * https://www.sqlite.org/lang_aggfunc.html) */ template - double total(F O::*m, Args &&... args) { + double total(F O::*m, Args&&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::total(m), std::forward(args)...); if(!rows.empty()) { @@ -528,7 +550,7 @@ namespace sqlite_orm { template typename std::enable_if::value, std::string>::type - dump(const T &preparedStatement) const { + dump(const T& preparedStatement) const { using context_t = serializator_context; context_t context{this->impl}; return serialize(preparedStatement.t, context); @@ -540,13 +562,13 @@ namespace sqlite_orm { */ template typename std::enable_if::value, std::string>::type - dump(const O &o) { - auto &tImpl = this->get_impl(); + dump(const O& o) { + auto& tImpl = this->get_impl(); std::stringstream ss; ss << "{ "; using pair = std::pair; std::vector pairs; - tImpl.table.for_each_column([&pairs, &o](auto &c) { + tImpl.table.for_each_column([&pairs, &o](auto& c) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; pair p{c.name, std::string()}; @@ -560,7 +582,7 @@ namespace sqlite_orm { pairs.push_back(move(p)); }); for(size_t i = 0; i < pairs.size(); ++i) { - auto &p = pairs[i]; + auto& p = pairs[i]; ss << p.first << " : '" << p.second << "'"; if(i < pairs.size() - 1) { ss << ", "; @@ -578,7 +600,7 @@ namespace sqlite_orm { * id and creates own one. */ template - void replace(const O &o) { + void replace(const O& o) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::replace(std::ref(o))); this->execute(statement); @@ -596,8 +618,19 @@ namespace sqlite_orm { this->execute(statement); } + template + void replace_range(It from, It to, L transformer) { + this->assert_mapped_type(); + if(from == to) { + return; + } + + auto statement = this->prepare(sqlite_orm::replace_range(from, to, std::move(transformer))); + this->execute(statement); + } + template - int insert(const O &o, columns_t cols) { + int insert(const O& o, columns_t cols) { constexpr const size_t colsCount = std::tuple_size>::value; static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); this->assert_mapped_type(); @@ -611,22 +644,116 @@ namespace sqlite_orm { * @return id of just created object. */ template - int insert(const O &o) { + int insert(const O& o) { this->assert_mapped_type(); - auto statement = this->prepare(sqlite_orm::insert(std::ref(o))); - return int(this->execute(statement)); + this->assert_insertable_type(); + + return call_insert_impl_and_catch_constraint_failed([this, &o]() { + auto statement = this->prepare(sqlite_orm::insert(std::ref(o))); + return int(this->execute(statement)); + }); + } + + /** + * Raw insert routine. Use this if `insert` with object does not fit you. This insert is designed to be able + * to call any type of `INSERT` query with no limitations. + * @example + * ```sql + * INSERT INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * storage.insert(into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix"))); + * ``` + * One more example: + * ```sql + * INSERT INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * storage.insert(into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs"))); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * INSERT INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * storage.insert(into(), default_values()); + * ``` + * Also one can use `INSERT OR ABORT`/`INSERT OR FAIL`/`INSERT OR IGNORE`/`INSERT OR REPLACE`/`INSERT ROLLBACK`: + * ```c++ + * storage.insert(or_ignore(), into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs"))); + * storage.insert(or_rollback(), into(), default_values()); + * storage.insert(or_abort(), into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix"))); + * ``` + */ + template + void insert(Args... args) { + auto statement = this->prepare(sqlite_orm::insert(std::forward(args)...)); + this->execute(statement); + } + + /** + * Raw replace statement creation routine. Use this if `replace` with object does not fit you. This replace is designed to be able + * to call any type of `REPLACE` query with no limitations. Actually this is the same query as raw insert except `OR...` option existance. + * @example + * ```sql + * REPLACE INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * storage.prepare(replace(into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * ``` + * One more example: + * ```sql + * REPLACE INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * storage.prepare(replace(into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * REPLACE INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * storage.prepare(replace(into(), default_values())); + * ``` + */ + template + void replace(Args... args) { + auto statement = this->prepare(sqlite_orm::replace(std::forward(args)...)); + this->execute(statement); } template void insert_range(It from, It to) { using O = typename std::iterator_traits::value_type; this->assert_mapped_type(); + this->assert_insertable_type(); if(from == to) { return; } - auto statement = this->prepare(sqlite_orm::insert_range(from, to)); - this->execute(statement); + call_insert_impl_and_catch_constraint_failed([this, from, to]() { + auto statement = this->prepare(sqlite_orm::insert_range(from, to)); + this->execute(statement); + }); + } + + template + void insert_range(It from, It to, L transformer) { + this->assert_mapped_type(); + this->assert_insertable_type(); + if(from == to) { + return; + } + call_insert_impl_and_catch_constraint_failed([this, from, to, transformer = std::move(transformer)]() { + auto statement = this->prepare(sqlite_orm::insert_range(from, to, std::move(transformer))); + this->execute(statement); + }); } /** @@ -636,7 +763,7 @@ namespace sqlite_orm { template void rename_table(std::string name) { this->assert_mapped_type(); - auto &tImpl = this->get_impl(); + auto& tImpl = this->get_impl(); tImpl.table.name = move(name); } @@ -647,30 +774,42 @@ namespace sqlite_orm { * any SQLite queries */ template - const std::string &tablename() const { + const std::string& tablename() const { this->assert_mapped_type(); - auto &tImpl = this->get_impl(); + auto& tImpl = this->get_impl(); return tImpl.table.name; } + template + const std::string* column_name(F O::*memberPointer) const { + return this->impl.column_name(memberPointer); + } + protected: template - sync_schema_result sync_table(const storage_impl, Tss...> &tableImpl, sqlite3 *db, bool) { + sync_schema_result schema_status(const storage_impl, Tss...>&, sqlite3*, bool) { + return sync_schema_result::already_in_sync; + } + + template class TTable, class... Tss, class... Cs> + sync_schema_result + schema_status(const storage_impl, Tss...>& tImpl, sqlite3* db, bool preserve) { + return tImpl.schema_status(db, preserve); + } + + template + sync_schema_result sync_table(const storage_impl, Tss...>& tableImpl, sqlite3* db, bool) { auto res = sync_schema_result::already_in_sync; using context_t = serializator_context; context_t context{this->impl}; auto query = serialize(tableImpl.table, context); - auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_void_exec(db, query); return res; } - template + template class TTable, class... Tss, class... Cs> sync_schema_result - sync_table(const storage_impl, Tss...> &tImpl, sqlite3 *db, bool preserve) { + sync_table(const storage_impl, Tss...>& tImpl, sqlite3* db, bool preserve) { auto res = sync_schema_result::already_in_sync; auto schema_stat = tImpl.schema_status(db, preserve); @@ -690,9 +829,9 @@ namespace sqlite_orm { auto dbTableInfo = tImpl.get_table_info(tImpl.table.name, db); // this vector will contain pointers to columns that gotta be added.. - std::vector columnsToAdd; + std::vector columnsToAdd; - tImpl.get_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + tImpl.calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); if(schema_stat == sync_schema_result::old_columns_removed) { @@ -724,6 +863,24 @@ namespace sqlite_orm { return res; } + template + prepared_statement_t prepare_impl(S statement) { + auto con = this->get_connection(); + sqlite3_stmt* stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(statement, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return prepared_statement_t{std::forward(statement), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + public: /** * This is a cute function used to replace migration up/down functionality. @@ -756,7 +913,7 @@ namespace sqlite_orm { auto con = this->get_connection(); std::map result; auto db = con.get(); - this->impl.for_each([&result, db, preserve, this](auto &tableImpl) { + this->impl.for_each([&result, db, preserve, this](auto& tableImpl) { auto res = this->sync_table(tableImpl, db, preserve); result.insert({tableImpl.table.name, res}); }); @@ -772,8 +929,9 @@ namespace sqlite_orm { auto con = this->get_connection(); std::map result; auto db = con.get(); - this->impl.for_each([&result, db, preserve](auto tableImpl) { - result.insert({tableImpl.table.name, tableImpl.schema_status(db, preserve)}); + this->impl.for_each([&result, db, preserve, this](auto& tableImpl) { + auto schemaStatus = this->schema_status(tableImpl, db, preserve); + result.insert({tableImpl.table.name, schemaStatus}); }); return result; } @@ -783,7 +941,7 @@ namespace sqlite_orm { * Note: table can be not mapped to a storage * @return true if table with a given name exists in db, false otherwise. */ - bool table_exists(const std::string &tableName) { + bool table_exists(const std::string& tableName) { auto con = this->get_connection(); return this->impl.table_exists(tableName, con.get()); } @@ -791,305 +949,147 @@ namespace sqlite_orm { template prepared_statement_t> prepare(select_t sel) { sel.highest_level = true; - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(sel, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(sel), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(sel)); } template prepared_statement_t> prepare(get_all_t get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(get_)); } template prepared_statement_t> prepare(get_all_pointer_t get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(get_)); + } + + template + prepared_statement_t> prepare(replace_raw_t ins) { + return prepare_impl>(std::move(ins)); + } + + template + prepared_statement_t> prepare(insert_raw_t ins) { + return prepare_impl>(std::move(ins)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template prepared_statement_t> prepare(get_all_optional_t get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(get_)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template prepared_statement_t, Wargs...>> prepare(update_all_t, Wargs...> upd) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(upd, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(upd), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl, Wargs...>>(std::move(upd)); } template prepared_statement_t> prepare(remove_all_t rem) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(rem, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(rem), stmt, std::move(con)}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(rem)); } template prepared_statement_t> prepare(get_t get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(get_)); } template prepared_statement_t> prepare(get_pointer_t get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(get_)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template prepared_statement_t> prepare(get_optional_t get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(get_)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template prepared_statement_t> prepare(update_t upd) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(upd, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(upd), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(upd)); } template prepared_statement_t> prepare(remove_t rem) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(rem, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(rem), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(rem)); } template prepared_statement_t> prepare(insert_t ins) { using object_type = typename expression_object_type::type; this->assert_mapped_type(); - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(ins, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(ins), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + this->assert_insertable_type(); + return prepare_impl>(std::move(ins)); } template prepared_statement_t> prepare(replace_t rep) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; using object_type = typename expression_object_type::type; this->assert_mapped_type(); - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(rep, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(rep), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(rep)); } - template - prepared_statement_t> prepare(insert_range_t statement) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(statement, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(statement), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + template + prepared_statement_t> prepare(insert_range_t statement) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + this->assert_insertable_type(); + return prepare_impl>(std::move(statement)); } - template - prepared_statement_t> prepare(replace_range_t rep) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(rep, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(rep), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + template + prepared_statement_t> prepare(replace_range_t rep) { + return prepare_impl>(std::move(rep)); } template prepared_statement_t> prepare(insert_explicit ins) { using object_type = typename expression_object_type::type; this->assert_mapped_type(); + return prepare_impl>(std::move(ins)); + } + + template + void execute(const prepared_statement_t>& statement) { auto con = this->get_connection(); - sqlite3_stmt *stmt; auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(ins, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(ins), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + auto stmt = statement.stmt; + sqlite3_reset(stmt); + auto index = 1; + iterate_ast(statement.t.args, [stmt, &index, db](auto& node) { + using node_type = typename std::decay::type; + conditional_binder> binder{stmt, index}; + if(SQLITE_OK != binder(node)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + perform_step(db, stmt); + } + + template + void execute(const prepared_statement_t>& statement) { + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + sqlite3_reset(stmt); + auto index = 1; + iterate_ast(statement.t.args, [stmt, &index, db](auto& node) { + using node_type = typename std::decay::type; + conditional_binder> binder{stmt, index}; + if(SQLITE_OK != binder(node)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + perform_step(db, stmt); } template - int64 execute(const prepared_statement_t> &statement) { + int64 execute(const prepared_statement_t>& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type::type; @@ -1097,40 +1097,37 @@ namespace sqlite_orm { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; - auto &tImpl = this->get_impl(); - auto &o = statement.t.obj; + auto& tImpl = this->get_impl(); + auto& o = statement.t.obj; sqlite3_reset(stmt); - iterate_tuple(statement.t.columns.columns, [&o, &index, &stmt, &tImpl, db](auto &m) { + iterate_tuple(statement.t.columns.columns, [&o, &index, &stmt, &tImpl, db](auto& m) { using column_type = typename std::decay::type; using field_type = typename column_result_t::type; - const field_type *value = tImpl.table.template get_object_field_pointer(o, m); + const field_type* value = tImpl.table.template get_object_field_pointer(o, m); if(SQLITE_OK != statement_binder().bind(stmt, index++, *value)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - return sqlite3_last_insert_rowid(db); - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_step(db, stmt); + return sqlite3_last_insert_rowid(db); } - template - void execute(const prepared_statement_t> &statement) { + template::value || is_replace::value)>::type* = nullptr> + void execute(const prepared_statement_t& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; - using object_type = typename expression_type::object_type; - auto &tImpl = this->get_impl(); + using object_type = typename expression_object_type::type; + auto& tImpl = this->get_impl(); auto index = 1; auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; sqlite3_reset(stmt); - for(auto it = statement.t.range.first; it != statement.t.range.second; ++it) { - auto &o = *it; - tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { + + auto processObject = [&index, &stmt, &tImpl, db](auto& o) { + tImpl.table.for_each_column([&](auto& c) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; if(c.member_pointer) { @@ -1149,180 +1146,120 @@ namespace sqlite_orm { } } }); - } - if(sqlite3_step(stmt) == SQLITE_DONE) { - //.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - - template - void execute(const prepared_statement_t> &statement) { - using statement_type = typename std::decay::type; - using expression_type = typename statement_type::expression_type; - using object_type = typename expression_type::object_type; - auto index = 1; - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto &tImpl = this->get_impl(); - sqlite3_reset(stmt); - for(auto it = statement.t.range.first; it != statement.t.range.second; ++it) { - auto &o = *it; - tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { - if(!c.template has>()) { - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - if(c.member_pointer) { - if(SQLITE_OK != - statement_binder().bind(stmt, index++, o.*c.member_pointer)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder valueHolder{((o).*(c.getter))()}; - if(SQLITE_OK != statement_binder().bind(stmt, index++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - } - }); - } - if(sqlite3_step(stmt) == SQLITE_DONE) { - //.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - - template - void execute(const prepared_statement_t> &statement) { + }; + + static_if{}>( + [&processObject](auto& statement) { + auto& transformer = statement.t.transformer; + std::for_each( /// + statement.t.range.first, + statement.t.range.second, + [&processObject, &transformer](auto& object) { + auto& realObject = transformer(object); + processObject(realObject); + }); + }, + [&processObject](auto& statement) { + auto& o = get_object(statement.t); + processObject(o); + })(statement); + perform_step(db, stmt); + } + + template::value || is_insert::value)>::type* = nullptr> + int64 execute(const prepared_statement_t& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type::type; - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; auto index = 1; - auto &o = get_object(statement.t); - auto &tImpl = this->get_impl(); - sqlite3_reset(stmt); - tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - if(c.member_pointer) { - if(SQLITE_OK != statement_binder().bind(stmt, index++, o.*c.member_pointer)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder valueHolder{((o).*(c.getter))()}; - if(SQLITE_OK != statement_binder().bind(stmt, index++, valueHolder.value)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - //.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - - template - int64 execute(const prepared_statement_t> &statement) { - using statement_type = typename std::decay::type; - using expression_type = typename statement_type::expression_type; - using object_type = typename expression_object_type::type; - int64 res = 0; auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; - auto index = 1; - auto &tImpl = this->get_impl(); - auto &o = get_object(statement.t); + auto& tImpl = this->get_impl(); auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); sqlite3_reset(stmt); - tImpl.table.for_each_column([&o, &index, &stmt, &tImpl, &compositeKeyColumnNames, db](auto &c) { - if(tImpl.table._without_rowid || !c.template has>()) { - auto it = std::find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); - if(it == compositeKeyColumnNames.end()) { - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - if(c.member_pointer) { - if(SQLITE_OK != - statement_binder().bind(stmt, index++, o.*c.member_pointer)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder valueHolder{((o).*(c.getter))()}; - if(SQLITE_OK != statement_binder().bind(stmt, index++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + auto processObject = [&index, &stmt, &tImpl, &compositeKeyColumnNames, db](auto& o) { + tImpl.table.for_each_column([&](auto& c) { + using table_type = typename std::decay::type; + if(table_type::is_without_rowid || !c.template has>()) { + auto it = std::find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); + if(it == compositeKeyColumnNames.end()) { + using column_type = typename std::decay::type; + using field_type = typename column_type::field_type; + if(c.member_pointer) { + if(SQLITE_OK != + statement_binder().bind(stmt, index++, o.*c.member_pointer)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + using getter_type = typename column_type::getter_type; + field_value_holder valueHolder{((o).*(c.getter))()}; + if(SQLITE_OK != + statement_binder().bind(stmt, index++, valueHolder.value)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } } } } - } - }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - res = sqlite3_last_insert_rowid(db); - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - return res; + }); + }; + + static_if{}>( + [&processObject](auto& statement) { + auto& transformer = statement.t.transformer; + std::for_each( /// + statement.t.range.first, + statement.t.range.second, + [&processObject, &transformer](auto& object) { + auto& realObject = transformer(object); + processObject(realObject); + }); + }, + [&processObject](auto& statement) { + auto& o = get_object(statement.t); + processObject(o); + })(statement); + + perform_step(db, stmt); + return sqlite3_last_insert_rowid(db); } template - void execute(const prepared_statement_t> &statement) { + void execute(const prepared_statement_t>& statement) { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { using field_type = typename std::decay::type; if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_step(db, stmt); } template - void execute(const prepared_statement_t> &statement) { + void execute(const prepared_statement_t>& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type::type; auto con = this->get_connection(); auto db = con.get(); - auto &tImpl = this->get_impl(); + auto& tImpl = this->get_impl(); auto stmt = statement.stmt; auto index = 1; - auto &o = get_object(statement.t); + auto& o = get_object(statement.t); sqlite3_reset(stmt); - tImpl.table.for_each_column([&o, stmt, &index, db](auto &c) { - if(!c.template has>()) { + tImpl.table.for_each_column([&o, stmt, &index, db](auto& c) { + if(!c.template has>()) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; if(c.member_pointer) { @@ -1343,8 +1280,8 @@ namespace sqlite_orm { } } }); - tImpl.table.for_each_column([&o, stmt, &index, db](auto &c) { - if(c.template has>()) { + tImpl.table.for_each_column([&o, stmt, &index, db](auto& c) { + if(c.template has>()) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; if(c.member_pointer) { @@ -1364,23 +1301,18 @@ namespace sqlite_orm { } } }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_step(db, stmt); } template - std::unique_ptr execute(const prepared_statement_t> &statement) { - auto &tImpl = this->get_impl(); + std::unique_ptr execute(const prepared_statement_t>& statement) { + auto& tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { using field_type = typename std::decay::type; if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), @@ -1407,14 +1339,14 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - std::optional execute(const prepared_statement_t> &statement) { - auto &tImpl = this->get_impl(); + std::optional execute(const prepared_statement_t>& statement) { + auto& tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { using field_type = typename std::decay::type; if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), @@ -1441,14 +1373,14 @@ namespace sqlite_orm { #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - T execute(const prepared_statement_t> &statement) { - auto &tImpl = this->get_impl(); + T execute(const prepared_statement_t>& statement) { + auto& tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { using field_type = typename std::decay::type; if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), @@ -1474,13 +1406,13 @@ namespace sqlite_orm { } template - void execute(const prepared_statement_t> &statement) { + void execute(const prepared_statement_t>& statement) { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t.conditions, [stmt, &index, db](auto &node) { + iterate_ast(statement.t.conditions, [stmt, &index, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -1488,23 +1420,18 @@ namespace sqlite_orm { sqlite3_errmsg(db)); } }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_step(db, stmt); } template - void execute(const prepared_statement_t, Wargs...>> &statement) { + void execute(const prepared_statement_t, Wargs...>>& statement) { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_tuple(statement.t.set.assigns, [&index, stmt, db](auto &setArg) { - iterate_ast(setArg, [&index, stmt, db](auto &node) { + iterate_tuple(statement.t.set.assigns, [&index, stmt, db](auto& setArg) { + iterate_ast(setArg, [&index, stmt, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -1513,7 +1440,7 @@ namespace sqlite_orm { } }); }); - iterate_ast(statement.t.conditions, [stmt, &index, db](auto &node) { + iterate_ast(statement.t.conditions, [stmt, &index, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -1521,22 +1448,17 @@ namespace sqlite_orm { sqlite3_errmsg(db)); } }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_step(db, stmt); } template::type> - std::vector execute(const prepared_statement_t> &statement) { + std::vector execute(const prepared_statement_t>& statement) { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto &node) { + iterate_ast(statement.t, [stmt, &index, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -1570,14 +1492,14 @@ namespace sqlite_orm { } template - R execute(const prepared_statement_t> &statement) { - auto &tImpl = this->get_impl(); + R execute(const prepared_statement_t>& statement) { + auto& tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto &node) { + iterate_ast(statement.t, [stmt, &index, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -1608,14 +1530,14 @@ namespace sqlite_orm { } template - R execute(const prepared_statement_t> &statement) { - auto &tImpl = this->get_impl(); + R execute(const prepared_statement_t>& statement) { + auto& tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto &node) { + iterate_ast(statement.t, [stmt, &index, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -1647,14 +1569,14 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - R execute(const prepared_statement_t> &statement) { - auto &tImpl = this->get_impl(); + R execute(const prepared_statement_t>& statement) { + auto& tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto &node) { + iterate_ast(statement.t, [stmt, &index, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -1684,6 +1606,23 @@ namespace sqlite_orm { return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + /*template + bool has_dependent_rows(const O& object) { + auto res = false; + using TupleWithForeignKeyTypes = typename storage_traits::storage_fk_references::type; + iterate_tuple([&res, this](auto *itemPointer){ + using ConstItem = typename std::remove_pointer::type; + using Item = typename std::decay::type; + if(!res) { + auto rows = this->select(count()); + if (!rows.empty()) { + res = rows[0]; + } + } + }); + return res; + }*/ }; // struct storage_t template @@ -1694,8 +1633,8 @@ namespace sqlite_orm { } template - internal::storage_t make_storage(const std::string &filename, Ts... tables) { - return {filename, internal::storage_impl(tables...)}; + internal::storage_t make_storage(const std::string& filename, Ts... tables) { + return {filename, internal::storage_impl(std::forward(tables)...)}; } /** diff --git a/dev/storage_base.h b/dev/storage_base.h index 21dee1d52..3c676dbd3 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -17,19 +17,23 @@ #include "transaction_guard.h" #include "statement_finalizer.h" #include "type_printer.h" -#include "tuple_helper.h" +#include "tuple_helper/tuple_helper.h" #include "row_extractor.h" +#include "util.h" #include "connection_holder.h" #include "backup.h" +#include "function.h" +#include "values_to_tuple.h" +#include "arg_values.h" namespace sqlite_orm { namespace internal { struct storage_base { - using collating_function = std::function; + using collating_function = std::function; - std::function on_open; + std::function on_open; pragma_t pragma; limit_accesor limit; @@ -40,50 +44,20 @@ namespace sqlite_orm { return {this->get_connection(), move(commitFunc), move(rollbackFunc)}; } - void drop_index(const std::string &indexName) { - auto con = this->get_connection(); - auto db = con.get(); + void drop_index(const std::string& indexName) { std::stringstream ss; ss << "DROP INDEX '" << indexName + "'"; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_void_exec(get_connection().get(), ss.str()); } void vacuum() { - auto con = this->get_connection(); - auto db = con.get(); - std::string query = "VACUUM"; - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_void_exec(get_connection().get(), "VACUUM"); } /** * Drops table with given name. */ - void drop_table(const std::string &tableName) { + void drop_table(const std::string& tableName) { auto con = this->get_connection(); this->drop_table_internal(tableName, con.get()); } @@ -91,11 +65,10 @@ namespace sqlite_orm { /** * Rename table named `from` to `to`. */ - void rename_table(const std::string &from, const std::string &to) { - auto con = this->get_connection(); + void rename_table(const std::string& from, const std::string& to) { std::stringstream ss; ss << "ALTER TABLE '" << from << "' RENAME TO '" << to << "'"; - this->perform_query_without_result(ss.str(), con.get()); + perform_void_exec(get_connection().get(), ss.str()); } /** @@ -131,13 +104,13 @@ namespace sqlite_orm { return sqlite3_libversion(); } - bool transaction(const std::function &f) { - this->begin_transaction(); + bool transaction(const std::function& f) { + auto guard = transaction_guard(); auto shouldCommit = f(); if(shouldCommit) { - this->commit(); + guard.commit(); } else { - this->rollback(); + guard.rollback(); } return shouldCommit; } @@ -174,8 +147,8 @@ namespace sqlite_orm { int res = sqlite3_exec( db, sql.c_str(), - [](void *data, int argc, char **argv, char * * /*columnName*/) -> int { - auto &tableNames_ = *(data_t *)data; + [](void* data, int argc, char** argv, char** /*columnName*/) -> int { + auto& tableNames_ = *(data_t*)data; for(int i = 0; i < argc; i++) { if(argv[i]) { tableNames_.push_back(argv[i]); @@ -201,9 +174,166 @@ namespace sqlite_orm { } } - void create_collation(const std::string &name, collating_function f) { - collating_function *functionPointer = nullptr; - if(f) { + /** + * Call this to create user defined scalar function. Can be called at any time no matter connection is opened or no. + * T - function class. T must have operator() overload and static name function like this: + * ``` + * struct SqrtFunction { + * + * double operator()(double arg) const { + * return std::sqrt(arg); + * } + * + * static const char *name() { + * return "SQRT"; + * } + * }; + * ``` + */ + template + void create_scalar_function() { + static_assert(is_scalar_function::value, "F cannot be a scalar function"); + + std::stringstream ss; + ss << F::name(); + auto name = ss.str(); + using args_tuple = typename callable_arguments::args_tuple; + using return_type = typename callable_arguments::return_type; + auto argsCount = int(std::tuple_size::value); + if(std::is_same>::value) { + argsCount = -1; + } + this->scalarFunctions.emplace_back(new scalar_function_t{ + move(name), + argsCount, + []() -> int* { + return (int*)(new F()); + }, + /* call = */ + [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { + auto& functionPointer = *static_cast(functionVoidPointer); + args_tuple argsTuple; + using tuple_size = std::tuple_size; + values_to_tuple().extract(values, argsTuple, argsCount); + auto result = call(functionPointer, std::move(argsTuple)); + statement_binder().result(context, result); + }, + delete_function_callback, + }); + + if(this->connection->retain_count() > 0) { + auto db = this->connection->get(); + try_to_create_function(db, static_cast(*this->scalarFunctions.back())); + } + } + + /** + * Call this to create user defined aggregate function. Can be called at any time no matter connection is opened or no. + * T - function class. T must have step member function, fin member function and static name function like this: + * ``` + * struct MeanFunction { + * double total = 0; + * int count = 0; + * + * void step(double value) { + * total += value; + * ++count; + * } + * + * int fin() const { + * return total / count; + * } + * + * static std::string name() { + * return "MEAN"; + * } + * }; + * ``` + */ + template + void create_aggregate_function() { + static_assert(is_aggregate_function::value, "F cannot be an aggregate function"); + + std::stringstream ss; + ss << F::name(); + auto name = ss.str(); + using args_tuple = typename callable_arguments::args_tuple; + using return_type = typename callable_arguments::return_type; + auto argsCount = int(std::tuple_size::value); + if(std::is_same>::value) { + argsCount = -1; + } + this->aggregateFunctions.emplace_back(new aggregate_function_t{ + move(name), + argsCount, + /* create = */ + []() -> int* { + return (int*)(new F()); + }, + /* step = */ + [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { + auto& functionPointer = *static_cast(functionVoidPointer); + args_tuple argsTuple; + using tuple_size = std::tuple_size; + values_to_tuple().extract(values, argsTuple, argsCount); + call(functionPointer, &F::step, move(argsTuple)); + }, + /* finalCall = */ + [](sqlite3_context* context, void* functionVoidPointer) { + auto& functionPointer = *static_cast(functionVoidPointer); + auto result = functionPointer.fin(); + statement_binder().result(context, result); + }, + delete_function_callback, + }); + + if(this->connection->retain_count() > 0) { + auto db = this->connection->get(); + try_to_create_function(db, static_cast(*this->aggregateFunctions.back())); + } + } + + /** + * Use it to delete scalar function you created before. Can be called at any time no matter connection is open or no. + */ + template + void delete_scalar_function() { + static_assert(is_scalar_function::value, "F cannot be a scalar function"); + std::stringstream ss; + ss << F::name(); + auto name = ss.str(); + this->delete_function_impl(name, this->scalarFunctions); + } + + /** + * Use it to delete aggregate function you created before. Can be called at any time no matter connection is open or no. + */ + template + void delete_aggregate_function() { + static_assert(is_aggregate_function::value, "F cannot be an aggregate function"); + std::stringstream ss; + ss << F::name(); + auto name = ss.str(); + this->delete_function_impl(name, this->aggregateFunctions); + } + + template + void create_collation() { + collating_function func = [](int leftLength, const void* lhs, int rightLength, const void* rhs) { + C collatingObject; + return collatingObject(leftLength, lhs, rightLength, rhs); + }; + std::stringstream ss; + ss << C::name(); + auto name = ss.str(); + ss.flush(); + this->create_collation(name, move(func)); + } + + void create_collation(const std::string& name, collating_function f) { + collating_function* functionPointer = nullptr; + const auto functionExists = bool(f); + if(functionExists) { functionPointer = &(collatingFunctions[name] = std::move(f)); } else { collatingFunctions.erase(name); @@ -212,29 +342,39 @@ namespace sqlite_orm { // create collations if db is open if(this->connection->retain_count() > 0) { auto db = this->connection->get(); - if(sqlite3_create_collation(db, - name.c_str(), - SQLITE_UTF8, - functionPointer, - f ? collate_callback : nullptr) != SQLITE_OK) { + auto resultCode = sqlite3_create_collation(db, + name.c_str(), + SQLITE_UTF8, + functionPointer, + functionExists ? collate_callback : nullptr); + if(resultCode != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } } + template + void delete_collation() { + std::stringstream ss; + ss << C::name(); + auto name = ss.str(); + ss.flush(); + this->create_collation(name, {}); + } + void begin_transaction() { this->connection->retain(); if(1 == this->connection->retain_count()) { this->on_open_internal(this->connection->get()); } auto db = this->connection->get(); - this->begin_transaction(db); + perform_void_exec(db, "BEGIN TRANSACTION"); } void commit() { auto db = this->connection->get(); - this->commit(db); + perform_void_exec(db, "COMMIT"); this->connection->release(); if(this->connection->retain_count() < 0) { throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction)); @@ -243,54 +383,54 @@ namespace sqlite_orm { void rollback() { auto db = this->connection->get(); - this->rollback(db); + perform_void_exec(db, "ROLLBACK"); this->connection->release(); if(this->connection->retain_count() < 0) { throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction)); } } - void backup_to(const std::string &filename) { + void backup_to(const std::string& filename) { auto backup = this->make_backup_to(filename); backup.step(-1); } - void backup_to(storage_base &other) { + void backup_to(storage_base& other) { auto backup = this->make_backup_to(other); backup.step(-1); } - void backup_from(const std::string &filename) { + void backup_from(const std::string& filename) { auto backup = this->make_backup_from(filename); backup.step(-1); } - void backup_from(storage_base &other) { + void backup_from(storage_base& other) { auto backup = this->make_backup_from(other); backup.step(-1); } - backup_t make_backup_to(const std::string &filename) { + backup_t make_backup_to(const std::string& filename) { auto holder = std::make_unique(filename); connection_ref conRef{*holder}; return {conRef, "main", this->get_connection(), "main", move(holder)}; } - backup_t make_backup_to(storage_base &other) { + backup_t make_backup_to(storage_base& other) { return {other.get_connection(), "main", this->get_connection(), "main", {}}; } - backup_t make_backup_from(const std::string &filename) { + backup_t make_backup_from(const std::string& filename) { auto holder = std::make_unique(filename); connection_ref conRef{*holder}; return {this->get_connection(), "main", conRef, "main", move(holder)}; } - backup_t make_backup_from(storage_base &other) { + backup_t make_backup_from(storage_base& other) { return {this->get_connection(), "main", other.get_connection(), "main", {}}; } - const std::string &filename() const { + const std::string& filename() const { return this->connection->filename; } @@ -316,7 +456,7 @@ namespace sqlite_orm { } protected: - storage_base(const std::string &filename_, int foreignKeysCount) : + storage_base(const std::string& filename_, int foreignKeysCount) : pragma(std::bind(&storage_base::get_connection, this)), limit(std::bind(&storage_base::get_connection, this)), inMemory(filename_.empty() || filename_ == ":memory:"), @@ -327,7 +467,7 @@ namespace sqlite_orm { } } - storage_base(const storage_base &other) : + storage_base(const storage_base& other) : on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this)), limit(std::bind(&storage_base::get_connection, this)), inMemory(other.inMemory), connection(std::make_unique(other.connection->filename)), @@ -347,13 +487,6 @@ namespace sqlite_orm { } } - const bool inMemory; - bool isOpenedForever = false; - std::unique_ptr connection; - std::map collatingFunctions; - const int cachedForeignKeysCount; - std::function _busy_handler; - connection_ref get_connection() { connection_ref res{*this->connection}; if(1 == this->connection->retain_count()) { @@ -364,25 +497,20 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3006019 - void foreign_keys(sqlite3 *db, bool value) { + void foreign_keys(sqlite3* db, bool value) { std::stringstream ss; ss << "PRAGMA foreign_keys = " << value; - auto query = ss.str(); - auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_void_exec(db, ss.str()); } - bool foreign_keys(sqlite3 *db) { + bool foreign_keys(sqlite3* db) { std::string query = "PRAGMA foreign_keys"; auto result = false; auto rc = sqlite3_exec( db, query.c_str(), - [](void *data, int argc, char **argv, char **) -> int { - auto &res = *(bool *)data; + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(bool*)data; if(argc) { res = row_extractor().extract(argv[0]); } @@ -398,7 +526,7 @@ namespace sqlite_orm { } #endif - void on_open_internal(sqlite3 *db) { + void on_open_internal(sqlite3* db) { #if SQLITE_VERSION_NUMBER >= 3006019 if(this->cachedForeignKeysCount) { @@ -413,15 +541,16 @@ namespace sqlite_orm { this->pragma.set_pragma("journal_mode", static_cast(this->pragma._journal_mode), db); } - for(auto &p: this->collatingFunctions) { - if(sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback) != - SQLITE_OK) { + for(auto& p: this->collatingFunctions) { + auto resultCode = + sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback); + if(resultCode != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } - for(auto &p: this->limit.limits) { + for(auto& p: this->limit.limits) { sqlite3_limit(db, p.first, p.second); } @@ -429,69 +558,120 @@ namespace sqlite_orm { sqlite3_busy_handler(this->connection->get(), busy_handler_callback, this); } + for(auto& functionPointer: this->scalarFunctions) { + try_to_create_function(db, static_cast(*functionPointer)); + } + + for(auto& functionPointer: this->aggregateFunctions) { + try_to_create_function(db, static_cast(*functionPointer)); + } + if(this->on_open) { this->on_open(db); } } - void begin_transaction(sqlite3 *db) { - std::stringstream ss; - ss << "BEGIN TRANSACTION"; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + void delete_function_impl(const std::string& name, + std::vector>& functionsVector) const { + auto it = find_if(functionsVector.begin(), functionsVector.end(), [&name](auto& functionPointer) { + return functionPointer->name == name; + }); + if(it != functionsVector.end()) { + functionsVector.erase(it); + it = functionsVector.end(); + + if(this->connection->retain_count() > 0) { + auto db = this->connection->get(); + auto resultCode = sqlite3_create_function_v2(db, + name.c_str(), + 0, + SQLITE_UTF8, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr); + if(resultCode != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } } } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + throw std::system_error(std::make_error_code(orm_error_code::function_not_found)); } } - void commit(sqlite3 *db) { - std::stringstream ss; - ss << "COMMIT"; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { + void try_to_create_function(sqlite3* db, scalar_function_t& function) { + auto resultCode = sqlite3_create_function_v2(db, + function.name.c_str(), + function.argumentsCount, + SQLITE_UTF8, + &function, + scalar_function_callback, + nullptr, + nullptr, + nullptr); + if(resultCode != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } - void rollback(sqlite3 *db) { - std::stringstream ss; - ss << "ROLLBACK"; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + void try_to_create_function(sqlite3* db, aggregate_function_t& function) { + auto resultCode = sqlite3_create_function(db, + function.name.c_str(), + function.argumentsCount, + SQLITE_UTF8, + &function, + nullptr, + aggregate_function_step_callback, + aggregate_function_final_callback); + if(resultCode != SQLITE_OK) { + throw std::system_error(std::error_code(resultCode, get_sqlite_error_category()), + sqlite3_errstr(resultCode)); } } - std::string current_timestamp(sqlite3 *db) { + static void + aggregate_function_step_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { + auto functionVoidPointer = sqlite3_user_data(context); + auto functionPointer = static_cast(functionVoidPointer); + auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); + auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); + if(*aggregateContextIntPointer == nullptr) { + *aggregateContextIntPointer = functionPointer->create(); + } + functionPointer->step(context, *aggregateContextIntPointer, argsCount, values); + } + + static void aggregate_function_final_callback(sqlite3_context* context) { + auto functionVoidPointer = sqlite3_user_data(context); + auto functionPointer = static_cast(functionVoidPointer); + auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); + auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); + functionPointer->finalCall(context, *aggregateContextIntPointer); + functionPointer->destroy(*aggregateContextIntPointer); + } + + static void scalar_function_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { + auto functionVoidPointer = sqlite3_user_data(context); + auto functionPointer = static_cast(functionVoidPointer); + std::unique_ptr callablePointer(functionPointer->create(), + functionPointer->destroy); + if(functionPointer->argumentsCount != -1 && functionPointer->argumentsCount != argsCount) { + throw std::system_error(std::make_error_code(orm_error_code::arguments_count_does_not_match)); + } + functionPointer->run(context, functionPointer, argsCount, values); + } + + template + static void delete_function_callback(int* pointer) { + auto voidPointer = static_cast(pointer); + auto fPointer = static_cast(voidPointer); + delete fPointer; + } + + std::string current_timestamp(sqlite3* db) { std::string result; std::stringstream ss; ss << "SELECT CURRENT_TIMESTAMP"; @@ -499,8 +679,8 @@ namespace sqlite_orm { auto rc = sqlite3_exec( db, query.c_str(), - [](void *data, int argc, char **argv, char **) -> int { - auto &res = *(std::string *)data; + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(std::string*)data; if(argc) { if(argv[0]) { res = row_extractor().extract(argv[0]); @@ -517,35 +697,19 @@ namespace sqlite_orm { return result; } - void drop_table_internal(const std::string &tableName, sqlite3 *db) { + void drop_table_internal(const std::string& tableName, sqlite3* db) { std::stringstream ss; ss << "DROP TABLE '" << tableName + "'"; - this->perform_query_without_result(ss.str(), db); + perform_void_exec(db, ss.str()); } - void perform_query_without_result(const std::string &query, sqlite3 *db) { - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - - static int collate_callback(void *arg, int leftLen, const void *lhs, int rightLen, const void *rhs) { - auto &f = *(collating_function *)arg; + static int collate_callback(void* arg, int leftLen, const void* lhs, int rightLen, const void* rhs) { + auto& f = *(collating_function*)arg; return f(leftLen, lhs, rightLen, rhs); } - static int busy_handler_callback(void *selfPointer, int triesCount) { - auto &storage = *static_cast(selfPointer); + static int busy_handler_callback(void* selfPointer, int triesCount) { + auto& storage = *static_cast(selfPointer); if(storage._busy_handler) { return storage._busy_handler(triesCount); } else { @@ -555,13 +719,22 @@ namespace sqlite_orm { // returns foreign keys count in storage definition template - static int foreign_keys_count(T &storageImpl) { + static int foreign_keys_count(T& storageImpl) { auto res = 0; - storageImpl.for_each([&res](auto &impl) { + storageImpl.for_each([&res](auto& impl) { res += impl.foreign_keys_count(); }); return res; } + + const bool inMemory; + bool isOpenedForever = false; + std::unique_ptr connection; + std::map collatingFunctions; + const int cachedForeignKeysCount; + std::function _busy_handler; + std::vector> scalarFunctions; + std::vector> aggregateFunctions; }; } } diff --git a/dev/storage_impl.h b/dev/storage_impl.h index 37aca6e25..7d6ff93a9 100644 --- a/dev/storage_impl.h +++ b/dev/storage_impl.h @@ -5,7 +5,7 @@ #include // std::nullptr_t #include // std::system_error, std::error_code #include // std::stringstream -#include // std::atoi +#include // std::atoi #include // std::forward, std::enable_if, std::is_same, std::remove_reference, std::false_type, std::true_type #include // std::pair, std::make_pair #include // std::vector @@ -15,6 +15,7 @@ #include "error_code.h" #include "statement_finalizer.h" #include "row_extractor.h" +#include "util.h" #include "constraints.h" #include "select_constraints.h" #include "field_printer.h" @@ -28,7 +29,7 @@ namespace sqlite_orm { struct storage_impl_base { - bool table_exists(const std::string &tableName, sqlite3 *db) const { + bool table_exists(const std::string& tableName, sqlite3* db) const { auto result = false; std::stringstream ss; ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = '" @@ -38,8 +39,8 @@ namespace sqlite_orm { auto rc = sqlite3_exec( db, query.c_str(), - [](void *data, int argc, char **argv, char * * /*azColName*/) -> int { - auto &res = *(bool *)data; + [](void* data, int argc, char** argv, char** /*azColName*/) -> int { + auto& res = *(bool*)data; if(argc) { res = !!std::atoi(argv[0]); } @@ -54,28 +55,15 @@ namespace sqlite_orm { return result; } - void rename_table(sqlite3 *db, const std::string &oldName, const std::string &newName) const { + void rename_table(sqlite3* db, const std::string& oldName, const std::string& newName) const { std::stringstream ss; ss << "ALTER TABLE " << oldName << " RENAME TO " << newName; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_void_exec(db, ss.str()); } - static bool get_remove_add_columns(std::vector &columnsToAdd, - std::vector &storageTableInfo, - std::vector &dbTableInfo) { + static bool calculate_remove_add_columns(std::vector& columnsToAdd, + std::vector& storageTableInfo, + std::vector& dbTableInfo) { bool notEqual = false; // iterate through storage columns @@ -83,15 +71,15 @@ namespace sqlite_orm { ++storageColumnInfoIndex) { // get storage's column info - auto &storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; - auto &columnName = storageColumnInfo.name; + auto& storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; + auto& columnName = storageColumnInfo.name; // search for a column in db eith the same name - auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto &ti) { + auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { return ti.name == columnName; }); if(dbColumnInfoIt != dbTableInfo.end()) { - auto &dbColumnInfo = *dbColumnInfoIt; + auto& dbColumnInfo = *dbColumnInfoIt; auto columnsAreEqual = dbColumnInfo.name == storageColumnInfo.name && dbColumnInfo.notnull == storageColumnInfo.notnull && @@ -112,14 +100,14 @@ namespace sqlite_orm { return notEqual; } - std::vector get_table_info(const std::string &tableName, sqlite3 *db) const { + std::vector get_table_info(const std::string& tableName, sqlite3* db) const { std::vector result; auto query = "PRAGMA table_info('" + tableName + "')"; auto rc = sqlite3_exec( db, query.c_str(), - [](void *data, int argc, char **argv, char **) -> int { - auto &res = *(std::vector *)data; + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(std::vector*)data; if(argc) { auto index = 0; auto cid = std::atoi(argv[index++]); @@ -129,7 +117,7 @@ namespace sqlite_orm { std::string dflt_value = argv[index] ? argv[index] : ""; index++; auto pk = std::atoi(argv[index++]); - res.push_back(table_info{cid, name, type, notnull, dflt_value, pk}); + res.push_back(table_info(cid, name, type, notnull, dflt_value, pk)); } return 0; }, @@ -159,7 +147,7 @@ namespace sqlite_orm { table_type table; template - void for_each(const L &l) { + void for_each(const L& l) { this->super::for_each(l); l(*this); } @@ -171,7 +159,7 @@ namespace sqlite_orm { */ int foreign_keys_count() { auto res = 0; - iterate_tuple(this->table.columns, [&res](auto &c) { + iterate_tuple(this->table.columns, [&res](auto& c) { if(internal::is_foreign_key::type>::value) { ++res; } @@ -187,7 +175,7 @@ namespace sqlite_orm { * `column_name` has SFINAE check for type equality but `column_name_simple` has not. */ template - std::string column_name_simple(F O::*m) const { + const std::string* column_name_simple(F O::*m) const { return this->table.find_column_name(m); } @@ -196,8 +184,8 @@ namespace sqlite_orm { * skip inequal type O. */ template - std::string column_name(F O::*m, - typename std::enable_if::value>::type * = nullptr) const { + const std::string* column_name(F O::*m, + typename std::enable_if::value>::type* = nullptr) const { return this->table.find_column_name(m); } @@ -205,50 +193,51 @@ namespace sqlite_orm { * Opposite version of function defined above. Just calls same function in superclass. */ template - std::string column_name(F O::*m, - typename std::enable_if::value>::type * = nullptr) const { + const std::string* + column_name(F O::*m, typename std::enable_if::value>::type* = nullptr) const { return this->super::column_name(m); } template - std::string column_name(const column_pointer &c, - typename std::enable_if::value>::type * = nullptr) const { + const std::string* column_name(const column_pointer& c, + typename std::enable_if::value>::type* = nullptr) const { return this->column_name_simple(c.field); } template - std::string column_name(const column_pointer &c, - typename std::enable_if::value>::type * = nullptr) const { + const std::string* + column_name(const column_pointer& c, + typename std::enable_if::value>::type* = nullptr) const { return this->super::column_name(c); } template - const auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { + const auto& get_impl(typename std::enable_if::value>::type* = nullptr) const { return *this; } template - const auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { + const auto& get_impl(typename std::enable_if::value>::type* = nullptr) const { return this->super::template get_impl(); } template - auto &get_impl(typename std::enable_if::value>::type * = nullptr) { + auto& get_impl(typename std::enable_if::value>::type* = nullptr) { return *this; } template - auto &get_impl(typename std::enable_if::value>::type * = nullptr) { + auto& get_impl(typename std::enable_if::value>::type* = nullptr) { return this->super::template get_impl(); } template - const auto *find_table(typename std::enable_if::value>::type * = nullptr) const { + const auto* find_table(typename std::enable_if::value>::type* = nullptr) const { return &this->table; } template - const auto *find_table(typename std::enable_if::value>::type * = nullptr) const { + const auto* find_table(typename std::enable_if::value>::type* = nullptr) const { return this->super::template find_table(); } @@ -261,34 +250,20 @@ namespace sqlite_orm { } } - void add_column(const table_info &ti, sqlite3 *db) const { + void add_column(const table_info& ti, sqlite3* db) const { std::stringstream ss; - ss << "ALTER TABLE " << this->table.name << " ADD COLUMN " << ti.name << " "; - ss << ti.type << " "; + ss << "ALTER TABLE " << this->table.name << " ADD COLUMN " << ti.name; + ss << " " << ti.type; if(ti.pk) { - ss << "PRIMARY KEY "; + ss << " PRIMARY KEY"; } if(ti.notnull) { - ss << "NOT NULL "; + ss << " NOT NULL"; } if(ti.dflt_value.length()) { - ss << "DEFAULT " << ti.dflt_value << " "; - } - auto query = ss.str(); - sqlite3_stmt *stmt; - auto prepareResult = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); - if(prepareResult == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - //.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + ss << " DEFAULT " << ti.dflt_value; } + perform_void_exec(db, ss.str()); } /** @@ -296,13 +271,11 @@ namespace sqlite_orm { * Performs CREATE TABLE %name% AS SELECT %this->table.columns_names()% FROM &this->table.name%; */ void - copy_table(sqlite3 *db, const std::string &name, const std::vector &columnsToIgnore) const { - std::ignore = columnsToIgnore; - + copy_table(sqlite3* db, const std::string& name, const std::vector& columnsToIgnore) const { std::stringstream ss; std::vector columnNames; - this->table.for_each_column([&columnNames, &columnsToIgnore](auto &c) { - auto &columnName = c.name; + this->table.for_each_column([&columnNames, &columnsToIgnore](auto& c) { + auto& columnName = c.name; auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), columnsToIgnore.end(), [&columnName](auto tableInfoPointer) { @@ -326,28 +299,14 @@ namespace sqlite_orm { for(size_t i = 0; i < columnNamesCount; ++i) { ss << columnNames[i]; if(i < columnNamesCount - 1) { - ss << ","; - } - ss << " "; - } - ss << "FROM '" << this->table.name << "' "; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - //.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + ss << ", "; } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); } + ss << " FROM '" << this->table.name << "' "; + perform_void_exec(db, ss.str()); } - sync_schema_result schema_status(sqlite3 *db, bool preserve) const { + sync_schema_result schema_status(sqlite3* db, bool preserve) const { auto res = sync_schema_result::already_in_sync; @@ -362,9 +321,9 @@ namespace sqlite_orm { auto dbTableInfo = this->get_table_info(this->table.name, db); // this vector will contain pointers to columns that gotta be added.. - std::vector columnsToAdd; + std::vector columnsToAdd; - if(this->get_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { + if(this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { gottaCreateTable = true; } @@ -423,14 +382,14 @@ namespace sqlite_orm { } template - void for_each(const L &) {} + void for_each(const L&) {} int foreign_keys_count() { return 0; } template - const void *find_table() const { + const void* find_table() const { return nullptr; } }; diff --git a/dev/storage_traits.h b/dev/storage_traits.h index 8d572af63..d10213761 100644 --- a/dev/storage_traits.h +++ b/dev/storage_traits.h @@ -11,6 +11,12 @@ namespace sqlite_orm { template struct table_t; + template + struct foreign_key_t; + + template + struct table_without_rowid_t; + namespace storage_traits { /** @@ -92,7 +98,21 @@ namespace sqlite_orm { */ template struct table_types> { - using type = std::tuple; + using args_tuple = std::tuple; + using columns_tuple = typename tuple_filter::type; + + using type = typename tuple_transformer::type; + }; + + /** + * type is std::tuple of field types of mapped colums. + */ + template + struct table_types> { + using args_tuple = std::tuple; + using columns_tuple = typename tuple_filter::type; + + using type = typename tuple_transformer::type; }; /** @@ -133,6 +153,171 @@ namespace sqlite_orm { typename std::enable_if::value>::type> : storage_mapped_columns_impl {}; + /** + * C is any column type: column_t or constraint type + * O - object type references in FOREIGN KEY + */ + template + struct column_foreign_keys_count : std::integral_constant {}; + + template + struct column_foreign_keys_count, O> { + using target_type = typename foreign_key_t::target_type; + + static constexpr const int value = std::is_same::value ? 1 : 0; + }; + + /** + * O - object type references in FOREIGN KEY + * Cs - column types which are stored in table_t::columns_type + */ + template + struct table_foreign_keys_count_impl; + + template + struct table_foreign_keys_count_impl { + static constexpr const int value = 0; + }; + + template + struct table_foreign_keys_count_impl { + static constexpr const int value = + column_foreign_keys_count::value + table_foreign_keys_count_impl::value; + }; + + /** + * T is table_t type + * O is object type which is the reference target (e.g. foreign_key(&Visit::userId).references(&User::id) has O = User) + */ + template + struct table_foreign_keys_count; + + template + struct table_foreign_keys_count, O> { + using table_type = table_t; + + static constexpr const int value = table_foreign_keys_count_impl::value; + }; + + /** + * S - storage class + * O - type mapped to S + */ + template + struct storage_foreign_keys_count_impl; + + template + struct storage_foreign_keys_count_impl, O> : std::integral_constant {}; + + template + struct storage_foreign_keys_count_impl, O> { + static constexpr const int value = table_foreign_keys_count::value + + storage_foreign_keys_count_impl, O>::value; + }; + + /** + * S - storage class + * O - type mapped to S + * This class tells how many types mapped to S have foreign keys to O + */ + template + struct storage_foreign_keys_count { + using impl_type = typename S::impl_type; + + static constexpr const int value = storage_foreign_keys_count_impl::value; + }; + + /** + * C is any column type: column_t or constraint type + * O - object type references in FOREIGN KEY + */ + template + struct column_fk_references { + using type = std::tuple<>; + }; + + template + struct column_fk_references< + foreign_key_t, + O, + typename std::enable_if::target_type>::value>::type> { + using target_type = typename foreign_key_t::source_type; + + using type = std::tuple; + }; + + template + struct column_fk_references< + foreign_key_t, + O, + typename std::enable_if::target_type>::value>::type> { + using type = std::tuple<>; + }; + + /** + * O - object type references in FOREIGN KEY + * Cs - column types which are stored in table_t::columns_type + */ + template + struct table_fk_references_impl; + + template + struct table_fk_references_impl { + using type = std::tuple<>; + }; + + template + struct table_fk_references_impl { + using head_tuple = typename column_fk_references::type; + using tail_tuple = typename table_fk_references_impl::type; + using type = typename conc_tuple::type; + }; + + /** + * T is table_t type + * O is object type which is the reference target (e.g. foreign_key(&Visit::userId).references(&User::id) has O = User) + */ + template + struct table_fk_references; + + template + struct table_fk_references, O> { + using table_type = table_t; + + using type = typename table_fk_references_impl::type; + }; + + /** + * S - storage class + * O - type mapped to S + */ + template + struct storage_fk_references_impl; + + template + struct storage_fk_references_impl, O> { + using type = std::tuple<>; + }; + + template + struct storage_fk_references_impl, O> { + using head_tuple = typename table_fk_references::type; + using tail_tuple = typename storage_fk_references_impl, O>::type; + using type = typename conc_tuple::type; + }; + + /** + * S - storage class + * O - type mapped to S + * type holds `std::tuple` with types that has references to O as foreign keys + */ + template + struct storage_fk_references { + using impl_type = typename S::impl_type; + + using type = typename storage_fk_references_impl::type; + }; + } } } diff --git a/dev/sync_schema_result.h b/dev/sync_schema_result.h index 3fd8255a0..e4df5cc96 100644 --- a/dev/sync_schema_result.h +++ b/dev/sync_schema_result.h @@ -41,7 +41,7 @@ namespace sqlite_orm { dropped_and_recreated, }; - inline std::ostream &operator<<(std::ostream &os, sync_schema_result value) { + inline std::ostream& operator<<(std::ostream& os, sync_schema_result value) { switch(value) { case sync_schema_result::new_table_created: return os << "new table created"; diff --git a/dev/table.h b/dev/table.h index 151238127..7ee322a0b 100644 --- a/dev/table.h +++ b/dev/table.h @@ -10,7 +10,7 @@ #include "static_magic.h" #include "typed_comparator.h" #include "constraints.h" -#include "tuple_helper.h" +#include "tuple_helper/tuple_helper.h" #include "table_info.h" #include "type_printer.h" #include "column.h" @@ -19,6 +19,7 @@ namespace sqlite_orm { namespace internal { + template struct table_base { /** @@ -26,37 +27,40 @@ namespace sqlite_orm { */ std::string name; - bool _without_rowid = false; + static constexpr const bool is_without_rowid = _without_rowid; }; + template + struct table_without_rowid_t; + /** - * Table interface class. Implementation is hidden in `table_impl` class. + * Template for table interface class. */ - template - struct table_t : table_base { + template + struct table_template : table_base<_without_rowid> { using object_type = T; using columns_type = std::tuple; + using super = table_base<_without_rowid>; static constexpr const int columns_count = static_cast(std::tuple_size::value); + using super::name; columns_type columns; - table_t(decltype(name) name_, columns_type columns_) : - table_base{std::move(name_)}, columns(std::move(columns_)) {} + table_template(std::string name_, columns_type columns_) : + super{std::move(name_)}, columns{std::move(columns_)} {} - table_t without_rowid() const { - auto res = *this; - res._without_rowid = true; - return res; + table_without_rowid_t without_rowid() const { + return {name, columns}; } /** * Function used to get field value from object by mapped member pointer/setter/getter */ template - const F *get_object_field_pointer(const object_type &obj, C c) const { - const F *res = nullptr; - this->for_each_column_with_field_type([&res, &c, &obj](auto &col) { + const F* get_object_field_pointer(const object_type& obj, C c) const { + const F* res = nullptr; + this->for_each_column_with_field_type([&res, &c, &obj](auto& col) { using column_type = typename std::remove_reference::type; using member_pointer_t = typename column_type::member_pointer_t; using getter_type = typename column_type::getter_type; @@ -64,21 +68,21 @@ namespace sqlite_orm { // Make static_if have at least one input as a workaround for GCC bug: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095 if(!res) { - static_if{}>([&res, &obj, &col](const C &c_) { + static_if{}>([&res, &obj, &col](const C& c_) { if(compare_any(col.member_pointer, c_)) { res = &(obj.*col.member_pointer); } })(c); } if(!res) { - static_if{}>([&res, &obj, &col](const C &c_) { + static_if{}>([&res, &obj, &col](const C& c_) { if(compare_any(col.getter, c_)) { res = &((obj).*(col.getter))(); } })(c); } if(!res) { - static_if{}>([&res, &obj, &col](const C &c_) { + static_if{}>([&res, &obj, &col](const C& c_) { if(compare_any(col.setter, c_)) { res = &((obj).*(col.getter))(); } @@ -93,7 +97,7 @@ namespace sqlite_orm { */ std::vector column_names() const { std::vector res; - this->for_each_column([&res](auto &c) { + this->for_each_column([&res](auto& c) { res.push_back(c.name); }); return res; @@ -103,8 +107,8 @@ namespace sqlite_orm { * Calls **l** with every primary key dedicated constraint */ template - void for_each_primary_key(const L &l) const { - iterate_tuple(this->columns, [&l](auto &column) { + void for_each_primary_key(const L& l) const { + iterate_tuple(this->columns, [&l](auto& column) { using column_type = typename std::decay::type; static_if{}>(l)(column); }); @@ -112,7 +116,7 @@ namespace sqlite_orm { std::vector composite_key_columns_names() const { std::vector res; - this->for_each_primary_key([this, &res](auto &c) { + this->for_each_primary_key([this, &res](auto& c) { res = this->composite_key_columns_names(c); }); return res; @@ -120,7 +124,7 @@ namespace sqlite_orm { std::vector primary_key_column_names() const { std::vector res; - this->for_each_column_with>([&res](auto &c) { + this->for_each_column_with>([&res](auto& c) { res.push_back(c.name); }); if(!res.size()) { @@ -130,12 +134,16 @@ namespace sqlite_orm { } template - std::vector composite_key_columns_names(const constraints::primary_key_t &pk) const { + std::vector composite_key_columns_names(const primary_key_t& pk) const { std::vector res; using pk_columns_tuple = decltype(pk.columns); res.reserve(std::tuple_size::value); - iterate_tuple(pk.columns, [this, &res](auto &v) { - res.push_back(this->find_column_name(v)); + iterate_tuple(pk.columns, [this, &res](auto& v) { + if(auto columnName = this->find_column_name(v)) { + res.push_back(*columnName); + } else { + res.push_back({}); + } }); return res; } @@ -148,11 +156,11 @@ namespace sqlite_orm { class O, typename = typename std::enable_if::value && !std::is_member_function_pointer::value>::type> - std::string find_column_name(F O::*m) const { - std::string res; - this->template for_each_column_with_field_type([&res, m](auto &c) { + const std::string* find_column_name(F O::*m) const { + const std::string* res = nullptr; + this->template for_each_column_with_field_type([&res, m](auto& c) { if(c.member_pointer == m) { - res = c.name; + res = &c.name; } }); return res; @@ -163,13 +171,13 @@ namespace sqlite_orm { * @return column name or empty string if nothing found. */ template - std::string find_column_name(G getter, - typename std::enable_if::value>::type * = nullptr) const { - std::string res; + const std::string* find_column_name(G getter, + typename std::enable_if::value>::type* = nullptr) const { + const std::string* res = nullptr; using field_type = typename getter_traits::field_type; - this->template for_each_column_with_field_type([&res, getter](auto &c) { + this->template for_each_column_with_field_type([&res, getter](auto& c) { if(compare_any(c.getter, getter)) { - res = c.name; + res = &c.name; } }); return res; @@ -180,13 +188,13 @@ namespace sqlite_orm { * @return column name or empty string if nothing found. */ template - std::string find_column_name(S setter, - typename std::enable_if::value>::type * = nullptr) const { - std::string res; + const std::string* find_column_name(S setter, + typename std::enable_if::value>::type* = nullptr) const { + const std::string* res = nullptr; using field_type = typename setter_traits::field_type; - this->template for_each_column_with_field_type([&res, setter](auto &c) { + this->template for_each_column_with_field_type([&res, setter](auto& c) { if(compare_any(c.setter, setter)) { - res = c.name; + res = &c.name; } }); return res; @@ -200,16 +208,16 @@ namespace sqlite_orm { * @param l Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} */ template - void for_each_column(const L &l) const { - iterate_tuple(this->columns, [&l](auto &column) { + void for_each_column(const L& l) const { + iterate_tuple(this->columns, [&l](auto& column) { using column_type = typename std::decay::type; static_if{}>(l)(column); }); } template - void for_each_column_with_field_type(const L &l) const { - iterate_tuple(this->columns, [&l](auto &column) { + void for_each_column_with_field_type(const L& l) const { + iterate_tuple(this->columns, [&l](auto& column) { using column_type = typename std::decay::type; using field_type = typename column_field_type::type; static_if{}>(l)(column); @@ -223,9 +231,9 @@ namespace sqlite_orm { * @param l Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} */ template - void for_each_column_with(const L &l) const { + void for_each_column_with(const L& l) const { using tuple_helper::tuple_contains_type; - iterate_tuple(this->columns, [&l](auto &column) { + iterate_tuple(this->columns, [&l](auto& column) { using column_type = typename std::decay::type; using constraints_type = typename column_constraints_type::type; static_if{}>(l)(column); @@ -235,7 +243,7 @@ namespace sqlite_orm { std::vector get_table_info() const { std::vector res; res.reserve(size_t(this->columns_count)); - this->for_each_column([&res](auto &col) { + this->for_each_column([&res](auto& col) { std::string dft; using field_type = typename std::decay::type::field_type; if(auto d = col.default_value()) { @@ -247,14 +255,14 @@ namespace sqlite_orm { type_printer().print(), col.not_null(), dft, - col.template has>(), + col.template has>(), }; res.emplace_back(i); }); auto compositeKeyColumnNames = this->composite_key_columns_names(); for(size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { - auto &columnName = compositeKeyColumnNames[i]; - auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_info &ti) { + auto& columnName = compositeKeyColumnNames[i]; + auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_info& ti) { return ti.name == columnName; }); if(it != res.end()) { @@ -264,6 +272,22 @@ namespace sqlite_orm { return res; } }; + + /** + * Table interface class. + */ + template + struct table_t : table_template { + using table_template::table_template; + }; + + /** + * Table interface class with 'without_rowid' tag. + */ + template + struct table_without_rowid_t : table_template { + using table_template::table_template; + }; } /** @@ -271,12 +295,12 @@ namespace sqlite_orm { * cause table class is templated and its constructing too (just like std::make_unique or std::make_pair). */ template>::type::object_type> - internal::table_t make_table(const std::string &name, Cs... args) { + internal::table_t make_table(const std::string& name, Cs... args) { return {name, std::make_tuple(std::forward(args)...)}; } template - internal::table_t make_table(const std::string &name, Cs... args) { + internal::table_t make_table(const std::string& name, Cs... args) { return {name, std::make_tuple(std::forward(args)...)}; } } diff --git a/dev/table_info.h b/dev/table_info.h index 8beccaea1..70469766c 100644 --- a/dev/table_info.h +++ b/dev/table_info.h @@ -11,6 +11,15 @@ namespace sqlite_orm { bool notnull = false; std::string dflt_value; int pk = 0; + + table_info(decltype(cid) cid_, + decltype(name) name_, + decltype(type) type_, + decltype(notnull) notnull_, + decltype(dflt_value) dflt_value_, + decltype(pk) pk_) : + cid(cid_), + name(move(name_)), type(move(type_)), notnull(notnull_), dflt_value(move(dflt_value_)), pk(pk_) {} }; } diff --git a/dev/table_name_collector.h b/dev/table_name_collector.h index 20cf1a857..0a0cd72b7 100644 --- a/dev/table_name_collector.h +++ b/dev/table_name_collector.h @@ -20,6 +20,10 @@ namespace sqlite_orm { find_table_name_t find_table_name; mutable table_name_set table_names; + table_name_collector() = default; + + table_name_collector(find_table_name_t _find_table_name) : find_table_name(std::move(_find_table_name)) {} + template table_name_set operator()(const T &) const { return {}; @@ -69,6 +73,30 @@ namespace sqlite_orm { table_names.insert(std::make_pair(move(tableName), "")); } } + + template + void operator()(const table_rowid_t &) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + table_names.insert(std::make_pair(move(tableName), "")); + } + } + + template + void operator()(const table_oid_t &) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + table_names.insert(std::make_pair(move(tableName), "")); + } + } + + template + void operator()(const table__rowid_t &) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + table_names.insert(std::make_pair(move(tableName), "")); + } + } }; } diff --git a/dev/table_type.h b/dev/table_type.h index e4bd1790a..0c1e35042 100644 --- a/dev/table_type.h +++ b/dev/table_type.h @@ -1,17 +1,27 @@ #pragma once -#include // std::enable_if, std::is_member_pointer +#include // std::enable_if, std::is_member_pointer, std::is_member_function_pointer -#include "select_constraints.h" -#include "column.h" +#include "member_traits/getter_traits.h" +#include "member_traits/setter_traits.h" +#include "member_traits/is_getter.h" +#include "member_traits/is_setter.h" namespace sqlite_orm { namespace internal { + template + struct column_pointer; + /** * Trait class used to define table mapped type by setter/getter/member * T - member pointer + * `type` is a type which is mapped. + * E.g. + * - `table_type::type` is `User` + * - `table_type::type` is `User` + * - `table_type::type` is `User` */ template struct table_type; diff --git a/dev/tags.h b/dev/tags.h new file mode 100644 index 000000000..bc77292ea --- /dev/null +++ b/dev/tags.h @@ -0,0 +1,12 @@ +#pragma once + +namespace sqlite_orm { + namespace internal { + struct negatable_t {}; + + /** + * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators + */ + struct condition_t {}; + } +} diff --git a/dev/tuple_helper.h b/dev/tuple_helper.h deleted file mode 100644 index b1535521d..000000000 --- a/dev/tuple_helper.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include // std::tuple, std::get -#include // std::false_type, std::true_type - -#include "static_magic.h" - -namespace sqlite_orm { - - // got from here http://stackoverflow.com/questions/25958259/how-do-i-find-out-if-a-tuple-contains-a-type - namespace tuple_helper { - - template - struct has_type; - - template - struct has_type> : std::false_type {}; - - template - struct has_type> : has_type> {}; - - template - struct has_type> : std::true_type {}; - - template - using tuple_contains_type = typename has_type::type; - - template - struct iterator { - - template - void operator()(const std::tuple &t, const L &l, bool reverse = true) { - if(reverse) { - l(std::get(t)); - iterator()(t, l, reverse); - } else { - iterator()(t, l, reverse); - l(std::get(t)); - } - } - }; - - template - struct iterator<0, Args...> { - - template - void operator()(const std::tuple &t, const L &l, bool /*reverse*/ = true) { - l(std::get<0>(t)); - } - }; - - template - struct iterator { - - template - void operator()(const std::tuple<> &, const L &, bool /*reverse*/ = true) { - //.. - } - }; - - template - void move_tuple_impl(L &lhs, R &rhs) { - std::get(lhs) = std::move(std::get(rhs)); - internal::static_if{}>([](auto &l, auto &r) { - move_tuple_impl(l, r); - })(lhs, rhs); - } - } - - namespace internal { - - template - void move_tuple(L &lhs, R &rhs) { - using bool_type = std::integral_constant; - static_if([](auto &l, auto &r) { - tuple_helper::move_tuple_impl(l, r); - })(lhs, rhs); - } - - template - void iterate_tuple(const std::tuple &t, const L &l) { - using tuple_type = std::tuple; - tuple_helper::iterator::value - 1, Args...>()(t, l, false); - } - - template - using tuple_cat_t = decltype(std::tuple_cat(std::declval()...)); - - template - struct conc_tuple { - using type = tuple_cat_t; - }; - - template class C> - struct count_tuple; - - template class C> - struct count_tuple, C> { - static constexpr const int value = 0; - }; - - template class C> - struct count_tuple, C> { - static constexpr const int value = C::value + count_tuple, C>::value; - }; - } -} diff --git a/dev/tuple_helper/count_tuple.h b/dev/tuple_helper/count_tuple.h new file mode 100644 index 000000000..5822b050a --- /dev/null +++ b/dev/tuple_helper/count_tuple.h @@ -0,0 +1,21 @@ +#pragma once + +#include // std::tuple + +namespace sqlite_orm { + namespace internal { + + template class C> + struct count_tuple; + + template class C> + struct count_tuple, C> { + static constexpr const int value = 0; + }; + + template class C> + struct count_tuple, C> { + static constexpr const int value = C::value + count_tuple, C>::value; + }; + } +} diff --git a/dev/tuple_helper/find_in_tuple.h b/dev/tuple_helper/find_in_tuple.h new file mode 100644 index 000000000..a3e13bca2 --- /dev/null +++ b/dev/tuple_helper/find_in_tuple.h @@ -0,0 +1,27 @@ +#pragma once + +#include // std::tuple +#include // std::enable_if + +namespace sqlite_orm { + namespace internal { + + template class C, class SFINAE = void> + struct find_in_tuple; + + template class C> + struct find_in_tuple, C, void> { + using type = void; + }; + + template class C> + struct find_in_tuple, C, typename std::enable_if::value>::type> { + using type = H; + }; + + template class C> + struct find_in_tuple, C, typename std::enable_if::value>::type> { + using type = typename find_in_tuple, C>::type; + }; + } +} diff --git a/dev/tuple_helper/same_or_void.h b/dev/tuple_helper/same_or_void.h new file mode 100644 index 000000000..10028bd7f --- /dev/null +++ b/dev/tuple_helper/same_or_void.h @@ -0,0 +1,35 @@ +#pragma once + +namespace sqlite_orm { + namespace internal { + + /** + * Accepts any number of arguments and evaluates `type` alias as T if all arguments are the same or void otherwise + */ + template + struct same_or_void { + using type = void; + }; + + template + struct same_or_void { + using type = A; + }; + + template + struct same_or_void { + using type = void; + }; + + template + struct same_or_void { + using type = A; + }; + + template + struct same_or_void { + using type = typename same_or_void::type; + }; + + } +} diff --git a/dev/tuple_helper/tuple_helper.h b/dev/tuple_helper/tuple_helper.h new file mode 100644 index 000000000..f279548d4 --- /dev/null +++ b/dev/tuple_helper/tuple_helper.h @@ -0,0 +1,186 @@ +#pragma once + +#include // std::tuple, std::get, std::tuple_element, std::tuple_size +#include // std::false_type, std::true_type + +#include "static_magic.h" + +namespace sqlite_orm { + + // got from here http://stackoverflow.com/questions/25958259/how-do-i-find-out-if-a-tuple-contains-a-type + namespace tuple_helper { + /** + * HAS_TYPE type trait + */ + template + struct has_type; + + template + struct has_type> : std::false_type {}; + + template + struct has_type> : has_type> {}; + + template + struct has_type> : std::true_type {}; + + template + using tuple_contains_type = typename has_type::type; + + /** + * HAS_SOME_TYPE type trait + */ + template class TT, typename Tuple> + struct has_some_type; + + template class TT> + struct has_some_type> : std::false_type {}; + + template class TT, typename U, typename... Ts> + struct has_some_type> : has_some_type> {}; + + template class TT, typename T, typename... Ts> + struct has_some_type, Ts...>> : std::true_type {}; + + template class TT, typename Tuple> + using tuple_contains_some_type = typename has_some_type::type; + + template + struct iterator_impl { + + template + void operator()(const std::tuple& tuple, const L& lambda, bool reverse = true) { + if(reverse) { + lambda(std::get(tuple)); + iterator_impl()(tuple, lambda, reverse); + } else { + iterator_impl()(tuple, lambda, reverse); + lambda(std::get(tuple)); + } + } + + template + void operator()(const L& lambda) { + iterator_impl()(lambda); + lambda((const typename std::tuple_element>::type*)nullptr); + } + }; + + template + struct iterator_impl<0, Args...> { + + template + void operator()(const std::tuple& tuple, const L& lambda, bool /*reverse*/ = true) { + lambda(std::get<0>(tuple)); + } + + template + void operator()(const L& lambda) { + lambda((const typename std::tuple_element<0, std::tuple>::type*)nullptr); + } + }; + + template + struct iterator_impl { + + template + void operator()(const std::tuple<>&, const L&, bool /*reverse*/ = true) { + //.. + } + + template + void operator()(const L&) { + //.. + } + }; + + template + struct iterator_impl2; + + template<> + struct iterator_impl2<> { + + template + void operator()(const L&) const { + //.. + } + }; + + template + struct iterator_impl2 { + + template + void operator()(const L& lambda) const { + lambda((const H*)nullptr); + iterator_impl2{}(lambda); + } + }; + + template + struct iterator; + + template + struct iterator> { + + template + void operator()(const L& lambda) const { + iterator_impl2{}(lambda); + } + }; + } + + namespace internal { + + // got it form here https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer + template + auto call_impl(Function& f, FunctionPointer functionPointer, Tuple t, std::index_sequence) { + return (f.*functionPointer)(std::get(t)...); + } + + template + auto call(Function& f, FunctionPointer functionPointer, Tuple t) { + static constexpr auto size = std::tuple_size::value; + return call_impl(f, functionPointer, move(t), std::make_index_sequence{}); + } + + template + auto call(Function& f, Tuple t) { + return call(f, &Function::operator(), move(t)); + } + + template + void move_tuple_impl(L& lhs, R& rhs) { + std::get(lhs) = std::move(std::get(rhs)); + internal::static_if{}>([](auto& l, auto& r) { + move_tuple_impl(l, r); + })(lhs, rhs); + } + + template + void move_tuple(L& lhs, R& rhs) { + using bool_type = std::integral_constant; + static_if([](auto& l, auto& r) { + move_tuple_impl(l, r); + })(lhs, rhs); + } + + template + void iterate_tuple(const std::tuple& tuple, const L& lambda) { + using tuple_type = std::tuple; + tuple_helper::iterator_impl::value - 1, Args...>()(tuple, lambda, false); + } + + template + void iterate_tuple(const L& lambda) { + tuple_helper::iterator{}(lambda); + } + + template + using tuple_cat_t = decltype(std::tuple_cat(std::declval()...)); + + template + struct conc_tuple { + using type = tuple_cat_t; + }; + } +} diff --git a/dev/tuple_helper/tuple_transformer.h b/dev/tuple_helper/tuple_transformer.h new file mode 100644 index 000000000..7086707d7 --- /dev/null +++ b/dev/tuple_helper/tuple_transformer.h @@ -0,0 +1,16 @@ +#pragma once + +#include // std::tuple + +namespace sqlite_orm { + namespace internal { + + template class F> + struct tuple_transformer; + + template class F> + struct tuple_transformer, F> { + using type = std::tuple::type...>; + }; + } +} diff --git a/dev/type_is_nullable.h b/dev/type_is_nullable.h index 56aa59909..a835b47c5 100644 --- a/dev/type_is_nullable.h +++ b/dev/type_is_nullable.h @@ -17,7 +17,7 @@ namespace sqlite_orm { */ template struct type_is_nullable : public std::false_type { - bool operator()(const T &) const { + bool operator()(const T&) const { return true; } }; @@ -27,7 +27,7 @@ namespace sqlite_orm { */ template struct type_is_nullable> : public std::true_type { - bool operator()(const std::shared_ptr &t) const { + bool operator()(const std::shared_ptr& t) const { return static_cast(t); } }; @@ -37,7 +37,7 @@ namespace sqlite_orm { */ template struct type_is_nullable> : public std::true_type { - bool operator()(const std::unique_ptr &t) const { + bool operator()(const std::unique_ptr& t) const { return static_cast(t); } }; @@ -48,7 +48,7 @@ namespace sqlite_orm { */ template struct type_is_nullable> : public std::true_type { - bool operator()(const std::optional &t) const { + bool operator()(const std::optional& t) const { return t.has_value(); } }; diff --git a/dev/type_printer.h b/dev/type_printer.h index 764d9081c..2ae90f87b 100644 --- a/dev/type_printer.h +++ b/dev/type_printer.h @@ -16,28 +16,28 @@ namespace sqlite_orm { struct type_printer; struct integer_printer { - inline const std::string &print() { + inline const std::string& print() { static const std::string res = "INTEGER"; return res; } }; struct text_printer { - inline const std::string &print() { + inline const std::string& print() { static const std::string res = "TEXT"; return res; } }; struct real_printer { - inline const std::string &print() { + inline const std::string& print() { static const std::string res = "REAL"; return res; } }; struct blob_printer { - inline const std::string &print() { + inline const std::string& print() { static const std::string res = "BLOB"; return res; } @@ -87,7 +87,7 @@ namespace sqlite_orm { struct type_printer : public text_printer {}; template<> - struct type_printer : public text_printer {}; + struct type_printer : public text_printer {}; template<> struct type_printer : public real_printer {}; diff --git a/dev/typed_comparator.h b/dev/typed_comparator.h index 9691e9cfa..288ed1cb2 100644 --- a/dev/typed_comparator.h +++ b/dev/typed_comparator.h @@ -9,20 +9,20 @@ namespace sqlite_orm { */ template struct typed_comparator { - bool operator()(const L &, const R &) const { + bool operator()(const L&, const R&) const { return false; } }; template struct typed_comparator { - bool operator()(const O &lhs, const O &rhs) const { + bool operator()(const O& lhs, const O& rhs) const { return lhs == rhs; } }; template - bool compare_any(const L &lhs, const R &rhs) { + bool compare_any(const L& lhs, const R& rhs) { return typed_comparator()(lhs, rhs); } } diff --git a/dev/util.h b/dev/util.h new file mode 100644 index 000000000..d3d792463 --- /dev/null +++ b/dev/util.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include // std::string +#include // std::system_error, std::error_code + +namespace sqlite_orm { + + namespace internal { + inline void perform_step(sqlite3* db, sqlite3_stmt* stmt) { + auto rc = sqlite3_step(stmt); + if(rc == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + static void perform_void_exec(sqlite3* db, const std::string& query) { + int rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); + if(rc != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + inline auto call_insert_impl_and_catch_constraint_failed(const T& insert_impl) { + try { + return insert_impl(); + } catch(const std::system_error& e) { + if(e.code() == std::error_code(SQLITE_CONSTRAINT, get_sqlite_error_category())) { + std::stringstream ss; + ss << "Attempting to execute 'insert' request resulted in an error like \"" << e.what() + << "\". Perhaps ordinary 'insert' is not acceptable for this table and you should try " + "'replace' or 'insert' with explicit column listing?"; + throw std::system_error(e.code(), ss.str()); + } + throw; + } + } + } +} diff --git a/dev/values.h b/dev/values.h index ed608738f..62852806e 100644 --- a/dev/values.h +++ b/dev/values.h @@ -3,6 +3,7 @@ #include // std::vector #include #include // std::tuple +#include // std::false_type, std::true_type namespace sqlite_orm { @@ -10,9 +11,17 @@ namespace sqlite_orm { template struct values_t { - std::tuple tuple; + using args_tuple = std::tuple; + + args_tuple tuple; }; + template + struct is_values : std::false_type {}; + + template + struct is_values> : std::true_type {}; + template struct dynamic_values_t { std::vector vector; diff --git a/dev/values_to_tuple.h b/dev/values_to_tuple.h new file mode 100644 index 000000000..6beca857e --- /dev/null +++ b/dev/values_to_tuple.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include // std::get, std::tuple_element + +#include "row_extractor.h" +#include "arg_values.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * T is a std::tuple type + * I is index to extract value from values C array to tuple. I must me < std::tuple_size::value + */ + template + struct values_to_tuple { + + void extract(sqlite3_value **values, T &tuple, int argsCount) const { + using element_type = typename std::tuple_element::type; + std::get(tuple) = row_extractor().extract(values[I]); + + values_to_tuple().extract(values, tuple, argsCount); + } + }; + + template + struct values_to_tuple { + void extract(sqlite3_value **values, T &tuple, int argsCount) const { + //.. + } + }; + + template<> + struct values_to_tuple, 0> { + void extract(sqlite3_value **values, std::tuple &tuple, int argsCount) const { + std::get<0>(tuple) = arg_values(argsCount, values); + } + }; + } +} diff --git a/dev/view.h b/dev/view.h index 5abddd4e5..4d17423c4 100644 --- a/dev/view.h +++ b/dev/view.h @@ -19,17 +19,26 @@ namespace sqlite_orm { namespace internal { + /** + * This class does not related to SQL view. This is a container like class which is returned by + * by storage_t::iterate function. This class contains STL functions: + * - size_t size() + * - bool empty() + * - iterator end() + * - iterator begin() + * All these functions are not right const cause all of them may open SQLite connections. + */ template struct view_t { using mapped_type = T; using storage_type = S; using self = view_t; - storage_type &storage; + storage_type& storage; connection_ref connection; get_all_t, Args...> args; - view_t(storage_type &stor, decltype(connection) conn, Args &&... args_) : + view_t(storage_type& stor, decltype(connection) conn, Args&&... args_) : storage(stor), connection(std::move(conn)), args{std::make_tuple(std::forward(args_)...)} {} size_t size() { @@ -40,12 +49,8 @@ namespace sqlite_orm { return !this->size(); } - iterator_t end() { - return {nullptr, *this}; - } - iterator_t begin() { - sqlite3_stmt *stmt = nullptr; + sqlite3_stmt* stmt = nullptr; auto db = this->connection.get(); using context_t = serializator_context; context_t context{this->storage.impl}; @@ -55,7 +60,7 @@ namespace sqlite_orm { auto ret = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); if(ret == SQLITE_OK) { auto index = 1; - iterate_ast(this->args.conditions, [&index, stmt, db](auto &node) { + iterate_ast(this->args.conditions, [&index, stmt, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -69,6 +74,10 @@ namespace sqlite_orm { sqlite3_errmsg(db)); } } + + iterator_t end() { + return {}; + } }; } } diff --git a/examples/blob.cpp b/examples/blob.cpp index dfb5418df..279ef842d 100644 --- a/examples/blob.cpp +++ b/examples/blob.cpp @@ -14,7 +14,7 @@ struct User { std::vector hash; // binary format }; -int main(int, char **) { +int main(int, char**) { using namespace sqlite_orm; auto storage = make_storage("blob.sqlite", make_table("users", diff --git a/examples/case.cpp b/examples/case.cpp index 2bc3a565d..f2e0d6a14 100644 --- a/examples/case.cpp +++ b/examples/case.cpp @@ -34,7 +34,7 @@ int main() { }); // list all students - for(auto &student: storage.iterate()) { + for(auto& student: storage.iterate()) { cout << storage.dump(student) << endl; } cout << endl; @@ -60,7 +60,7 @@ int main() { .when(greater_or_equal(&Student::marks, 50), then("C")) .else_("Sorry!! Failed") .end())); - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << ' ' << std::get<1>(row) << ' ' << std::get<2>(row) << ' ' << std::get<3>(row) << endl; } @@ -69,7 +69,7 @@ int main() { { // with alias struct GradeAlias : alias_tag { - static const std::string &get() { + static const std::string& get() { static const std::string res = "Grade"; return res; } @@ -94,7 +94,7 @@ int main() { .when(greater_or_equal(&Student::marks, 50), then("C")) .else_("Sorry!! Failed") .end()))); - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << ' ' << std::get<1>(row) << ' ' << std::get<2>(row) << ' ' << std::get<3>(row) << endl; } diff --git a/examples/check.cpp b/examples/check.cpp index 865a6f16a..f6b2841f0 100644 --- a/examples/check.cpp +++ b/examples/check.cpp @@ -41,20 +41,20 @@ int main() { try { storage.insert(Contact{0, "John", "Doe", {}, "408123456"}); - } catch(const std::system_error &e) { + } catch(const std::system_error& e) { cout << e.what() << endl; } storage.insert(Contact{0, "John", "Doe", {}, "(408)-123-456"}); try { storage.insert(Product{0, "New Product", 900, 1000}); - } catch(const std::system_error &e) { + } catch(const std::system_error& e) { cout << e.what() << endl; } try { storage.insert(Product{0, "New XFactor", 1000, -10}); - } catch(const std::system_error &e) { + } catch(const std::system_error& e) { cout << e.what() << endl; } diff --git a/examples/collate.cpp b/examples/collate.cpp index e925f6f28..426790019 100644 --- a/examples/collate.cpp +++ b/examples/collate.cpp @@ -19,7 +19,7 @@ struct Foo { int baz; }; -int main(int, char **) { +int main(int, char**) { using namespace sqlite_orm; auto storage = make_storage( diff --git a/examples/composite_key.cpp b/examples/composite_key.cpp index df7f7aee2..e128f0efb 100644 --- a/examples/composite_key.cpp +++ b/examples/composite_key.cpp @@ -53,7 +53,7 @@ int main() { try { // 2 and 'Drake' values will be ignored cause they are primary keys storage.insert(User{2, "Drake", "Singer"}); - } catch(const std::system_error &e) { + } catch(const std::system_error& e) { cout << "exception = " << e.what() << endl; } storage.replace(User{2, "The Weeknd", "Singer"}); diff --git a/examples/core_functions.cpp b/examples/core_functions.cpp index 0029fbe9d..d2d45a69b 100644 --- a/examples/core_functions.cpp +++ b/examples/core_functions.cpp @@ -1,4 +1,3 @@ - #include #include #include @@ -7,10 +6,10 @@ using std::cout; using std::endl; struct MarvelHero { - int id; + int id = 0; std::string name; std::string abilities; - short points; + short points = 0; }; struct Contact { @@ -20,7 +19,23 @@ struct Contact { std::string phone; }; -int main(int, char **argv) { +struct Customer { + int id = 0; + std::string firstName; + std::string lastName; + std::string company; + std::string address; + std::string city; + std::string state; + std::string country; + std::string postalCode; + std::string phone; + std::unique_ptr fax; + std::string email; + int supportRepId = 0; +}; + +int main(int, char** argv) { cout << "path = " << argv[0] << endl; using namespace sqlite_orm; @@ -34,7 +49,21 @@ int main(int, char **argv) { make_column("contact_id", &Contact::id, primary_key()), make_column("first_name", &Contact::firstName), make_column("last_name", &Contact::lastName), - make_column("phone", &Contact::phone))); + make_column("phone", &Contact::phone)), + make_table("customers", + make_column("CustomerId", &Customer::id, primary_key()), + make_column("FirstName", &Customer::firstName), + make_column("LastName", &Customer::lastName), + make_column("Company", &Customer::company), + make_column("Address", &Customer::address), + make_column("City", &Customer::city), + make_column("State", &Customer::state), + make_column("Country", &Customer::country), + make_column("PostalCode", &Customer::postalCode), + make_column("Phone", &Customer::phone), + make_column("Fax", &Customer::fax), + make_column("Email", &Customer::email), + make_column("SupportRepId", &Customer::supportRepId))); storage.sync_schema(); storage.remove_all(); @@ -51,6 +80,777 @@ int main(int, char **argv) { storage.insert(MarvelHero{-1, "Clint Barton", "Hawkeye", -11}); storage.insert(MarvelHero{-1, "Natasha Romanoff", "Black widow", 8}); storage.insert(MarvelHero{-1, "Groot", "I am Groot!", 2}); + + cout << "SELECT last_insert_rowid() = " << storage.select(last_insert_rowid()).front() << endl; + + storage.replace(Customer{1, + "Luís", + "Gonçalves", + "Embraer - Empresa Brasileira de Aeronáutica S.A.", + "Av. Brigadeiro Faria Lima, 2170", + "São José dos Campos", + "SP", + "Brazil", + "12227-000", + "+55 (12) 3923-5555", + std::make_unique("+55 (12) 3923-5566"), + "luisg@embraer.com.br", + 3}); + storage.replace(Customer{2, + "Leonie", + "Köhler", + "", + "Theodor-Heuss-Straße 34", + "Stuttgart", + "", + "Germany", + "70174", + "+49 0711 2842222", + nullptr, + "leonekohler@surfeu.de", + 5}); + storage.replace(Customer{3, + "François", + "Tremblay", + "", + "1498 rue Bélanger", + "Montréal", + "QC", + "Canada", + "H2G 1A7", + "+1 (514) 721-4711", + nullptr, + "ftremblay@gmail.com", + 3}); + storage.replace(Customer{4, + "Bjørn", + "Hansen", + "", + "Ullevålsveien 14", + "Oslo", + "", + "Norway", + "0171", + "+47 22 44 22 22", + nullptr, + "bjorn.hansen@yahoo.no", + 4}); + storage.replace(Customer{5, + "František", + "Wichterlová", + "JetBrains s.r.o.", + "Klanova 9/506", + "Prague", + "", + "Czech Republic", + "14700", + "+420 2 4172 5555", + std::make_unique("+420 2 4172 5555"), + "frantisekw@jetbrains.com", + 4}); + storage.replace(Customer{6, + "Helena", + "Holý", + "", + "Rilská 3174/6", + "Prague", + "", + "Czech Republic", + "14300", + "+420 2 4177 0449", + nullptr, + "hholy@gmail.com", + 5}); + storage.replace(Customer{7, + "Astrid", + "Gruber", + "", + "Rotenturmstraße 4, 1010 Innere Stadt", + "Vienne", + "", + "Austria", + "1010", + "+43 01 5134505", + nullptr, + "astrid.gruber@apple.at", + 5}); + storage.replace(Customer{8, + "Daan", + "Peeters", + "", + "Grétrystraat 63", + "Brussels", + "", + "Belgium", + "1000", + "+32 02 219 03 03", + nullptr, + "daan_peeters@apple.be", + 4}); + storage.replace(Customer{9, + "Kara", + "Nielsen", + "", + "Sønder Boulevard 51", + "Copenhagen", + "", + "Denmark", + "1720", + "+453 3331 9991", + nullptr, + "kara.nielsen@jubii.dk", + 4}); + storage.replace(Customer{10, + "Eduardo", + "Martins", + "Woodstock Discos", + "Rua Dr. Falcão Filho, 155", + "São Paulo", + "SP", + "Brazil", + "01007-010", + "+55 (11) 3033-5446", + std::make_unique("+55 (11) 3033-4564"), + "eduardo@woodstock.com.br", + 4}); + storage.replace(Customer{11, + "Alexandre", + "Rocha", + "Banco do Brasil S.A.", + "Av. Paulista, 2022", + "São Paulo", + "SP", + "Brazil", + "01310-200", + "+55 (11) 3055-3278", + std::make_unique("+55 (11) 3055-8131"), + "alero@uol.com.br", + 5}); + storage.replace(Customer{12, + "Roberto", + "Almeida", + "Riotur", + "Praça Pio X, 119", + "Rio de Janeiro", + "RJ", + "Brazil", + "20040-020", + "+55 (21) 2271-7000", + std::make_unique("+55 (21) 2271-7070"), + "roberto.almeida@riotur.gov.br", + 3}); + storage.replace(Customer{13, + "Fernanda", + "Ramos", + "", + "Qe 7 Bloco G", + "Brasília", + "DF", + "Brazil", + "71020-677", + "+55 (61) 3363-5547", + std::make_unique("+55 (61) 3363-7855"), + "fernadaramos4@uol.com.br", + 4}); + storage.replace(Customer{14, + "Mark", + "Philips", + "Telus", + "8210 111 ST NW", + "Edmonton", + "AB", + "Canada", + "T6G 2C7", + "+1 (780) 434-4554", + std::make_unique("+1 (780) 434-5565"), + "mphilips12@shaw.ca", + 5}); + storage.replace(Customer{15, + "Jennifer", + "Peterson", + "Rogers Canada", + "700 W Pender Street", + "Vancouver", + "BC", + "Canada", + "V6C 1G8", + "+1 (604) 688-2255", + std::make_unique("+1 (604) 688-8756"), + "jenniferp@rogers.ca", + 3}); + storage.replace(Customer{16, + "Frank", + "Harris", + "Google Inc.", + "1600 Amphitheatre Parkway", + "Mountain View", + "CA", + "USA", + "94043-1351", + "+1 (650) 253-0000", + std::make_unique("+1 (650) 253-0000"), + "fharris@google.com", + 4}); + storage.replace(Customer{17, + "Jack", + "Smith", + "Microsoft Corporation", + "1 Microsoft Way", + "Redmond", + "WA", + "USA", + "98052-8300", + "+1 (425) 882-8080", + std::make_unique("+1 (425) 882-8081"), + "jacksmith@microsoft.com", + 5}); + storage.replace(Customer{18, + "Michelle", + "Brooks", + "", + "627 Broadway", + "New York", + "NY", + "USA", + "10012-2612", + "+1 (212) 221-3546", + std::make_unique("+1 (212) 221-4679"), + "michelleb@aol.com", + 3}); + storage.replace(Customer{19, + "Tim", + "Goyer", + "Apple Inc.", + "1 Infinite Loop", + "Cupertino", + "CA", + "USA", + "95014", + "+1 (408) 996-1010", + std::make_unique("+1 (408) 996-1011"), + "tgoyer@apple.com", + 3}); + storage.replace(Customer{20, + "Dan", + "Miller", + "", + "541 Del Medio Avenue", + "Mountain View", + "CA", + "USA", + "94040-111", + "+1 (650) 644-3358", + nullptr, + "dmiller@comcast.com", + 4}); + storage.replace(Customer{21, + "Kathy", + "Chase", + "", + "801 W 4th Street", + "Reno", + "NV", + "USA", + "89503", + "+1 (775) 223-7665", + nullptr, + "kachase@hotmail.com", + 5}); + storage.replace(Customer{22, + "Heather", + "Leacock", + "", + "120 S Orange Ave", + "Orlando", + "FL", + "USA", + "32801", + "+1 (407) 999-7788", + nullptr, + "hleacock@gmail.com", + 4}); + storage.replace(Customer{23, + "John", + "Gordon", + "", + "69 Salem Street", + "Boston", + "MA", + "USA", + "2113", + "+1 (617) 522-1333", + nullptr, + "johngordon22@yahoo.com", + 4}); + storage.replace(Customer{24, + "Frank", + "Ralston", + "", + "162 E Superior Street", + "Chicago", + "IL", + "USA", + "60611", + "+1 (312) 332-3232", + nullptr, + "fralston@gmail.com", + 3}); + storage.replace(Customer{25, + "Victor", + "Stevens", + "", + "319 N. Frances Street", + "Madison", + "WI", + "USA", + "53703", + "+1 (608) 257-0597", + nullptr, + "vstevens@yahoo.com", + 5}); + storage.replace(Customer{26, + "Richard", + "Cunningham", + "", + "2211 W Berry Street", + "Fort Worth", + "TX", + "USA", + "76110", + "+1 (817) 924-7272", + nullptr, + "ricunningham@hotmail.com", + 4}); + storage.replace(Customer{27, + "Patrick", + "Gray", + "", + "1033 N Park Ave", + "Tucson", + "AZ", + "USA", + "85719", + "+1 (520) 622-4200", + nullptr, + "patrick.gray@aol.com", + 4}); + storage.replace(Customer{28, + "Julia", + "Barnett", + "", + "302 S 700 E", + "Salt Lake City", + "UT", + "USA", + "84102", + "+1 (801) 531-7272", + nullptr, + "jubarnett@gmail.com", + 5}); + storage.replace(Customer{29, + "Robert", + "Brown", + "", + "796 Dundas Street West", + "Toronto", + "ON", + "Canada", + "M6J 1V1", + "+1 (416) 363-8888", + nullptr, + "robbrown@shaw.ca", + 3}); + storage.replace(Customer{30, + "Edward", + "Francis", + "", + "230 Elgin Street", + "Ottawa", + "ON", + "Canada", + "K2P 1L7", + "+1 (613) 234-3322", + nullptr, + "edfrancis@yachoo.ca", + 3}); + storage.replace(Customer{31, + "Martha", + "Silk", + "", + "194A Chain Lake Drive", + "Halifax", + "NS", + "Canada", + "B3S 1C5", + "+1 (902) 450-0450", + nullptr, + "marthasilk@gmail.com", + 5}); + storage.replace(Customer{32, + "Aaron", + "Mitchell", + "", + "696 Osborne Street", + "Winnipeg", + "MB", + "Canada", + "R3L 2B9", + "+1 (204) 452-6452", + nullptr, + "aaronmitchell@yahoo.ca", + 4}); + storage.replace(Customer{33, + "Ellie", + "Sullivan", + "", + "5112 48 Street", + "Yellowknife", + "NT", + "Canada", + "X1A 1N6", + "+1 (867) 920-2233", + nullptr, + "ellie.sullivan@shaw.ca", + 3}); + storage.replace(Customer{34, + "João", + "Fernandes", + "", + "Rua da Assunção 53", + "Lisbon", + "", + "Portugal", + "", + "+351 (213) 466-111", + nullptr, + "jfernandes@yahoo.pt", + 4}); + storage.replace(Customer{35, + "Madalena", + "Sampaio", + "", + "Rua dos Campeões Europeus de Viena, 4350", + "Porto", + "", + "Portugal", + "", + "+351 (225) 022-448", + nullptr, + "masampaio@sapo.pt", + 4}); + storage.replace(Customer{36, + "Hannah", + "Schneider", + "", + "Tauentzienstraße 8", + "Berlin", + "", + "Germany", + "10789", + "+49 030 26550280", + nullptr, + "hannah.schneider@yahoo.de", + 5}); + storage.replace(Customer{37, + "Fynn", + "Zimmermann", + "", + "Berger Straße 10", + "Frankfurt", + "", + "Germany", + "60316", + "+49 069 40598889", + nullptr, + "fzimmermann@yahoo.de", + 3}); + storage.replace(Customer{38, + "Niklas", + "Schröder", + "", + "Barbarossastraße 19", + "Berlin", + "", + "Germany", + "10779", + "+49 030 2141444", + nullptr, + "nschroder@surfeu.de", + 3}); + storage.replace(Customer{39, + "Camille", + "Bernard", + "", + "4, Rue Milton", + "Paris", + "", + "France", + "75009", + "+33 01 49 70 65 65", + nullptr, + "camille.bernard@yahoo.fr", + 4}); + storage.replace(Customer{40, + "Dominique", + "Lefebvre", + "", + "8, Rue Hanovre", + "Paris", + "", + "France", + "75002", + "+33 01 47 42 71 71", + nullptr, + "dominiquelefebvre@gmail.com", + 4}); + storage.replace(Customer{41, + "Marc", + "Dubois", + "", + "11, Place Bellecour", + "Lyon", + "", + "France", + "69002", + "+33 04 78 30 30 30", + nullptr, + "marc.dubois@hotmail.com", + 5}); + storage.replace(Customer{42, + "Wyatt", + "Girard", + "", + "9, Place Louis Barthou", + "Bordeaux", + "", + "France", + "33000", + "+33 05 56 96 96 96", + nullptr, + "wyatt.girard@yahoo.fr", + 3}); + storage.replace(Customer{43, + "Isabelle", + "Mercier", + "", + "68, Rue Jouvence", + "Dijon", + "", + "France", + "21000", + "+33 03 80 73 66 99", + nullptr, + "isabelle_mercier@apple.fr", + 3}); + storage.replace(Customer{44, + "Terhi", + "Hämäläinen", + "", + "Porthaninkatu 9", + "Helsinki", + "", + "Finland", + "00530", + "+358 09 870 2000", + nullptr, + "terhi.hamalainen@apple.fi", + 3}); + storage.replace(Customer{45, + "Ladislav", + "Kovács", + "", + "Erzsébet krt. 58.", + "Budapest", + "", + "Hungary", + "H-1073", + "", + nullptr, + "ladislav_kovacs@apple.hu", + 3}); + storage.replace(Customer{46, + "Hugh", + "O'Reilly", + "", + "3 Chatham Street", + "Dublin", + "Dublin", + "Ireland", + "", + "+353 01 6792424", + nullptr, + "hughoreilly@apple.ie", + 3}); + storage.replace(Customer{47, + "Lucas", + "Mancini", + "", + "Via Degli Scipioni, 43", + "Rome", + "RM", + "Italy", + "00192", + "+39 06 39733434", + nullptr, + "lucas.mancini@yahoo.it", + 5}); + storage.replace(Customer{48, + "Johannes", + "Van der Berg", + "", + "Lijnbaansgracht 120bg", + "Amsterdam", + "VV", + "Netherlands", + "1016", + "+31 020 6223130", + nullptr, + "johavanderberg@yahoo.nl", + 5}); + storage.replace(Customer{49, + "Stanisław", + "Wójcik", + "", + "Ordynacka 10", + "Warsaw", + "", + "Poland", + "00-358", + "+48 22 828 37 39", + nullptr, + "stanisław.wójcik@wp.pl", + 4}); + storage.replace(Customer{50, + "Enrique", + "Muñoz", + "", + "C/ San Bernardo 85", + "Madrid", + "", + "Spain", + "28015", + "+34 914 454 454", + nullptr, + "enrique_munoz@yahoo.es", + 5}); + storage.replace(Customer{51, + "Joakim", + "Johansson", + "", + "Celsiusg. 9", + "Stockholm", + "", + "Sweden", + "11230", + "+46 08-651 52 52", + nullptr, + "joakim.johansson@yahoo.se", + 5}); + storage.replace(Customer{52, + "Emma", + "Jones", + "", + "202 Hoxton Street", + "London", + "", + "United Kingdom", + "N1 5LH", + "+44 020 7707 0707", + nullptr, + "emma_jones@hotmail.com", + 3}); + storage.replace(Customer{53, + "Phil", + "Hughes", + "", + "113 Lupus St", + "London", + "", + "United Kingdom", + "SW1V 3EN", + "+44 020 7976 5722", + nullptr, + "phil.hughes@gmail.com", + 3}); + storage.replace(Customer{54, + "Steve", + "Murray", + "", + "110 Raeburn Pl", + "Edinburgh ", + "", + "United Kingdom", + "EH4 1HH", + "+44 0131 315 3300", + nullptr, + "steve.murray@yahoo.uk", + 5}); + storage.replace(Customer{55, + "Mark", + "Taylor", + "", + "421 Bourke Street", + "Sidney", + "NSW", + "Australia", + "2010", + "+61 (02) 9332 3633", + nullptr, + "mark.taylor@yahoo.au", + 4}); + storage.replace(Customer{56, + "Diego", + "Gutiérrez", + "", + "307 Macacha Güemes", + "Buenos Aires", + "", + "Argentina", + "1106", + "+54 (0)11 4311 4333", + nullptr, + "diego.gutierrez@yahoo.ar", + 4}); + storage.replace(Customer{57, + "Luis", + "Rojas", + "", + "Calle Lira, 198", + "Santiago", + "", + "Chile", + "", + "+56 (0)2 635 4444", + nullptr, + "luisrojas@yahoo.cl", + 5}); + storage.replace(Customer{58, + "Manoj", + "Pareek", + "", + "12,Community Centre", + "Delhi", + "", + "India", + "110017", + "+91 0124 39883988", + nullptr, + "manoj.pareek@rediff.com", + 3}); + storage.replace(Customer{59, + "Puja", + "Srivastava", + "", + "3,Raj Bhavan Road", + "Bangalore", + "", + "India", + "560001", + "+91 080 22289999", + nullptr, + "puja_srivastava@yahoo.in", + 3}); + return true; }); @@ -63,7 +863,7 @@ int main(int, char **argv) { // FROM marvel auto nameLengths = storage.select(length(&MarvelHero::name)); // nameLengths is std::vector cout << "nameLengths.size = " << nameLengths.size() << endl; - for(auto &len: nameLengths) { + for(auto& len: nameLengths) { cout << len << " "; } cout << endl; @@ -74,7 +874,7 @@ int main(int, char **argv) { columns(&MarvelHero::name, length(&MarvelHero::name))); // namesWithLengths is std::vector> cout << "namesWithLengths.size = " << namesWithLengths.size() << endl; - for(auto &row: namesWithLengths) { + for(auto& row: namesWithLengths) { cout << "LENGTH(" << std::get<0>(row) << ") = " << std::get<1>(row) << endl; } @@ -84,7 +884,7 @@ int main(int, char **argv) { auto namesWithLengthGreaterThan5 = storage.select(&MarvelHero::name, where(length(&MarvelHero::name) > 5)); // std::vector cout << "namesWithLengthGreaterThan5.size = " << namesWithLengthGreaterThan5.size() << endl; - for(auto &name: namesWithLengthGreaterThan5) { + for(auto& name: namesWithLengthGreaterThan5) { cout << "name = " << name << endl; } @@ -100,7 +900,7 @@ int main(int, char **argv) { // FROM marvel auto absPoints = storage.select(abs(&MarvelHero::points)); // std::vector> cout << "absPoints: "; - for(auto &value: absPoints) { + for(auto& value: absPoints) { if(value) { cout << *value; } else { @@ -115,7 +915,7 @@ int main(int, char **argv) { // WHERE ABS(points) < 5 auto namesByAbs = storage.select(&MarvelHero::name, where(abs(&MarvelHero::points) < 5)); cout << "namesByAbs.size = " << namesByAbs.size() << endl; - for(auto &name: namesByAbs) { + for(auto& name: namesByAbs) { cout << name << endl; } cout << endl; @@ -130,7 +930,7 @@ int main(int, char **argv) { // FROM marvel auto lowerNames = storage.select(lower(&MarvelHero::name)); cout << "lowerNames.size = " << lowerNames.size() << endl; - for(auto &name: lowerNames) { + for(auto& name: lowerNames) { cout << name << endl; } cout << endl; @@ -139,7 +939,7 @@ int main(int, char **argv) { // FROM marvel auto upperAbilities = storage.select(upper(&MarvelHero::abilities)); cout << "upperAbilities.size = " << upperAbilities.size() << endl; - for(auto &abilities: upperAbilities) { + for(auto& abilities: upperAbilities) { cout << abilities << endl; } cout << endl; @@ -148,9 +948,18 @@ int main(int, char **argv) { storage.remove_all(); // SELECT changes() - auto rowsRemoved = storage.select(changes()).front(); - cout << "rowsRemoved = " << rowsRemoved << endl; - assert(rowsRemoved == storage.changes()); + { + auto rowsRemoved = storage.select(changes()).front(); + cout << "rowsRemoved = " << rowsRemoved << endl; + assert(rowsRemoved == storage.changes()); + } + + // SELECT total_changes() + { + auto rowsRemoved = storage.select(total_changes()).front(); + cout << "rowsRemoved = " << rowsRemoved << endl; + assert(rowsRemoved == storage.changes()); + } return false; }); @@ -164,7 +973,7 @@ int main(int, char **argv) { // FROM marvel auto emails = storage.select(lower(&MarvelHero::name) || c("@marvel.com")); cout << "emails.size = " << emails.size() << endl; - for(auto &email: emails) { + for(auto& email: emails) { cout << email << endl; } cout << endl; @@ -192,7 +1001,7 @@ int main(int, char **argv) { } // SELECT * FROM marvel ORDER BY RANDOM() - for(auto &hero: storage.iterate(order_by(sqlite_orm::random()))) { + for(auto& hero: storage.iterate(order_by(sqlite_orm::random()))) { cout << "hero = " << storage.dump(hero) << endl; } @@ -291,7 +1100,7 @@ int main(int, char **argv) { { cout << endl; struct o_pos : alias_tag { - static const std::string &get() { + static const std::string& get() { static const std::string res = "o_pos"; return res; } @@ -302,7 +1111,7 @@ int main(int, char **argv) { // WHERE o_pos > 0 auto rows = storage.select(columns(&MarvelHero::name, as(instr(&MarvelHero::abilities, "o"))), where(greater_than(get(), 0))); - for(auto &row: rows) { + for(auto& row: rows) { cout << get<0>(row) << '\t' << get<1>(row) << endl; } cout << endl; @@ -320,7 +1129,7 @@ int main(int, char **argv) { // SET phone = REPLACE(phone, '410', '+1-410') storage.update_all(set(c(&Contact::phone) = replace(&Contact::phone, "410", "+1-410"))); cout << "Contacts:" << endl; - for(auto &contact: storage.iterate()) { + for(auto& contact: storage.iterate()) { cout << storage.dump(contact) << endl; } @@ -339,5 +1148,38 @@ int main(int, char **argv) { // SELECT soundex('Schn Thomson') cout << "SELECT soundex('Schn Thomson') = " << storage.select(soundex("Schn Thomson")).front() << endl; #endif + + // SELECT unicode('A') + cout << "SELECT unicode('A') = " << storage.select(unicode("A")).front() << endl; + + // SELECT unicode('Brush') + cout << "SELECT unicode('Brush') = " << storage.select(unicode("Brush")).front() << endl; + + // SELECT unicode(substr('Brush', 2)) + cout << "SELECT unicode(substr('Brush', 2)) = " << storage.select(unicode(substr("Brush", 2))).front() << endl; + + // SELECT typeof(1) + cout << "SELECT typeof(1) = " << storage.select(typeof_(1)).front() << endl; + + // SELECT firstname, lastname, IFNULL(fax, 'Call:' || phone) fax + // FROM customers + // ORDER BY firstname; + { + cout << endl; + cout << "SELECT firstname, lastname, IFNULL(fax, 'Call:' || phone) fax" << endl; + cout << "FROM customers" << endl; + cout << "ORDER BY firstname" << endl; + cout << endl; + + auto rows = storage.select(columns(&Customer::firstName, + &Customer::lastName, + ifnull(&Customer::fax, "Call:" || c(&Customer::phone))), + order_by(&Customer::firstName)); + cout << "rows count: " << rows.size() << endl; + for(auto& row: rows) { + cout << get<0>(row) << '\t' << get<1>(row) << '\t' << get<2>(row) << endl; + } + cout << endl; + } return 0; } diff --git a/examples/cross_join.cpp b/examples/cross_join.cpp index f8b09ef1f..28b2853ed 100644 --- a/examples/cross_join.cpp +++ b/examples/cross_join.cpp @@ -20,7 +20,7 @@ namespace DataModel { }; } -static auto initStorage(const std::string &path) { +static auto initStorage(const std::string& path) { using namespace DataModel; using namespace sqlite_orm; return make_storage(path, @@ -28,7 +28,7 @@ static auto initStorage(const std::string &path) { make_table("suits", make_column("suit", &Suit::suit))); } -int main(int, char **) { +int main(int, char**) { using namespace DataModel; using Storage = decltype(initStorage("")); diff --git a/examples/custom_aliases.cpp b/examples/custom_aliases.cpp index 1ac89470d..b7ac51e62 100644 --- a/examples/custom_aliases.cpp +++ b/examples/custom_aliases.cpp @@ -28,20 +28,20 @@ struct Department { using namespace sqlite_orm; struct EmployeeIdAlias : alias_tag { - static const std::string &get() { + static const std::string& get() { static const std::string res = "COMPANY_ID"; return res; } }; struct CompanyNameAlias : alias_tag { - static const std::string &get() { + static const std::string& get() { static const std::string res = "COMPANY_NAME"; return res; } }; -int main(int, char **argv) { +int main(int, char** argv) { cout << argv[0] << endl; auto storage = make_storage("custom_aliases.sqlite", @@ -85,7 +85,7 @@ int main(int, char **argv) { cout << "ID" << '\t' << "NAME" << '\t' << "AGE" << '\t' << "DEPT" << endl; cout << "----------" << '\t' << "----------" << '\t' << "----------" << '\t' << "----------" << endl; - for(auto &row: simpleRows) { + for(auto& row: simpleRows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) << endl; } diff --git a/examples/distinct.cpp b/examples/distinct.cpp index 6813f6ac1..3848e1a86 100644 --- a/examples/distinct.cpp +++ b/examples/distinct.cpp @@ -16,7 +16,7 @@ struct Employee { double salary; }; -auto initStorage(const std::string &path) { +auto initStorage(const std::string& path) { using namespace sqlite_orm; return make_storage(path, make_table("COMPANY", @@ -28,7 +28,7 @@ auto initStorage(const std::string &path) { } using Storage = decltype(initStorage("")); -int main(int, char **) { +int main(int, char**) { Storage storage = initStorage("distinct.sqlite"); @@ -117,7 +117,7 @@ int main(int, char **) { auto pureNames = storage.select(&Employee::name); cout << "NAME" << endl; cout << "----------" << endl; - for(auto &name: pureNames) { + for(auto& name: pureNames) { cout << name << endl; } cout << endl; @@ -127,7 +127,7 @@ int main(int, char **) { auto distinctNames = storage.select(distinct(&Employee::name)); cout << "NAME" << endl; cout << "----------" << endl; - for(auto &name: distinctNames) { + for(auto& name: distinctNames) { cout << name << endl; } cout << endl; @@ -136,7 +136,7 @@ int main(int, char **) { auto severalColumns = storage.select(distinct(columns(&Employee::address, &Employee::name))); cout << "ADDRESS" << '\t' << "NAME" << endl; cout << "----------" << endl; - for(auto &t: severalColumns) { + for(auto& t: severalColumns) { cout << std::get<0>(t) << '\t' << std::get<1>(t) << endl; } return 0; diff --git a/examples/enum_binding.cpp b/examples/enum_binding.cpp index 160dc21f0..e8164f730 100644 --- a/examples/enum_binding.cpp +++ b/examples/enum_binding.cpp @@ -46,7 +46,7 @@ std::string GenderToString(Gender gender) { * that's why I placed it separatedly. You can use any transformation type/form * (for example BETTER_ENUM https://github.com/aantron/better-enums) */ -std::unique_ptr GenderFromString(const std::string &s) { +std::unique_ptr GenderFromString(const std::string& s) { if(s == "female") { return std::make_unique(Gender::Female); } else if(s == "male") { @@ -83,7 +83,7 @@ namespace sqlite_orm { template<> struct statement_binder { - int bind(sqlite3_stmt *stmt, int index, const Gender &value) { + int bind(sqlite3_stmt* stmt, int index, const Gender& value) { return statement_binder().bind(stmt, index, GenderToString(value)); // or return sqlite3_bind_text(stmt, index++, GenderToString(value).c_str(), -1, SQLITE_TRANSIENT); } @@ -95,7 +95,7 @@ namespace sqlite_orm { */ template<> struct field_printer { - std::string operator()(const Gender &t) const { + std::string operator()(const Gender& t) const { return GenderToString(t); } }; @@ -108,7 +108,7 @@ namespace sqlite_orm { */ template<> struct row_extractor { - Gender extract(const char *row_value) { + Gender extract(const char* row_value) { if(auto gender = GenderFromString(row_value)) { return *gender; } else { @@ -116,14 +116,14 @@ namespace sqlite_orm { } } - Gender extract(sqlite3_stmt *stmt, int columnIndex) { + Gender extract(sqlite3_stmt* stmt, int columnIndex) { auto str = sqlite3_column_text(stmt, columnIndex); - return this->extract((const char *)str); + return this->extract((const char*)str); } }; } -int main(int /* argc*/, char ** /*argv*/) { +int main(int /* argc*/, char** /*argv*/) { using namespace sqlite_orm; auto storage = make_storage("", make_table("superheros", @@ -150,7 +150,7 @@ int main(int /* argc*/, char ** /*argv*/) { // print all superheros cout << "allSuperHeros = " << allSuperHeros.size() << endl; - for(auto &superHero: allSuperHeros) { + for(auto& superHero: allSuperHeros) { cout << storage.dump(superHero) << endl; } @@ -161,7 +161,7 @@ int main(int /* argc*/, char ** /*argv*/) { auto males = storage.get_all(where(c(&SuperHero::gender) == Gender::Male)); cout << "males = " << males.size() << endl; assert(males.size() == 2); - for(auto &superHero: males) { + for(auto& superHero: males) { cout << storage.dump(superHero) << endl; } @@ -169,7 +169,7 @@ int main(int /* argc*/, char ** /*argv*/) { auto females = storage.get_all(where(c(&SuperHero::gender) == Gender::Female)); assert(females.size() == 1); cout << "females = " << females.size() << endl; - for(auto &superHero: females) { + for(auto& superHero: females) { cout << storage.dump(superHero) << endl; } diff --git a/examples/exists.cpp b/examples/exists.cpp index 6ebeeb3fa..35e947638 100644 --- a/examples/exists.cpp +++ b/examples/exists.cpp @@ -41,7 +41,7 @@ struct Order { std::string agentCode; }; -int main(int, char **) { +int main(int, char**) { using namespace sqlite_orm; auto storage = make_storage("exists.sqlite", @@ -421,7 +421,7 @@ int main(int, char **) { order_by(&Agent::comission)); cout << "AGENT_CODE AGENT_NAME WORKING_AREA COMMISSION" << endl; cout << "---------- ---------------------------------------- ------------ ----------" << endl; - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) << endl; } @@ -443,7 +443,7 @@ int main(int, char **) { having(greater_than(count(), 2)))))); cout << "CUST_CODE CUST_NAME CUST_CITY GRADE" << endl; cout << "---------- ---------- ----------------------------------- ----------" << endl; - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) << endl; } @@ -459,7 +459,7 @@ int main(int, char **) { columns(&Order::agentCode, &Order::num, &Order::amount, &Order::custCode), where(not exists(select(&Customer::agentCode, where(is_equal(&Customer::paymentAmt, 1400)))))); cout << "AGENT_CODE ORD_NUM ORD_AMOUNT CUST_CODE" << endl; - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) << endl; } diff --git a/examples/fetch_content/src/main.cpp b/examples/fetch_content/src/main.cpp index e39a410d3..8b8be1ff1 100644 --- a/examples/fetch_content/src/main.cpp +++ b/examples/fetch_content/src/main.cpp @@ -14,7 +14,7 @@ struct RapArtist { std::string name; }; -int main(int, char **) { +int main(int, char**) { auto storage = make_storage(":memory:", make_table("rap_artists", diff --git a/examples/find_package/src/main.cpp b/examples/find_package/src/main.cpp index e39a410d3..8b8be1ff1 100644 --- a/examples/find_package/src/main.cpp +++ b/examples/find_package/src/main.cpp @@ -14,7 +14,7 @@ struct RapArtist { std::string name; }; -int main(int, char **) { +int main(int, char**) { auto storage = make_storage(":memory:", make_table("rap_artists", diff --git a/examples/foreign_key.cpp b/examples/foreign_key.cpp index 830ccf4b9..b8bb85051 100644 --- a/examples/foreign_key.cpp +++ b/examples/foreign_key.cpp @@ -23,7 +23,7 @@ struct Track { std::unique_ptr trackArtist; // must map to &Artist::artistId }; -int main(int, char **argv) { +int main(int, char** argv) { cout << "path = " << argv[0] << endl; using namespace sqlite_orm; @@ -38,7 +38,7 @@ int main(int, char **argv) { make_column("trackartist", &Track::trackArtist), foreign_key(&Track::trackArtist).references(&Artist::artistId))); auto syncSchemaRes = storage.sync_schema(); - for(auto &p: syncSchemaRes) { + for(auto& p: syncSchemaRes) { cout << p.first << " " << p.second << endl; } @@ -57,7 +57,7 @@ int main(int, char **argv) { // does not correspond to row in the artist table. storage.replace(Track{14, "Mr. Bojangles", std::make_unique(3)}); assert(0); - } catch(const std::system_error &e) { + } catch(const std::system_error& e) { cout << e.what() << endl; } @@ -72,7 +72,7 @@ int main(int, char **argv) { storage.update_all(set(assign(&Track::trackArtist, 3)), where(is_equal(&Track::trackName, "Mr. Bojangles"))); assert(0); - } catch(const std::system_error &e) { + } catch(const std::system_error& e) { cout << e.what() << endl; } @@ -92,7 +92,7 @@ int main(int, char **argv) { // the track table contains a row that refer to it. storage.remove_all(where(is_equal(&Artist::artistName, "Frank Sinatra"))); assert(0); - } catch(const std::system_error &e) { + } catch(const std::system_error& e) { cout << e.what() << endl; } @@ -106,7 +106,7 @@ int main(int, char **argv) { // exists records in the track table that refer to it. storage.update_all(set(assign(&Artist::artistId, 4)), where(is_equal(&Artist::artistName, "Dean Martin"))); assert(0); - } catch(const std::system_error &e) { + } catch(const std::system_error& e) { cout << e.what() << endl; } @@ -127,7 +127,7 @@ int main(int, char **argv) { make_column("trackartist", &Track::trackArtist), foreign_key(&Track::trackArtist).references(&Artist::artistId).on_update.cascade())); auto syncSchemaRes = storage.sync_schema(); - for(auto &p: syncSchemaRes) { + for(auto& p: syncSchemaRes) { cout << p.first << " " << p.second << endl; } @@ -150,13 +150,13 @@ int main(int, char **argv) { storage.update_all(set(c(&Artist::artistId) = 100), where(c(&Artist::artistName) == "Dean Martin")); cout << "artists:" << endl; - for(auto &artist: storage.iterate()) { + for(auto& artist: storage.iterate()) { cout << artist.artistId << '\t' << artist.artistName << endl; } cout << endl; cout << "tracks:" << endl; - for(auto &track: storage.iterate()) { + for(auto& track: storage.iterate()) { cout << track.trackId << '\t' << track.trackName << '\t'; if(track.trackArtist) { cout << *track.trackArtist; @@ -179,7 +179,7 @@ int main(int, char **argv) { make_column("trackartist", &Track::trackArtist, default_value(0)), foreign_key(&Track::trackArtist).references(&Artist::artistId).on_delete.set_default())); auto syncSchemaRes = storage.sync_schema(); - for(auto &p: syncSchemaRes) { + for(auto& p: syncSchemaRes) { cout << p.first << " " << p.second << endl; } @@ -198,7 +198,7 @@ int main(int, char **argv) { try { storage.remove_all(where(c(&Artist::artistName) == "Sammy Davis Jr.")); assert(0); - } catch(const std::system_error &e) { + } catch(const std::system_error& e) { cout << e.what() << endl; } @@ -211,13 +211,13 @@ int main(int, char **argv) { storage.remove_all(where(c(&Artist::artistName) == "Sammy Davis Jr.")); cout << "artists:" << endl; - for(auto &artist: storage.iterate()) { + for(auto& artist: storage.iterate()) { cout << artist.artistId << '\t' << artist.artistName << endl; } cout << endl; cout << "tracks:" << endl; - for(auto &track: storage.iterate()) { + for(auto& track: storage.iterate()) { cout << track.trackId << '\t' << track.trackName << '\t'; if(track.trackArtist) { cout << *track.trackArtist; diff --git a/examples/group_by.cpp b/examples/group_by.cpp index b56d883df..8be912278 100644 --- a/examples/group_by.cpp +++ b/examples/group_by.cpp @@ -17,7 +17,7 @@ struct Employee { double salary; }; -int main(int, char **) { +int main(int, char**) { using namespace sqlite_orm; auto storage = make_storage("group_by.sqlite", @@ -43,7 +43,7 @@ int main(int, char **) { // FROM COMPANY // GROUP BY NAME; auto salaryName = storage.select(columns(&Employee::name, sum(&Employee::salary)), group_by(&Employee::name)); - for(auto &t: salaryName) { + for(auto& t: salaryName) { cout << std::get<0>(t) << '\t' << *std::get<1>(t) << endl; } @@ -55,7 +55,7 @@ int main(int, char **) { cout << endl << "Now, our table has the following records with duplicate names:" << endl << endl; - for(auto &employee: storage.iterate()) { + for(auto& employee: storage.iterate()) { cout << storage.dump(employee) << endl; } @@ -65,7 +65,7 @@ int main(int, char **) { salaryName = storage.select(columns(&Employee::name, sum(&Employee::salary)), group_by(&Employee::name), order_by(&Employee::name)); - for(auto &t: salaryName) { + for(auto& t: salaryName) { cout << std::get<0>(t) << '\t' << *std::get<1>(t) << endl; } @@ -73,7 +73,7 @@ int main(int, char **) { salaryName = storage.select(columns(&Employee::name, sum(&Employee::salary)), group_by(&Employee::name), order_by(&Employee::name).desc()); - for(auto &t: salaryName) { + for(auto& t: salaryName) { cout << std::get<0>(t) << '\t' << *std::get<1>(t) << endl; } diff --git a/examples/having.cpp b/examples/having.cpp index e32eb8789..82f7c395f 100644 --- a/examples/having.cpp +++ b/examples/having.cpp @@ -49,7 +49,7 @@ int main() { // HAVING count(name) < 2; auto rows = storage.get_all(group_by(&Employee::name), having(lesser_than(count(&Employee::name), 2))); - for(auto &employee: rows) { + for(auto& employee: rows) { cout << storage.dump(employee) << endl; } cout << endl; @@ -61,7 +61,7 @@ int main() { // HAVING count(name) > 2; auto rows = storage.get_all(group_by(&Employee::name), having(greater_than(count(&Employee::name), 2))); - for(auto &employee: rows) { + for(auto& employee: rows) { cout << storage.dump(employee) << endl; } cout << endl; diff --git a/examples/in_memory.cpp b/examples/in_memory.cpp index f3f534aff..5799f967a 100644 --- a/examples/in_memory.cpp +++ b/examples/in_memory.cpp @@ -14,7 +14,7 @@ struct RapArtist { std::string name; }; -int main(int, char **) { +int main(int, char**) { auto storage = make_storage(":memory:", make_table("rap_artists", diff --git a/examples/index.cpp b/examples/index.cpp index fd45ec6a1..709af0a17 100644 --- a/examples/index.cpp +++ b/examples/index.cpp @@ -26,7 +26,7 @@ auto storage = make_column("last_name", &Contract::lastName), make_column("email", &Contract::email))); -int main(int, char **) { +int main(int, char**) { storage.sync_schema(); storage.remove_all(); @@ -42,7 +42,7 @@ int main(int, char **) { "Doe", "john.doe@sqlitetutorial.net", }); - } catch(const std::system_error &e) { + } catch(const std::system_error& e) { cout << e.what() << endl; } std::vector moreContracts = { diff --git a/examples/insert.cpp b/examples/insert.cpp index f29bb899c..742e4d76e 100644 --- a/examples/insert.cpp +++ b/examples/insert.cpp @@ -1,7 +1,7 @@ /** * This code is an implementation of this article https://www.tutorialspoint.com/sqlite/sqlite_insert_query.htm * with C++ specific features like creating object and inserting within - * single line and separatedly, inserting a subclass object and inserting a vector. + * single line and separately, inserting a subclass object and inserting a vector. */ #include @@ -23,7 +23,7 @@ struct DetailedEmployee : public Employee { std::string birthDate; }; -int main(int, char **) { +int main(int, char**) { using namespace sqlite_orm; auto storage = make_storage("insert.sqlite", @@ -90,7 +90,7 @@ int main(int, char **) { // and closes database. So triple insert will open and close the db three times. // Transaction openes and closes the db only once. storage.transaction([&] { - for(auto &employee: otherEmployees) { + for(auto& employee: otherEmployees) { storage.insert(employee); } return true; // commit @@ -106,9 +106,27 @@ int main(int, char **) { james.id = storage.insert(james); cout << "---------------------" << endl; - for(auto &employee: storage.iterate()) { + for(auto& employee: storage.iterate()) { cout << storage.dump(employee) << endl; } + // INSERT INTO COMPANY(ID, NAME, AGE, ADDRESS, SALARY) + // VALUES (3, 'Sofia', 26, 'Madrid', 15000.0) + // (4, 'Doja', 26, 'LA', 25000.0) + // ON CONFLICT(ID) DO UPDATE SET NAME = excluded.NAME, + // AGE = excluded.AGE, + // ADDRESS = excluded.ADDRESS, + // SALARY = excluded.SALARY + + storage.insert( + into(), + columns(&Employee::id, &Employee::name, &Employee::age, &Employee::address, &Employee::salary), + values(std::make_tuple(3, "Sofia", 26, "Madrid", 15000.0), std::make_tuple(4, "Doja", 26, "LA", 25000.0)), + on_conflict(&Employee::id) + .do_update(set(c(&Employee::name) = excluded(&Employee::name), + c(&Employee::age) = excluded(&Employee::age), + c(&Employee::address) = excluded(&Employee::address), + c(&Employee::salary) = excluded(&Employee::salary)))); + return 0; } diff --git a/examples/iteration.cpp b/examples/iteration.cpp index 4aa4aeb28..f7bc9ec23 100644 --- a/examples/iteration.cpp +++ b/examples/iteration.cpp @@ -12,7 +12,7 @@ struct MarvelHero { std::string abilities; }; -int main(int, char **) { +int main(int, char**) { using namespace sqlite_orm; auto storage = make_storage("iteration.sqlite", make_table("marvel", @@ -40,19 +40,19 @@ int main(int, char **) { // iterate through heros - iteration takes less memory than `get_all` because // iteration fetches row by row once it is needed. If you break at any iteration // statement will be cleared without fetching remaining rows. - for(auto &hero: storage.iterate()) { + for(auto& hero: storage.iterate()) { cout << "hero = " << storage.dump(hero) << endl; } cout << "====" << endl; // one can iterate with custom WHERE conditions.. - for(auto &hero: storage.iterate(where(c(&MarvelHero::name) == "Thor"))) { + for(auto& hero: storage.iterate(where(c(&MarvelHero::name) == "Thor"))) { cout << "hero = " << storage.dump(hero) << endl; } cout << "Heros with LENGTH(name) < 6 :" << endl; - for(auto &hero: storage.iterate(where(length(&MarvelHero::name) < 6))) { + for(auto& hero: storage.iterate(where(length(&MarvelHero::name) < 6))) { cout << "hero = " << storage.dump(hero) << endl; } diff --git a/examples/key_value.cpp b/examples/key_value.cpp index 238694f59..7658fe1e4 100644 --- a/examples/key_value.cpp +++ b/examples/key_value.cpp @@ -29,7 +29,7 @@ struct KeyValue { std::string value; }; -auto &getStorage() { +auto& getStorage() { using namespace sqlite_orm; static auto storage = make_storage("key_value_example.sqlite", make_table("key_value", @@ -38,13 +38,13 @@ auto &getStorage() { return storage; } -void setValue(const std::string &key, const std::string &value) { +void setValue(const std::string& key, const std::string& value) { using namespace sqlite_orm; KeyValue kv{key, value}; getStorage().replace(kv); } -std::string getValue(const std::string &key) { +std::string getValue(const std::string& key) { using namespace sqlite_orm; if(auto kv = getStorage().get_pointer(key)) { return kv->value; @@ -57,7 +57,7 @@ int storedKeysCount() { return getStorage().count(); } -int main(int, char **argv) { +int main(int, char** argv) { cout << argv[0] << endl; // to know executable path in case if you need to access sqlite directly from sqlite client diff --git a/examples/left_and_inner_join.cpp b/examples/left_and_inner_join.cpp index 8004a84e4..6f5e90a34 100644 --- a/examples/left_and_inner_join.cpp +++ b/examples/left_and_inner_join.cpp @@ -35,7 +35,7 @@ struct Track { double unitPrice; }; -inline auto initStorage(const std::string &path) { +inline auto initStorage(const std::string& path) { using namespace sqlite_orm; return make_storage(path, make_table("artists", @@ -57,7 +57,7 @@ inline auto initStorage(const std::string &path) { make_column("UnitPrice", &Track::unitPrice))); } -int main(int, char **) { +int main(int, char**) { auto storage = initStorage("chinook.db"); @@ -75,15 +75,15 @@ int main(int, char **) { left_join(on(c(&Album::artistId) == &Artist::artistId)), order_by(&Album::albumId)); cout << "rows count = " << rows.size() << endl; - for(auto &row: rows) { - auto &artistId = std::get<0>(row); + for(auto& row: rows) { + auto& artistId = std::get<0>(row); if(artistId) { cout << *artistId; } else { cout << "null"; } cout << '\t'; - auto &albumId = std::get<1>(row); + auto& albumId = std::get<1>(row); if(albumId) { cout << *albumId; } else { @@ -106,15 +106,15 @@ int main(int, char **) { left_join(on(c(&Album::artistId) == &Artist::artistId)), where(is_null(&Album::albumId))); cout << "rows count = " << rows.size() << endl; - for(auto &row: rows) { - auto &artistId = std::get<0>(row); + for(auto& row: rows) { + auto& artistId = std::get<0>(row); if(artistId) { cout << *artistId; } else { cout << "null"; } cout << '\t'; - auto &albumId = std::get<1>(row); + auto& albumId = std::get<1>(row); if(albumId) { cout << *albumId; } else { @@ -135,7 +135,7 @@ int main(int, char **) { auto innerJoinRows0 = storage.select(columns(&Track::trackId, &Track::name, &Album::title), inner_join(on(c(&Track::albumId) == &Album::albumId))); cout << "innerJoinRows0 count = " << innerJoinRows0.size() << endl; - for(auto &row: innerJoinRows0) { + for(auto& row: innerJoinRows0) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t'; if(std::get<2>(row)) { cout << *std::get<2>(row); @@ -159,7 +159,7 @@ int main(int, char **) { storage.select(columns(&Track::trackId, &Track::name, &Track::albumId, &Album::albumId, &Album::title), inner_join(on(c(&Album::albumId) == &Track::trackId))); cout << "innerJoinRows1 count = " << innerJoinRows1.size() << endl; - for(auto &row: innerJoinRows1) { + for(auto& row: innerJoinRows1) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t'; if(std::get<2>(row)) { cout << *std::get<2>(row); @@ -196,7 +196,7 @@ int main(int, char **) { inner_join(on(c(&Album::albumId) == &Track::albumId)), inner_join(on(c(&Artist::artistId) == &Album::artistId))); cout << "innerJoinRows2 count = " << innerJoinRows2.size() << endl; - for(auto &row: innerJoinRows2) { + for(auto& row: innerJoinRows2) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t'; if(std::get<2>(row)) { cout << *std::get<2>(row); @@ -293,7 +293,7 @@ int main(int, char **) { // ON a.doctor_id=c.doctor_id; auto rows = storage2.select(columns(&Doctor::id, &Doctor::name, &Visit::patientName, &Visit::vdate), left_join(on(c(&Doctor::id) == &Visit::doctorId))); - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) << endl; } @@ -306,7 +306,7 @@ int main(int, char **) { // ON a.doctor_id=c.doctor_id; rows = storage2.select(columns(&Doctor::id, &Doctor::name, &Visit::patientName, &Visit::vdate), join(on(c(&Doctor::id) == &Visit::doctorId))); - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) << endl; } @@ -319,7 +319,7 @@ int main(int, char **) { // USING(doctor_id); rows = storage2.select(columns(&Doctor::id, &Doctor::name, &Visit::patientName, &Visit::vdate), left_join(using_(&Visit::doctorId))); // or using_(&Doctor::id) - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) << endl; } diff --git a/examples/multi_table_select.cpp b/examples/multi_table_select.cpp index c9d1ca729..e3c1d2700 100644 --- a/examples/multi_table_select.cpp +++ b/examples/multi_table_select.cpp @@ -23,7 +23,7 @@ struct ReqDetail { double itemCost; }; -int main(int, char **) { +int main(int, char**) { using namespace sqlite_orm; auto storage = make_storage("multi_table_select.sqlite", @@ -62,7 +62,7 @@ int main(int, char **) { auto rows = storage.select( columns(&ReqEquip::reqNumber, &ReqEquip::requestor, &ReqDetail::quantity, &ReqDetail::stockNumber), where(c(&ReqEquip::reqNumber) == &ReqDetail::reqNumber)); - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t'; cout << std::get<1>(row) << '\t'; cout << std::get<2>(row) << '\t'; @@ -79,7 +79,7 @@ int main(int, char **) { rows = storage.select( columns(&ReqEquip::reqNumber, &ReqEquip::requestor, &ReqDetail::quantity, &ReqDetail::stockNumber), where(c(&ReqEquip::reqNumber) == &ReqDetail::reqNumber and c(&ReqEquip::reqNumber) == 1000)); - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t'; cout << std::get<1>(row) << '\t'; cout << std::get<2>(row) << '\t'; diff --git a/examples/natural_join.cpp b/examples/natural_join.cpp index dcfe37e05..f66ca2b65 100644 --- a/examples/natural_join.cpp +++ b/examples/natural_join.cpp @@ -74,7 +74,7 @@ int main() { natural_join(), where(c(&Doctor::degree) == "MD")); cout << "rows count = " << rows.size() << endl; - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) << '\t' << std::get<4>(row) << endl; } @@ -96,7 +96,7 @@ int main() { natural_join(), where(c(&Doctor::degree) == "MD")); cout << "rows count = " << rows.size() << endl; - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) << '\t' << std::get<4>(row) << '\t' << std::get<5>(row) << endl; } diff --git a/examples/nullable_enum_binding.cpp b/examples/nullable_enum_binding.cpp index 24c0db618..46d360cc8 100644 --- a/examples/nullable_enum_binding.cpp +++ b/examples/nullable_enum_binding.cpp @@ -31,7 +31,7 @@ std::unique_ptr GenderToString(Gender gender) { throw std::domain_error("Invalid Gender enum"); } -std::unique_ptr GenderFromString(const std::string &s) { +std::unique_ptr GenderFromString(const std::string& s) { if(s == "female") { return std::make_unique(Gender::Female); } else if(s == "male") { @@ -54,7 +54,7 @@ namespace sqlite_orm { template<> struct statement_binder { - int bind(sqlite3_stmt *stmt, int index, const Gender &value) { + int bind(sqlite3_stmt* stmt, int index, const Gender& value) { if(auto str = GenderToString(value)) { return statement_binder().bind(stmt, index, *str); } else { @@ -65,7 +65,7 @@ namespace sqlite_orm { template<> struct field_printer { - std::string operator()(const Gender &t) const { + std::string operator()(const Gender& t) const { if(auto res = GenderToString(t)) { return *res; } else { @@ -76,7 +76,7 @@ namespace sqlite_orm { template<> struct row_extractor { - Gender extract(const char *row_value) { + Gender extract(const char* row_value) { if(row_value) { if(auto gender = GenderFromString(row_value)) { return *gender; @@ -88,9 +88,9 @@ namespace sqlite_orm { } } - Gender extract(sqlite3_stmt *stmt, int columnIndex) { + Gender extract(sqlite3_stmt* stmt, int columnIndex) { auto str = sqlite3_column_text(stmt, columnIndex); - return this->extract((const char *)str); + return this->extract((const char*)str); } }; @@ -102,13 +102,13 @@ namespace sqlite_orm { struct type_is_nullable : public std::true_type { // this function must return whether value null or not (false is null). Don't forget to implement it - bool operator()(const Gender &g) const { + bool operator()(const Gender& g) const { return g != Gender::None; } }; } -int main(int, char **) { +int main(int, char**) { using namespace sqlite_orm; auto storage = make_storage("nullable_enum.sqlite", make_table("users", @@ -135,19 +135,19 @@ int main(int, char **) { }); cout << "All users :" << endl; - for(auto &user: storage.iterate()) { + for(auto& user: storage.iterate()) { cout << storage.dump(user) << endl; } auto allWithNoneGender = storage.get_all(where(is_null(&User::gender))); cout << "allWithNoneGender = " << allWithNoneGender.size() << endl; - for(auto &user: allWithNoneGender) { + for(auto& user: allWithNoneGender) { cout << storage.dump(user) << endl; } auto allWithGender = storage.get_all(where(is_not_null(&User::gender))); cout << "allWithGender = " << allWithGender.size() << endl; - for(auto &user: allWithGender) { + for(auto& user: allWithGender) { cout << storage.dump(user) << endl; } diff --git a/examples/prepared_statement.cpp b/examples/prepared_statement.cpp index f6121cfdb..0ac8c7648 100644 --- a/examples/prepared_statement.cpp +++ b/examples/prepared_statement.cpp @@ -70,7 +70,7 @@ int main() { // now 'doctors' table has two rows. // Next we shall reuse the statement again with member assignment - auto &doctor = get<0>(replaceStatement); // doctor is Doctor & + auto& doctor = get<0>(replaceStatement); // doctor is Doctor & doctor.doctor_id = 212; doctor.doctor_name = "Dr. Ke Gee"; doctor.degree = "MD"; @@ -99,7 +99,7 @@ int main() { } cout << "Doctors count = " << storage.count() << endl; - for(auto &doctor: storage.iterate()) { + for(auto& doctor: storage.iterate()) { cout << storage.dump(doctor) << endl; } @@ -116,7 +116,7 @@ int main() { } cout << "Specialities count = " << storage.count() << endl; - for(auto &speciality: storage.iterate()) { + for(auto& speciality: storage.iterate()) { cout << storage.dump(speciality) << endl; } { @@ -147,7 +147,7 @@ int main() { storage.replace(Visit{212, "Jason Mallin", "2013-10-12"}); } cout << "Visits count = " << storage.count() << endl; - for(auto &visit: storage.iterate()) { + for(auto& visit: storage.iterate()) { cout << storage.dump(visit) << endl; } { @@ -159,7 +159,7 @@ int main() { { auto rows = storage.execute(selectStatement); cout << "rows count = " << rows.size() << endl; - for(auto &id: rows) { + for(auto& id: rows) { cout << id << endl; } } @@ -172,7 +172,7 @@ int main() { get<0>(selectStatement) = 11; auto rows = storage.execute(selectStatement); cout << "rows count = " << rows.size() << endl; - for(auto &id: rows) { + for(auto& id: rows) { cout << id << endl; } } @@ -187,7 +187,7 @@ int main() { { auto rows = storage.execute(selectStatement); cout << "rows count = " << rows.size() << endl; - for(auto &row: rows) { + for(auto& row: rows) { cout << get<0>(row) << '\t' << get<1>(row) << endl; } } @@ -199,7 +199,7 @@ int main() { { auto rows = storage.execute(selectStatement); cout << "rows count = " << rows.size() << endl; - for(auto &row: rows) { + for(auto& row: rows) { cout << get<0>(row) << '\t' << get<1>(row) << endl; } } diff --git a/examples/private_class_members.cpp b/examples/private_class_members.cpp index 779f14269..00a4294ef 100644 --- a/examples/private_class_members.cpp +++ b/examples/private_class_members.cpp @@ -43,7 +43,7 @@ class Player { } }; -int main(int, char **) { +int main(int, char**) { using namespace sqlite_orm; auto storage = make_storage( "private.sqlite", @@ -81,7 +81,7 @@ int main(int, char **) { auto somePlayers = storage.get_all(where(lesser_than(length(&Player::getName), 5))); cout << "players with length(name) < 5 = " << somePlayers.size() << endl; assert(somePlayers.size() == 1); - for(auto &player: somePlayers) { + for(auto& player: somePlayers) { cout << storage.dump(player) << endl; } } diff --git a/examples/select.cpp b/examples/select.cpp index 02b4c28af..d8632e5e0 100644 --- a/examples/select.cpp +++ b/examples/select.cpp @@ -19,7 +19,7 @@ using namespace sqlite_orm; using std::cout; using std::endl; -int main(int, char **) { +int main(int, char**) { auto storage = make_storage("select.sqlite", make_table("COMPANY", make_column("ID", &Employee::id, primary_key()), @@ -66,7 +66,7 @@ int main(int, char **) { // now let's select id, name and salary.. auto idsNamesSalarys = storage.select(columns(&Employee::id, &Employee::name, &Employee::salary)); // decltype(idsNamesSalarys) = vector>> - for(auto &tpl: idsNamesSalarys) { + for(auto& tpl: idsNamesSalarys) { cout << "id = " << std::get<0>(tpl) << ", name = " << std::get<1>(tpl) << ", salary = "; if(std::get<2>(tpl)) { cout << *std::get<2>(tpl); @@ -80,16 +80,16 @@ int main(int, char **) { auto allEmployeesTuples = storage.select(asterisk()); cout << "allEmployeesTuples count = " << allEmployeesTuples.size() << endl; - for(auto &row: allEmployeesTuples) { // row is std::tuple, + for(auto& row: allEmployeesTuples) { // row is std::tuple, // std::unique_ptr> cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t'; - if(auto &value = std::get<3>(row)) { + if(auto& value = std::get<3>(row)) { cout << *value; } else { cout << "null"; } cout << '\t'; - if(auto &value = std::get<4>(row)) { + if(auto& value = std::get<4>(row)) { cout << *value; } else { cout << "null"; diff --git a/examples/self_join.cpp b/examples/self_join.cpp index c450b78df..51fc6d950 100644 --- a/examples/self_join.cpp +++ b/examples/self_join.cpp @@ -36,7 +36,7 @@ template struct custom_alias : sqlite_orm::alias_tag { using type = T; - static const std::string &get() { + static const std::string& get() { static const std::string res = "emp"; return res; } @@ -205,7 +205,7 @@ int main() { c(&Employee::firstName) || " " || c(&Employee::lastName)), inner_join(on(alias_column(&Employee::reportsTo) == c(&Employee::employeeId)))); cout << "firstNames count = " << firstNames.size() << endl; - for(auto &row: firstNames) { + for(auto& row: firstNames) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << endl; } @@ -225,7 +225,7 @@ int main() { c(&Employee::firstName) || " " || c(&Employee::lastName)), inner_join(on(alias_column(&Employee::reportsTo) == c(&Employee::employeeId)))); cout << "firstNames count = " << firstNames.size() << endl; - for(auto &row: firstNames) { + for(auto& row: firstNames) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << endl; } } diff --git a/examples/subentities.cpp b/examples/subentities.cpp index cfb5fb863..c27d67c38 100644 --- a/examples/subentities.cpp +++ b/examples/subentities.cpp @@ -42,7 +42,7 @@ auto storage = make_table("marks", make_column("mark", &Mark::value), make_column("student_id", &Mark::student_id))); // inserts or updates student and does the same with marks -int addStudent(const Student &student) { +int addStudent(const Student& student) { auto studentId = student.id; if(storage.count(where(c(&Student::id) == student.id))) { storage.update(student); @@ -52,7 +52,7 @@ int addStudent(const Student &student) { // insert all marks within a transaction storage.transaction([&] { storage.remove_all(where(c(&Mark::student_id) == studentId)); - for(auto &mark: student.marks) { + for(auto& mark: student.marks) { storage.insert(Mark{mark, studentId}); } return true; @@ -71,7 +71,7 @@ Student getStudent(int studentId) { return res; // must be moved automatically by compiler } -int main(int, char **) { +int main(int, char**) { decltype(Student::id) mikeId; decltype(Student::id) annaId; @@ -97,7 +97,7 @@ int main(int, char **) { auto mike = getStudent(mikeId); cout << "mike = " << storage.dump(mike) << endl; cout << "mike.marks = "; - for(auto &m: mike.marks) { + for(auto& m: mike.marks) { cout << m << " "; } cout << endl; @@ -105,7 +105,7 @@ int main(int, char **) { auto anna = getStudent(annaId); cout << "anna = " << storage.dump(anna) << endl; cout << "anna.marks = "; - for(auto &m: anna.marks) { + for(auto& m: anna.marks) { cout << m << " "; } cout << endl; diff --git a/examples/subquery.cpp b/examples/subquery.cpp index 76d49d4d2..4bc5f5d61 100644 --- a/examples/subquery.cpp +++ b/examples/subquery.cpp @@ -38,7 +38,7 @@ struct JobHistory { decltype(Employee::departmentId) departmentId; }; -int main(int, char **) { +int main(int, char**) { using namespace sqlite_orm; auto storage = make_storage("subquery.sqlite", make_table("employees", @@ -1288,7 +1288,7 @@ int main(int, char **) { select(&Employee::salary, where(is_equal(&Employee::firstName, "Alexander")))))); cout << "first_name last_name salary" << endl; cout << "---------- ---------- ----------" << endl; - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << endl; } } @@ -1300,7 +1300,7 @@ int main(int, char **) { where(greater_than(&Employee::salary, select(avg(&Employee::salary))))); cout << "employee_id first_name last_name salary" << endl; cout << "----------- ---------- ---------- ----------" << endl; - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) << endl; } @@ -1316,7 +1316,7 @@ int main(int, char **) { where(in(&Employee::departmentId, select(&Department::id, where(c(&Department::locationId) == 1700))))); cout << "first_name last_name department_id" << endl; cout << "---------- ---------- -------------" << endl; - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << endl; } } @@ -1334,7 +1334,7 @@ int main(int, char **) { select(&Department::id, where(between(&Department::managerId, 100, 200)))))); cout << "first_name last_name department_id" << endl; cout << "---------- ---------- -------------" << endl; - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << endl; } } @@ -1355,7 +1355,7 @@ int main(int, char **) { where(is_equal(&Employee::departmentId, alias_column(&Employee::departmentId))))))); cout << "last_name salary department_id" << endl; cout << "---------- ---------- -------------" << endl; - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << endl; } } @@ -1372,7 +1372,7 @@ int main(int, char **) { select(count(), where(is_equal(&Employee::id, &JobHistory::employeeId)))))); cout << "first_name last_name employee_id job_id" << endl; cout << "---------- ---------- ----------- ----------" << endl; - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) << endl; } diff --git a/examples/synchronous.cpp b/examples/synchronous.cpp index d10da13dd..6147efff2 100644 --- a/examples/synchronous.cpp +++ b/examples/synchronous.cpp @@ -12,7 +12,7 @@ struct Query { uint16_t type; }; -int main(int, char **) { +int main(int, char**) { using namespace sqlite_orm; diff --git a/examples/union.cpp b/examples/union.cpp index 7a2704874..4c5b588de 100644 --- a/examples/union.cpp +++ b/examples/union.cpp @@ -80,10 +80,10 @@ int main() { left_outer_join(on(is_equal(&Employee::id, &Department::employeeId)))))); assert(rows.size() == 7); - std::sort(rows.begin(), rows.end(), [](auto &lhs, auto &rhs) { + std::sort(rows.begin(), rows.end(), [](auto& lhs, auto& rhs) { return std::get<0>(lhs) < std::get<0>(rhs); }); - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << endl; } cout << endl; @@ -103,7 +103,7 @@ int main() { inner_join(on(is_equal(&Employee::id, &Department::employeeId)))), select(columns(&Department::employeeId, &Employee::name, &Department::dept), left_outer_join(on(is_equal(&Employee::id, &Department::employeeId)))))); - for(auto &row: rows) { + for(auto& row: rows) { cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << endl; } cout << endl; diff --git a/examples/unique.cpp b/examples/unique.cpp index 24b3ac81b..2a7b518ec 100644 --- a/examples/unique.cpp +++ b/examples/unique.cpp @@ -14,7 +14,7 @@ struct Entry { std::unique_ptr nullableColumn; }; -int main(int, char **) { +int main(int, char**) { using namespace sqlite_orm; auto storage = make_storage("unique.sqlite", make_table("unique_test", @@ -34,7 +34,7 @@ int main(int, char **) { auto id2 = storage.insert(Entry{0, sameString, std::make_unique("I got you")}); cout << "inserted " << storage.dump(storage.get(id2)) << endl; - } catch(const std::system_error &e) { + } catch(const std::system_error& e) { cerr << e.what() << endl; } diff --git a/examples/update.cpp b/examples/update.cpp index 727cbbedd..ed3a27342 100644 --- a/examples/update.cpp +++ b/examples/update.cpp @@ -17,7 +17,7 @@ struct Employee { double salary; }; -inline auto initStorage(const std::string &path) { +inline auto initStorage(const std::string& path) { using namespace sqlite_orm; return make_storage(path, make_table("COMPANY", @@ -32,7 +32,7 @@ using Storage = decltype(initStorage("")); static std::unique_ptr stor; -int main(int, char **) { +int main(int, char**) { stor = std::make_unique(initStorage("update.sqlite")); stor->sync_schema(); stor->remove_all(); @@ -47,7 +47,7 @@ int main(int, char **) { stor->replace(Employee{7, "James", 24, "Houston", 10000.0}); // show 'COMPANY' table contents - for(auto &employee: stor->iterate()) { + for(auto& employee: stor->iterate()) { cout << stor->dump(employee) << endl; } cout << endl; @@ -60,7 +60,7 @@ int main(int, char **) { employee6); // actually this call updates all non-primary-key columns' values to passed object's fields // show 'COMPANY' table contents again - for(auto &employee: stor->iterate()) { + for(auto& employee: stor->iterate()) { cout << stor->dump(employee) << endl; } cout << endl; @@ -71,7 +71,7 @@ int main(int, char **) { where(c(&Employee::age) < 30)); // show 'COMPANY' table contents one more time - for(auto &employee: stor->iterate()) { + for(auto& employee: stor->iterate()) { cout << stor->dump(employee) << endl; } cout << endl; diff --git a/examples/user_defined_functions.cpp b/examples/user_defined_functions.cpp new file mode 100644 index 000000000..fcc2a2b9f --- /dev/null +++ b/examples/user_defined_functions.cpp @@ -0,0 +1,166 @@ +/** + * This example was taken from here http://souptonuts.sourceforge.net/readme_sqlite_tutorial.html + */ +#include +#include + +using namespace sqlite_orm; +using std::cout; +using std::endl; + +/** + * Scalar function must be defined as a dedicated class with at least two functions: + * 1) `operator()` which takes any amount of arguments that can be obtained using row_extractor + * and returns the result of any type that can be bound using statement_binder. + * `operator()` can be both right const and non-right const. + * 2) static `name` which return a name. Return type can be any and must have operator<<(std::ostream &) overload. + * You can use `std::string`, `std::string_view` or `const char *` for example. + * Note: If you want a function to accept variadic arguments make `operator()` accepting + * a single argument of type `(const arg_values&)`. + */ +struct SignFunction { + + double operator()(double arg) const { + if(arg > 0) { + return 1; + } else if(arg < 0) { + return -1; + } else { + return 0; + } + } + + static const char *name() { + return "SIGN"; + } +}; + +/** + * Aggregate function must be defined as a dedicated class with at least three functions: + * 1) `void step(...)` which can be called 0 or more times during one call inside single SQL query (once per row). + * It can accept any number of arguments that can be obtained using row_extractor. + * 2) `fin() const` which is called once per call inside single SQL query. `fin` can have any result type + * that can be bound using statement_binder. Result of `fin` is a result of aggregate function. + * 3) static `name` which return a name. Return type can be any and must have operator<<(std::ostream &) overload. + * You can use `std::string`, `std::string_view` or `const char *` for example. + */ +struct AcceleratedSumFunction { + std::vector list; + + void step(int value) { + if(!this->list.empty()) { + auto nextValue = list.back() + value; + this->list.push_back(nextValue); + } else { + this->list.push_back(value); + } + } + + std::string fin() const { + std::stringstream ss; + ss << "("; + for(size_t i = 0; i < this->list.size(); ++i) { + ss << this->list[i]; + if(i < (this->list.size() - 1)) { + ss << ", "; + } + } + ss << ")"; + return ss.str(); + } + + /** + * `const std::string &` is also a valid type of name cause it has `operator<<(std::ostream &` overload + */ + static const std::string &name() { + static const std::string result = "ASUM"; + return result; + } +}; + +/** + * This is also a scalar function just like `SignFunction` but this function + * can accept variadic amount of arguments. The only difference is arguments list + * of `operator()`: it is `const arg_values &`. `arg_values` has STL container API. + */ +struct ArithmeticMeanFunction { + + double operator()(const arg_values &args) const { + double result = 0; + for(auto arg_value: args) { + if(arg_value.is_float()) { + result += arg_value.get(); + } else if(arg_value.is_integer()) { + result += arg_value.get(); + } + } + if(!args.empty()) { + result /= double(args.size()); + } + return result; + } + + static const std::string &name() { + static const std::string result = "ARITHMETIC_MEAN"; + return result; + } +}; + +int main() { + + struct Table { + int a = 0; + int b = 0; + int c = 0; + }; + + auto storage = make_storage( + {}, + make_table("t", make_column("a", &Table::a), make_column("b", &Table::b), make_column("c", &Table::c))); + storage.sync_schema(); + + /** + * This function can be called at any time doesn't matter whether connection is open or not. + * To delete created scalar function use `storage.delete_scalar_function()` function call. + */ + storage.create_scalar_function(); + + // SELECT SIGN(3) + auto signRows = storage.select(func(3)); + cout << "SELECT SIGN(3) = " << signRows.at(0) << endl; + + storage.insert(Table{1, -1, 2}); + storage.insert(Table{2, -2, 4}); + storage.insert(Table{3, -3, 8}); + storage.insert(Table{4, -4, 16}); + + storage.create_aggregate_function(); + + // SELECT ASUM(a), ASUM(b), ASUM(c) + // FROM t + auto aSumRows = storage.select(columns(func(&Table::a), + func(&Table::b), + func(&Table::c))); + cout << "SELECT ASUM(a), ASUM(b), ASUM(c) FROM t:" << endl; + for(auto &row: aSumRows) { + cout << '\t' << get<0>(row) << endl; + cout << '\t' << get<1>(row) << endl; + cout << '\t' << get<2>(row) << endl; + } + + storage.create_scalar_function(); + + // SELECT ARITHMETIC_MEAN(5, 6, 7) + auto arithmeticMeanRows1 = storage.select(func(5, 6, 7)); + cout << "SELECT ARITHMETIC_MEAN(5, 6, 7) = " << arithmeticMeanRows1.front() << endl; + + // SELECT ARITHMETIC_MEAN(-2, 1) + auto arithmeticMeanRows2 = storage.select(func(-2, 1)); + cout << "SELECT ARITHMETIC_MEAN(-2, 1) = " << arithmeticMeanRows2.front() << endl; + + // SELECT ARITHMETIC_MEAN(-5.5, 4, 13.2, 256.4) + auto arithmeticMeanRows3 = storage.select(func(-5.5, 4, 13.2, 256.4)); + cout << "SELECT ARITHMETIC_MEAN(-5.5, 4, 13.2, 256.4) = " << arithmeticMeanRows3.front() << endl; + + return 0; +} diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 22c493422..78c4706ea 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -18,6 +18,8 @@ __pragma(push_macro("min")) #if __cplusplus >= 201703L // use of C++17 or higher // Enables use of std::optional in SQLITE_ORM. #define SQLITE_ORM_OPTIONAL_SUPPORTED +#define SQLITE_ORM_STRING_VIEW_SUPPORTED +#define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED #endif #pragma once @@ -44,6 +46,10 @@ __pragma(push_macro("min")) failed_to_init_a_backup, unknown_member_value, incorrect_order, + cannot_use_default_value, + arguments_count_does_not_match, + function_not_found, + index_is_out_of_bounds, }; } @@ -51,7 +57,7 @@ namespace sqlite_orm { class orm_error_category : public std::error_category { public: - const char *name() const noexcept override final { + const char* name() const noexcept override final { return "ORM error"; } @@ -83,6 +89,14 @@ namespace sqlite_orm { return "Unknown member value"; case orm_error_code::incorrect_order: return "Incorrect order"; + case orm_error_code::cannot_use_default_value: + return "The statement 'INSERT INTO * DEFAULT VALUES' can be used with only one row"; + case orm_error_code::arguments_count_does_not_match: + return "Arguments count does not match"; + case orm_error_code::function_not_found: + return "Function not found"; + case orm_error_code::index_is_out_of_bounds: + return "Index is out of bounds"; default: return "unknown error"; } @@ -91,7 +105,7 @@ namespace sqlite_orm { class sqlite_error_category : public std::error_category { public: - const char *name() const noexcept override final { + const char* name() const noexcept override final { return "SQLite error"; } @@ -100,18 +114,18 @@ namespace sqlite_orm { } }; - inline const orm_error_category &get_orm_error_category() { + inline const orm_error_category& get_orm_error_category() { static orm_error_category res; return res; } - inline const sqlite_error_category &get_sqlite_error_category() { + inline const sqlite_error_category& get_sqlite_error_category() { static sqlite_error_category res; return res; } template - std::string get_error_message(sqlite3 *db, T &&... args) { + std::string get_error_message(sqlite3* db, T&&... args) { std::ostringstream stream; using unpack = int[]; static_cast(unpack{0, (static_cast(static_cast(stream << args)), 0)...}); @@ -120,7 +134,7 @@ namespace sqlite_orm { } template - [[noreturn]] void throw_error(sqlite3 *db, T &&... args) { + [[noreturn]] void throw_error(sqlite3* db, T&&... args) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), get_error_message(db, std::forward(args)...)); } @@ -136,7 +150,7 @@ namespace std { } #pragma once -#include // std::tuple, std::get +#include // std::tuple, std::get, std::tuple_element, std::tuple_size #include // std::false_type, std::true_type // #include "static_magic.h" @@ -150,27 +164,27 @@ namespace sqlite_orm { namespace internal { static inline decltype(auto) empty_callable() { - static auto res = [](auto &&...) {}; + static auto res = [](auto&&...) {}; return (res); } template - decltype(auto) static_if(std::true_type, const T &t, const F &) { + decltype(auto) static_if(std::true_type, const T& t, const F&) { return (t); } template - decltype(auto) static_if(std::false_type, const T &, const F &f) { + decltype(auto) static_if(std::false_type, const T&, const F& f) { return (f); } template - decltype(auto) static_if(const T &t, const F &f) { + decltype(auto) static_if(const T& t, const F& f) { return static_if(std::integral_constant{}, t, f); } template - decltype(auto) static_if(const T &t) { + decltype(auto) static_if(const T& t) { return static_if(std::integral_constant{}, t, empty_callable()); } @@ -184,7 +198,9 @@ namespace sqlite_orm { // got from here http://stackoverflow.com/questions/25958259/how-do-i-find-out-if-a-tuple-contains-a-type namespace tuple_helper { - + /** + * HAS_TYPE type trait + */ template struct has_type; @@ -200,62 +216,152 @@ namespace sqlite_orm { template using tuple_contains_type = typename has_type::type; + /** + * HAS_SOME_TYPE type trait + */ + template class TT, typename Tuple> + struct has_some_type; + + template class TT> + struct has_some_type> : std::false_type {}; + + template class TT, typename U, typename... Ts> + struct has_some_type> : has_some_type> {}; + + template class TT, typename T, typename... Ts> + struct has_some_type, Ts...>> : std::true_type {}; + + template class TT, typename Tuple> + using tuple_contains_some_type = typename has_some_type::type; + template - struct iterator { + struct iterator_impl { template - void operator()(const std::tuple &t, const L &l, bool reverse = true) { + void operator()(const std::tuple& tuple, const L& lambda, bool reverse = true) { if(reverse) { - l(std::get(t)); - iterator()(t, l, reverse); + lambda(std::get(tuple)); + iterator_impl()(tuple, lambda, reverse); } else { - iterator()(t, l, reverse); - l(std::get(t)); + iterator_impl()(tuple, lambda, reverse); + lambda(std::get(tuple)); } } + + template + void operator()(const L& lambda) { + iterator_impl()(lambda); + lambda((const typename std::tuple_element>::type*)nullptr); + } }; template - struct iterator<0, Args...> { + struct iterator_impl<0, Args...> { + + template + void operator()(const std::tuple& tuple, const L& lambda, bool /*reverse*/ = true) { + lambda(std::get<0>(tuple)); + } template - void operator()(const std::tuple &t, const L &l, bool /*reverse*/ = true) { - l(std::get<0>(t)); + void operator()(const L& lambda) { + lambda((const typename std::tuple_element<0, std::tuple>::type*)nullptr); } }; template - struct iterator { + struct iterator_impl { + + template + void operator()(const std::tuple<>&, const L&, bool /*reverse*/ = true) { + //.. + } + + template + void operator()(const L&) { + //.. + } + }; + + template + struct iterator_impl2; + + template<> + struct iterator_impl2<> { template - void operator()(const std::tuple<> &, const L &, bool /*reverse*/ = true) { + void operator()(const L&) const { //.. } }; + template + struct iterator_impl2 { + + template + void operator()(const L& lambda) const { + lambda((const H*)nullptr); + iterator_impl2{}(lambda); + } + }; + + template + struct iterator; + + template + struct iterator> { + + template + void operator()(const L& lambda) const { + iterator_impl2{}(lambda); + } + }; + } + + namespace internal { + + // got it form here https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer + template + auto call_impl(Function& f, FunctionPointer functionPointer, Tuple t, std::index_sequence) { + return (f.*functionPointer)(std::get(t)...); + } + + template + auto call(Function& f, FunctionPointer functionPointer, Tuple t) { + static constexpr auto size = std::tuple_size::value; + return call_impl(f, functionPointer, move(t), std::make_index_sequence{}); + } + + template + auto call(Function& f, Tuple t) { + return call(f, &Function::operator(), move(t)); + } + template - void move_tuple_impl(L &lhs, R &rhs) { + void move_tuple_impl(L& lhs, R& rhs) { std::get(lhs) = std::move(std::get(rhs)); - internal::static_if{}>([](auto &l, auto &r) { + internal::static_if{}>([](auto& l, auto& r) { move_tuple_impl(l, r); })(lhs, rhs); } - } - - namespace internal { template - void move_tuple(L &lhs, R &rhs) { + void move_tuple(L& lhs, R& rhs) { using bool_type = std::integral_constant; - static_if([](auto &l, auto &r) { - tuple_helper::move_tuple_impl(l, r); + static_if([](auto& l, auto& r) { + move_tuple_impl(l, r); })(lhs, rhs); } template - void iterate_tuple(const std::tuple &t, const L &l) { + void iterate_tuple(const std::tuple& tuple, const L& lambda) { using tuple_type = std::tuple; - tuple_helper::iterator::value - 1, Args...>()(t, l, false); + tuple_helper::iterator_impl::value - 1, Args...>()(tuple, lambda, false); + } + + template + void iterate_tuple(const L& lambda) { + tuple_helper::iterator{}(lambda); } template @@ -265,6 +371,57 @@ namespace sqlite_orm { struct conc_tuple { using type = tuple_cat_t; }; + } +} +#pragma once + +#include // std::tuple +#include // std::enable_if + +namespace sqlite_orm { + namespace internal { + + template class C, class SFINAE = void> + struct find_in_tuple; + + template class C> + struct find_in_tuple, C, void> { + using type = void; + }; + + template class C> + struct find_in_tuple, C, typename std::enable_if::value>::type> { + using type = H; + }; + + template class C> + struct find_in_tuple, C, typename std::enable_if::value>::type> { + using type = typename find_in_tuple, C>::type; + }; + } +} +#pragma once + +#include // std::tuple + +namespace sqlite_orm { + namespace internal { + + template class F> + struct tuple_transformer; + + template class F> + struct tuple_transformer, F> { + using type = std::tuple::type...>; + }; + } +} +#pragma once + +#include // std::tuple + +namespace sqlite_orm { + namespace internal { template class C> struct count_tuple; @@ -282,6 +439,41 @@ namespace sqlite_orm { } #pragma once +namespace sqlite_orm { + namespace internal { + + /** + * Accepts any number of arguments and evaluates `type` alias as T if all arguments are the same or void otherwise + */ + template + struct same_or_void { + using type = void; + }; + + template + struct same_or_void { + using type = A; + }; + + template + struct same_or_void { + using type = void; + }; + + template + struct same_or_void { + using type = A; + }; + + template + struct same_or_void { + using type = typename same_or_void::type; + }; + + } +} +#pragma once + #include // std::string #include // std::shared_ptr, std::unique_ptr #include // std::vector @@ -298,28 +490,28 @@ namespace sqlite_orm { struct type_printer; struct integer_printer { - inline const std::string &print() { + inline const std::string& print() { static const std::string res = "INTEGER"; return res; } }; struct text_printer { - inline const std::string &print() { + inline const std::string& print() { static const std::string res = "TEXT"; return res; } }; struct real_printer { - inline const std::string &print() { + inline const std::string& print() { static const std::string res = "REAL"; return res; } }; struct blob_printer { - inline const std::string &print() { + inline const std::string& print() { static const std::string res = "BLOB"; return res; } @@ -369,7 +561,7 @@ namespace sqlite_orm { struct type_printer : public text_printer {}; template<> - struct type_printer : public text_printer {}; + struct type_printer : public text_printer {}; template<> struct type_printer : public real_printer {}; @@ -413,3406 +605,5037 @@ namespace sqlite_orm { #include // std::is_base_of, std::false_type, std::true_type #include // std::ostream -namespace sqlite_orm { +// #include "table_type.h" - namespace constraints { +#include // std::enable_if, std::is_member_pointer, std::is_member_function_pointer - /** - * AUTOINCREMENT constraint class. - */ - struct autoincrement_t { +// #include "member_traits/getter_traits.h" - operator std::string() const { - return "AUTOINCREMENT"; - } - }; +// #include "getters.h" - struct primary_key_base { - enum class order_by { - unspecified, - ascending, - descending, - }; +namespace sqlite_orm { + namespace internal { - order_by asc_option = order_by::unspecified; + template + using getter_by_value_const = T (O::*)() const; - operator std::string() const { - std::string res = "PRIMARY KEY"; - switch(this->asc_option) { - case order_by::ascending: - res += " ASC"; - break; - case order_by::descending: - res += " DESC"; - break; - default: - break; - } - return res; - } - }; + template + using getter_by_value = T (O::*)(); - /** - * PRIMARY KEY constraint class. - * Cs is parameter pack which contains columns (member pointers and/or function pointers). Can be empty when - * used withen `make_column` function. - */ - template - struct primary_key_t : primary_key_base { - using order_by = primary_key_base::order_by; - using columns_tuple = std::tuple; + template + using getter_by_ref_const = T& (O::*)() const; - columns_tuple columns; + template + using getter_by_ref = T& (O::*)(); - primary_key_t(decltype(columns) c) : columns(move(c)) {} + template + using getter_by_const_ref_const = const T& (O::*)() const; - primary_key_t asc() const { - auto res = *this; - res.asc_option = order_by::ascending; - return res; - } + template + using getter_by_const_ref = const T& (O::*)(); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + using getter_by_value_const_noexcept = T (O::*)() const noexcept; - primary_key_t desc() const { - auto res = *this; - res.asc_option = order_by::descending; - return res; - } - }; + template + using getter_by_value_noexcept = T (O::*)() noexcept; - struct unique_base { - operator std::string() const { - return "UNIQUE"; - } - }; + template + using getter_by_ref_const_noexcept = T& (O::*)() const noexcept; - /** - * UNIQUE constraint class. - */ - template - struct unique_t : unique_base { - using columns_tuple = std::tuple; + template + using getter_by_ref_noexcept = T& (O::*)() noexcept; - columns_tuple columns; + template + using getter_by_const_ref_const_noexcept = const T& (O::*)() const noexcept; - unique_t(columns_tuple columns_) : columns(move(columns_)) {} - }; + template + using getter_by_const_ref_noexcept = const T& (O::*)() noexcept; +#endif + } +} + +namespace sqlite_orm { + namespace internal { - /** - * DEFAULT constraint class. - * T is a value type. - */ template - struct default_t { - using value_type = T; + struct getter_traits; - value_type value; + template + struct getter_traits> { + using object_type = O; + using field_type = T; - operator std::string() const { - return "DEFAULT"; - } + static constexpr const bool returns_lvalue = false; }; -#if SQLITE_VERSION_NUMBER >= 3006019 + template + struct getter_traits> { + using object_type = O; + using field_type = T; - /** - * FOREIGN KEY constraint class. - * Cs are columns which has foreign key - * Rs are column which C references to - * Available in SQLite 3.6.19 or higher - */ + static constexpr const bool returns_lvalue = false; + }; - template - struct foreign_key_t; + template + struct getter_traits> { + using object_type = O; + using field_type = T; - enum class foreign_key_action { - none, // not specified - no_action, - restrict_, - set_null, - set_default, - cascade, + static constexpr const bool returns_lvalue = true; }; - inline std::ostream &operator<<(std::ostream &os, foreign_key_action action) { - switch(action) { - case decltype(action)::no_action: - os << "NO ACTION"; - break; - case decltype(action)::restrict_: - os << "RESTRICT"; - break; - case decltype(action)::set_null: - os << "SET NULL"; - break; - case decltype(action)::set_default: - os << "SET DEFAULT"; - break; - case decltype(action)::cascade: - os << "CASCADE"; - break; - case decltype(action)::none: - break; - } - return os; - } - - struct on_update_delete_base { - const bool update; // true if update and false if delete + template + struct getter_traits> { + using object_type = O; + using field_type = T; - operator std::string() const { - if(this->update) { - return "ON UPDATE"; - } else { - return "ON DELETE"; - } - } + static constexpr const bool returns_lvalue = true; }; - /** - * F - foreign key class - */ - template - struct on_update_delete_t : on_update_delete_base { - using foreign_key_type = F; - - const foreign_key_type &fk; + template + struct getter_traits> { + using object_type = O; + using field_type = T; - on_update_delete_t(decltype(fk) fk_, decltype(update) update_, foreign_key_action action_) : - on_update_delete_base{update_}, fk(fk_), _action(action_) {} + static constexpr const bool returns_lvalue = true; + }; - foreign_key_action _action = foreign_key_action::none; + template + struct getter_traits> { + using object_type = O; + using field_type = T; - foreign_key_type no_action() const { - auto res = this->fk; - if(update) { - res.on_update._action = foreign_key_action::no_action; - } else { - res.on_delete._action = foreign_key_action::no_action; - } - return res; - } + static constexpr const bool returns_lvalue = true; + }; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct getter_traits> { + using object_type = O; + using field_type = T; - foreign_key_type restrict_() const { - auto res = this->fk; - if(update) { - res.on_update._action = foreign_key_action::restrict_; - } else { - res.on_delete._action = foreign_key_action::restrict_; - } - return res; - } + static constexpr const bool returns_lvalue = false; + }; - foreign_key_type set_null() const { - auto res = this->fk; - if(update) { - res.on_update._action = foreign_key_action::set_null; - } else { - res.on_delete._action = foreign_key_action::set_null; - } - return res; - } + template + struct getter_traits> { + using object_type = O; + using field_type = T; - foreign_key_type set_default() const { - auto res = this->fk; - if(update) { - res.on_update._action = foreign_key_action::set_default; - } else { - res.on_delete._action = foreign_key_action::set_default; - } - return res; - } + static constexpr const bool returns_lvalue = false; + }; - foreign_key_type cascade() const { - auto res = this->fk; - if(update) { - res.on_update._action = foreign_key_action::cascade; - } else { - res.on_delete._action = foreign_key_action::cascade; - } - return res; - } + template + struct getter_traits> { + using object_type = O; + using field_type = T; - operator bool() const { - return this->_action != decltype(this->_action)::none; - } + static constexpr const bool returns_lvalue = true; }; - template - struct foreign_key_t, std::tuple> { - using columns_type = std::tuple; - using references_type = std::tuple; - using self = foreign_key_t; + template + struct getter_traits> { + using object_type = O; + using field_type = T; - columns_type columns; - references_type references; + static constexpr const bool returns_lvalue = true; + }; - on_update_delete_t on_update; - on_update_delete_t on_delete; + template + struct getter_traits> { + using object_type = O; + using field_type = T; - static_assert(std::tuple_size::value == std::tuple_size::value, - "Columns size must be equal to references tuple"); + static constexpr const bool returns_lvalue = true; + }; - foreign_key_t(columns_type columns_, references_type references_) : - columns(std::move(columns_)), references(std::move(references_)), - on_update(*this, true, foreign_key_action::none), on_delete(*this, false, foreign_key_action::none) {} + template + struct getter_traits> { + using object_type = O; + using field_type = T; - foreign_key_t(const self &other) : - columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), - on_delete(*this, false, other.on_delete._action) {} + static constexpr const bool returns_lvalue = true; + }; +#endif + } +} - self &operator=(const self &other) { - this->columns = other.columns; - this->references = other.references; - this->on_update = {*this, true, other.on_update._action}; - this->on_delete = {*this, false, other.on_delete._action}; - return *this; - } +// #include "member_traits/setter_traits.h" - template - void for_each_column(const L &) {} +// #include "setters.h" - template - constexpr bool has_every() const { - return false; - } - }; +namespace sqlite_orm { + namespace internal { - /** - * Cs can be a class member pointer, a getter function member pointer or setter - * func member pointer - * Available in SQLite 3.6.19 or higher - */ - template - struct foreign_key_intermediate_t { - using tuple_type = std::tuple; + template + using setter_by_value = void (O::*)(T); - tuple_type columns; + template + using setter_by_ref = void (O::*)(T&); - foreign_key_intermediate_t(tuple_type columns_) : columns(std::move(columns_)) {} + template + using setter_by_const_ref = void (O::*)(const T&); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + using setter_by_value_noexcept = void (O::*)(T) noexcept; - template - foreign_key_t, std::tuple> references(Rs... refs) { - return {std::move(this->columns), std::make_tuple(std::forward(refs)...)}; - } - }; -#endif + template + using setter_by_ref_noexcept = void (O::*)(T&) noexcept; - struct collate_t { - internal::collate_argument argument = internal::collate_argument::binary; + template + using setter_by_const_ref_noexcept = void (O::*)(const T&) noexcept; +#endif + } +} - collate_t(internal::collate_argument argument_) : argument(argument_) {} +namespace sqlite_orm { + namespace internal { - operator std::string() const { - std::string res = "COLLATE " + this->string_from_collate_argument(this->argument); - return res; - } + template + struct setter_traits; - static std::string string_from_collate_argument(internal::collate_argument argument) { - switch(argument) { - case decltype(argument)::binary: - return "BINARY"; - case decltype(argument)::nocase: - return "NOCASE"; - case decltype(argument)::rtrim: - return "RTRIM"; - } - throw std::system_error(std::make_error_code(orm_error_code::invalid_collate_argument_enum)); - } + template + struct setter_traits> { + using object_type = O; + using field_type = T; }; - struct check_string { - operator std::string() const { - return "CHECK"; - } + template + struct setter_traits> { + using object_type = O; + using field_type = T; }; - template - struct check_t : check_string { - using expression_type = T; + template + struct setter_traits> { + using object_type = O; + using field_type = T; + }; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct setter_traits> { + using object_type = O; + using field_type = T; + }; - expression_type expression; + template + struct setter_traits> { + using object_type = O; + using field_type = T; + }; - check_t(expression_type expression_) : expression(std::move(expression_)) {} + template + struct setter_traits> { + using object_type = O; + using field_type = T; }; +#endif + } +} - template - struct is_constraint : std::false_type {}; +// #include "member_traits/is_getter.h" - template<> - struct is_constraint : std::true_type {}; +#include // std::false_type, std::true_type - template - struct is_constraint> : std::true_type {}; +// #include "getters.h" - template - struct is_constraint> : std::true_type {}; - - template - struct is_constraint> : std::true_type {}; - - template - struct is_constraint> : std::true_type {}; - - template<> - struct is_constraint : std::true_type {}; +namespace sqlite_orm { + namespace internal { template - struct is_constraint> : std::true_type {}; - - template - struct constraints_size; - - template<> - struct constraints_size<> { - static constexpr const int value = 0; - }; + struct is_getter : std::false_type {}; - template - struct constraints_size { - static constexpr const int value = is_constraint::value + constraints_size::value; - }; - } + template + struct is_getter> : std::true_type {}; -#if SQLITE_VERSION_NUMBER >= 3006019 + template + struct is_getter> : std::true_type {}; - /** - * FOREIGN KEY constraint construction function that takes member pointer as argument - * Available in SQLite 3.6.19 or higher - */ - template - constraints::foreign_key_intermediate_t foreign_key(Cs... columns) { - return {std::make_tuple(std::forward(columns)...)}; - } -#endif + template + struct is_getter> : std::true_type {}; - /** - * UNIQUE constraint builder function. - */ - template - constraints::unique_t unique(Args... args) { - return {std::make_tuple(std::forward(args)...)}; - } + template + struct is_getter> : std::true_type {}; - inline constraints::unique_t<> unique() { - return {{}}; - } + template + struct is_getter> : std::true_type {}; - inline constraints::autoincrement_t autoincrement() { - return {}; - } + template + struct is_getter> : std::true_type {}; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct is_getter> : std::true_type {}; - template - constraints::primary_key_t primary_key(Cs... cs) { - return {std::make_tuple(std::forward(cs)...)}; - } + template + struct is_getter> : std::true_type {}; - inline constraints::primary_key_t<> primary_key() { - return {{}}; - } + template + struct is_getter> : std::true_type {}; - template - constraints::default_t default_value(T t) { - return {std::move(t)}; - } + template + struct is_getter> : std::true_type {}; - inline constraints::collate_t collate_nocase() { - return {internal::collate_argument::nocase}; - } + template + struct is_getter> : std::true_type {}; - inline constraints::collate_t collate_binary() { - return {internal::collate_argument::binary}; + template + struct is_getter> : std::true_type {}; +#endif } +} - inline constraints::collate_t collate_rtrim() { - return {internal::collate_argument::rtrim}; - } +// #include "member_traits/is_setter.h" - template - constraints::check_t check(T t) { - return {std::move(t)}; - } +// #include "setters.h" +namespace sqlite_orm { namespace internal { - /** - * FOREIGN KEY traits. Common case - */ - template - struct is_foreign_key : std::false_type {}; - - /** - * FOREIGN KEY traits. Specialized case - */ - template - struct is_foreign_key> : std::true_type {}; - - /** - * PRIMARY KEY traits. Common case - */ template - struct is_primary_key : public std::false_type {}; - - /** - * PRIMARY KEY traits. Specialized case - */ - template - struct is_primary_key> : public std::true_type {}; - } - -} -#pragma once - -#include // std::false_type, std::true_type -#include // std::shared_ptr, std::unique_ptr -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include // std::optional -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - -namespace sqlite_orm { + struct is_setter : std::false_type {}; - /** - * This is class that tells `sqlite_orm` that type is nullable. Nullable types - * are mapped to sqlite database as `NULL` and not-nullable are mapped as `NOT NULL`. - * Default nullability status for all types is `NOT NULL`. So if you want to map - * custom type as `NULL` (for example: boost::optional) you have to create a specialiation - * of type_is_nullable for your type and derive from `std::true_type`. - */ - template - struct type_is_nullable : public std::false_type { - bool operator()(const T &) const { - return true; - } - }; + template + struct is_setter> : std::true_type {}; - /** - * This is a specialization for std::shared_ptr. std::shared_ptr is nullable in sqlite_orm. - */ - template - struct type_is_nullable> : public std::true_type { - bool operator()(const std::shared_ptr &t) const { - return static_cast(t); - } - }; + template + struct is_setter> : std::true_type {}; - /** - * This is a specialization for std::unique_ptr. std::unique_ptr is nullable too. - */ - template - struct type_is_nullable> : public std::true_type { - bool operator()(const std::unique_ptr &t) const { - return static_cast(t); - } - }; + template + struct is_setter> : std::true_type {}; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct is_setter> : std::true_type {}; -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - /** - * This is a specialization for std::optional. std::optional is nullable. - */ - template - struct type_is_nullable> : public std::true_type { - bool operator()(const std::optional &t) const { - return t.has_value(); - } - }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct is_setter> : std::true_type {}; + template + struct is_setter> : std::true_type {}; +#endif + } } -#pragma once - -#include // std::unique_ptr -#include // std::string -#include // std::stringstream - -// #include "constraints.h" - -// #include "serializator_context.h" namespace sqlite_orm { namespace internal { - struct serializator_context_base { - bool replace_bindable_with_question = false; - bool skip_table_name = true; - bool use_parentheses = true; - - template - std::string column_name(F O::*) const { - return {}; - } - }; - - template - struct serializator_context : serializator_context_base { - using impl_type = I; - - const impl_type &impl; + template + struct column_pointer; - serializator_context(const impl_type &impl_) : impl(impl_) {} + /** + * Trait class used to define table mapped type by setter/getter/member + * T - member pointer + * `type` is a type which is mapped. + * E.g. + * - `table_type::type` is `User` + * - `table_type::type` is `User` + * - `table_type::type` is `User` + */ + template + struct table_type; - template - std::string column_name(F O::*m) const { - return this->impl.column_name(m); - } + template + struct table_type::value && + !std::is_member_function_pointer::value>::type> { + using type = O; }; - template - struct serializator_context_builder { - using storage_type = S; - using impl_type = typename storage_type::impl_type; - - serializator_context_builder(const storage_type &storage_) : storage(storage_) {} - - serializator_context operator()() const { - return {this->storage.impl}; - } + template + struct table_type::value>::type> { + using type = typename getter_traits::object_type; + }; - const storage_type &storage; + template + struct table_type::value>::type> { + using type = typename setter_traits::object_type; }; + template + struct table_type, void> { + using type = T; + }; } - } +// #include "tuple_helper/tuple_helper.h" + namespace sqlite_orm { namespace internal { - template - std::string serialize(const T &t); - /** - * This class is used in tuple interation to know whether tuple constains `default_value_t` - * constraint class and what it's value if it is + * AUTOINCREMENT constraint class. */ - struct default_value_extractor { - - template - std::unique_ptr operator()(const A &) { - return {}; - } + struct autoincrement_t { - template - std::unique_ptr operator()(const constraints::default_t &t) { - serializator_context_base context; - return std::make_unique(serialize(t.value, context)); + operator std::string() const { + return "AUTOINCREMENT"; } }; - } - -} -#pragma once - -#include // std::false_type, std::true_type + struct primary_key_base { + enum class order_by { + unspecified, + ascending, + descending, + }; -// #include "negatable.h" + order_by asc_option = order_by::unspecified; -namespace sqlite_orm { - namespace internal { - struct negatable_t {}; - } -} - -namespace sqlite_orm { - - namespace internal { + operator std::string() const { + std::string res = "PRIMARY KEY"; + switch(this->asc_option) { + case order_by::ascending: + res += " ASC"; + break; + case order_by::descending: + res += " DESC"; + break; + default: + break; + } + return res; + } + }; /** - * Inherit this class to support arithmetic types overloading + * PRIMARY KEY constraint class. + * Cs is parameter pack which contains columns (member pointers and/or function pointers). Can be empty when + * used withen `make_column` function. */ - struct arithmetic_t {}; + template + struct primary_key_t : primary_key_base { + using order_by = primary_key_base::order_by; + using columns_tuple = std::tuple; - template - struct binary_operator : Ds... { - using left_type = L; - using right_type = R; + columns_tuple columns; - left_type lhs; - right_type rhs; + primary_key_t(decltype(columns) c) : columns(move(c)) {} - binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} - }; + primary_key_t asc() const { + auto res = *this; + res.asc_option = order_by::ascending; + return res; + } - struct conc_string { - operator std::string() const { - return "||"; + primary_key_t desc() const { + auto res = *this; + res.asc_option = order_by::descending; + return res; } }; - /** - * Result of concatenation || operator - */ - template - using conc_t = binary_operator; - - struct add_string { + struct unique_base { operator std::string() const { - return "+"; + return "UNIQUE"; } }; /** - * Result of addition + operator + * UNIQUE constraint class. */ - template - using add_t = binary_operator; + template + struct unique_t : unique_base { + using columns_tuple = std::tuple; - struct sub_string { - operator std::string() const { - return "-"; - } + columns_tuple columns; + + unique_t(columns_tuple columns_) : columns(move(columns_)) {} }; /** - * Result of substitute - operator + * DEFAULT constraint class. + * T is a value type. */ - template - using sub_t = binary_operator; + template + struct default_t { + using value_type = T; + + value_type value; - struct mul_string { operator std::string() const { - return "*"; + return "DEFAULT"; } }; +#if SQLITE_VERSION_NUMBER >= 3006019 + /** - * Result of multiply * operator + * FOREIGN KEY constraint class. + * Cs are columns which has foreign key + * Rs are column which C references to + * Available in SQLite 3.6.19 or higher */ - template - using mul_t = binary_operator; - struct div_string { - operator std::string() const { - return "/"; - } + template + struct foreign_key_t; + + enum class foreign_key_action { + none, // not specified + no_action, + restrict_, + set_null, + set_default, + cascade, }; - /** - * Result of divide / operator - */ - template - using div_t = binary_operator; + inline std::ostream& operator<<(std::ostream& os, foreign_key_action action) { + switch(action) { + case decltype(action)::no_action: + os << "NO ACTION"; + break; + case decltype(action)::restrict_: + os << "RESTRICT"; + break; + case decltype(action)::set_null: + os << "SET NULL"; + break; + case decltype(action)::set_default: + os << "SET DEFAULT"; + break; + case decltype(action)::cascade: + os << "CASCADE"; + break; + case decltype(action)::none: + break; + } + return os; + } + + struct on_update_delete_base { + const bool update; // true if update and false if delete - struct mod_string { operator std::string() const { - return "%"; + if(this->update) { + return "ON UPDATE"; + } else { + return "ON DELETE"; + } } }; /** - * Result of mod % operator + * F - foreign key class */ - template - using mod_t = binary_operator; + template + struct on_update_delete_t : on_update_delete_base { + using foreign_key_type = F; - struct bitwise_shift_left_string { - operator std::string() const { - return "<<"; - } - }; + const foreign_key_type& fk; - /** - * Result of bitwise shift left << operator - */ - template - using bitwise_shift_left_t = binary_operator; + on_update_delete_t(decltype(fk) fk_, decltype(update) update_, foreign_key_action action_) : + on_update_delete_base{update_}, fk(fk_), _action(action_) {} - struct bitwise_shift_right_string { - operator std::string() const { - return ">>"; - } - }; + foreign_key_action _action = foreign_key_action::none; - /** - * Result of bitwise shift right >> operator - */ - template - using bitwise_shift_right_t = binary_operator; + foreign_key_type no_action() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::no_action; + } else { + res.on_delete._action = foreign_key_action::no_action; + } + return res; + } - struct bitwise_and_string { - operator std::string() const { - return "&"; + foreign_key_type restrict_() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::restrict_; + } else { + res.on_delete._action = foreign_key_action::restrict_; + } + return res; } - }; - /** - * Result of bitwise and & operator - */ - template - using bitwise_and_t = binary_operator; + foreign_key_type set_null() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::set_null; + } else { + res.on_delete._action = foreign_key_action::set_null; + } + return res; + } - struct bitwise_or_string { - operator std::string() const { - return "|"; + foreign_key_type set_default() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::set_default; + } else { + res.on_delete._action = foreign_key_action::set_default; + } + return res; } - }; - /** - * Result of bitwise or | operator - */ - template - using bitwise_or_t = binary_operator; + foreign_key_type cascade() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::cascade; + } else { + res.on_delete._action = foreign_key_action::cascade; + } + return res; + } - struct bitwise_not_string { - operator std::string() const { - return "~"; + operator bool() const { + return this->_action != decltype(this->_action)::none; } }; - /** - * Result of bitwise not ~ operator - */ - template - struct bitwise_not_t : bitwise_not_string, arithmetic_t, negatable_t { - using argument_type = T; + template + struct foreign_key_t, std::tuple> { + using columns_type = std::tuple; + using references_type = std::tuple; + using self = foreign_key_t; - argument_type argument; + /** + * Holds obect type of all referenced columns. + */ + using target_type = typename same_or_void::type...>::type; - bitwise_not_t(argument_type argument_) : argument(std::move(argument_)) {} - }; + /** + * Holds obect type of all source columns. + */ + using source_type = typename same_or_void::type...>::type; - struct assign_string { - operator std::string() const { - return "="; + columns_type columns; + references_type references; + + on_update_delete_t on_update; + on_update_delete_t on_delete; + + static_assert(std::tuple_size::value == std::tuple_size::value, + "Columns size must be equal to references tuple"); + static_assert(!std::is_same::value, "All references must have the same type"); + + foreign_key_t(columns_type columns_, references_type references_) : + columns(std::move(columns_)), references(std::move(references_)), + on_update(*this, true, foreign_key_action::none), on_delete(*this, false, foreign_key_action::none) {} + + foreign_key_t(const self& other) : + columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), + on_delete(*this, false, other.on_delete._action) {} + + self& operator=(const self& other) { + this->columns = other.columns; + this->references = other.references; + this->on_update = {*this, true, other.on_update._action}; + this->on_delete = {*this, false, other.on_delete._action}; + return *this; + } + + template + void for_each_column(const L&) {} + + template + constexpr bool has_every() const { + return false; } }; - /** - * Result of assign = operator - */ - template - using assign_t = binary_operator; /** - * Assign operator traits. Common case + * Cs can be a class member pointer, a getter function member pointer or setter + * func member pointer + * Available in SQLite 3.6.19 or higher */ - template - struct is_assign_t : public std::false_type {}; + template + struct foreign_key_intermediate_t { + using tuple_type = std::tuple; - /** - * Assign operator traits. Specialized case - */ - template - struct is_assign_t> : public std::true_type {}; + tuple_type columns; - /** - * Is not an operator but a result of c(...) function. Has operator= overloaded which returns assign_t - */ - template - struct expression_t { - T t; + foreign_key_intermediate_t(tuple_type columns_) : columns(std::move(columns_)) {} - expression_t(T t_) : t(std::move(t_)) {} + template + foreign_key_t, std::tuple> references(Rs... refs) { + return {std::move(this->columns), std::make_tuple(std::forward(refs)...)}; + } + }; +#endif - template - assign_t operator=(R r) const { - return {this->t, std::move(r)}; + struct collate_constraint_t { + internal::collate_argument argument = internal::collate_argument::binary; + + collate_constraint_t(internal::collate_argument argument_) : argument(argument_) {} + + operator std::string() const { + std::string res = "COLLATE " + this->string_from_collate_argument(this->argument); + return res; } - assign_t operator=(std::nullptr_t) const { - return {this->t, nullptr}; + static std::string string_from_collate_argument(internal::collate_argument argument) { + switch(argument) { + case decltype(argument)::binary: + return "BINARY"; + case decltype(argument)::nocase: + return "NOCASE"; + case decltype(argument)::rtrim: + return "RTRIM"; + } + throw std::system_error(std::make_error_code(orm_error_code::invalid_collate_argument_enum)); } }; - } + struct check_string { + operator std::string() const { + return "CHECK"; + } + }; - /** - * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or - * `storage.update(set(c(&User::name) = "Dua Lipa")); - */ - template - internal::expression_t c(T t) { - return {std::move(t)}; + template + struct check_t : check_string { + using expression_type = T; + + expression_type expression; + + check_t(expression_type expression_) : expression(std::move(expression_)) {} + }; + + template + struct is_constraint : std::false_type {}; + + template<> + struct is_constraint : std::true_type {}; + + template + struct is_constraint> : std::true_type {}; + + template + struct is_constraint> : std::true_type {}; + + template + struct is_constraint> : std::true_type {}; + + template + struct is_constraint> : std::true_type {}; + + template<> + struct is_constraint : std::true_type {}; + + template + struct is_constraint> : std::true_type {}; + + template + struct constraints_size; + + template<> + struct constraints_size<> { + static constexpr const int value = 0; + }; + + template + struct constraints_size { + static constexpr const int value = is_constraint::value + constraints_size::value; + }; } +#if SQLITE_VERSION_NUMBER >= 3006019 + /** - * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT - * name || '@gmail.com' FROM users + * FOREIGN KEY constraint construction function that takes member pointer as argument + * Available in SQLite 3.6.19 or higher */ - template - internal::conc_t conc(L l, R r) { - return {std::move(l), std::move(r)}; + template + internal::foreign_key_intermediate_t foreign_key(Cs... columns) { + return {std::make_tuple(std::forward(columns)...)}; } +#endif /** - * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users + * UNIQUE constraint builder function. */ - template - internal::add_t add(L l, R r) { - return {std::move(l), std::move(r)}; + template + internal::unique_t unique(Args... args) { + return {std::make_tuple(std::forward(args)...)}; } - /** - * Public interface for - operator. Example: `select(add(&User::age, 1));` => SELECT age - 1 FROM users - */ - template - internal::sub_t sub(L l, R r) { - return {std::move(l), std::move(r)}; + inline internal::unique_t<> unique() { + return {{}}; } - template - internal::mul_t mul(L l, R r) { - return {std::move(l), std::move(r)}; + inline internal::autoincrement_t autoincrement() { + return {}; } - template - internal::div_t div(L l, R r) { - return {std::move(l), std::move(r)}; + template + internal::primary_key_t primary_key(Cs... cs) { + return {std::make_tuple(std::forward(cs)...)}; } - template - internal::mod_t mod(L l, R r) { - return {std::move(l), std::move(r)}; + inline internal::primary_key_t<> primary_key() { + return {{}}; } - template - internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { - return {std::move(l), std::move(r)}; + template + internal::default_t default_value(T t) { + return {std::move(t)}; } - template - internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { - return {std::move(l), std::move(r)}; + inline internal::collate_constraint_t collate_nocase() { + return {internal::collate_argument::nocase}; } - template - internal::bitwise_and_t bitwise_and(L l, R r) { - return {std::move(l), std::move(r)}; + inline internal::collate_constraint_t collate_binary() { + return {internal::collate_argument::binary}; } - template - internal::bitwise_or_t bitwise_or(L l, R r) { - return {std::move(l), std::move(r)}; + inline internal::collate_constraint_t collate_rtrim() { + return {internal::collate_argument::rtrim}; } template - internal::bitwise_not_t bitwise_not(T t) { + internal::check_t check(T t) { return {std::move(t)}; } - template - internal::assign_t assign(L l, R r) { - return {std::move(l), std::move(r)}; - } - -} -#pragma once - -#include // std::tuple -#include // std::string -#include // std::unique_ptr -#include // std::true_type, std::false_type, std::is_same, std::enable_if, std::is_member_pointer, std::is_member_function_pointer + namespace internal { -// #include "type_is_nullable.h" + /** + * FOREIGN KEY traits. Common case + */ + template + struct is_foreign_key : std::false_type {}; -// #include "tuple_helper.h" + /** + * FOREIGN KEY traits. Specialized case + */ + template + struct is_foreign_key> : std::true_type {}; -// #include "default_value_extractor.h" + /** + * PRIMARY KEY traits. Common case + */ + template + struct is_primary_key : public std::false_type {}; -// #include "constraints.h" + /** + * PRIMARY KEY traits. Specialized case + */ + template + struct is_primary_key> : public std::true_type {}; -// #include "getter_traits.h" + /** + * PRIMARY KEY INSERTABLE traits. + */ + template + struct is_primary_key_insertable { + using field_type = typename T::field_type; + using constraints_type = typename T::constraints_type; -namespace sqlite_orm { + static_assert((tuple_helper::tuple_contains_type, constraints_type>::value), + "an unexpected type was passed"); - namespace internal { + static constexpr bool value = + (tuple_helper::tuple_contains_some_type::value || + tuple_helper::tuple_contains_type::value || + std::is_base_of>::value); + }; + } - template - struct is_field_member_pointer : std::false_type {}; +} +#pragma once - template - struct is_field_member_pointer::value && - !std::is_member_function_pointer::value>::type> - : std::true_type {}; +#include // std::false_type, std::true_type +#include // std::shared_ptr, std::unique_ptr +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct field_member_traits; +namespace sqlite_orm { - template - struct field_member_traits::value>::type> { - using object_type = O; - using field_type = F; - }; - - /** - * Getters aliases + /** + * This is class that tells `sqlite_orm` that type is nullable. Nullable types + * are mapped to sqlite database as `NULL` and not-nullable are mapped as `NOT NULL`. + * Default nullability status for all types is `NOT NULL`. So if you want to map + * custom type as `NULL` (for example: boost::optional) you have to create a specialiation + * of type_is_nullable for your type and derive from `std::true_type`. */ - template - using getter_by_value_const = T (O::*)() const; - - template - using getter_by_value = T (O::*)(); - - template - using getter_by_ref_const = T &(O::*)() const; - - template - using getter_by_ref = T &(O::*)(); + template + struct type_is_nullable : public std::false_type { + bool operator()(const T&) const { + return true; + } + }; - template - using getter_by_const_ref_const = const T &(O::*)() const; + /** + * This is a specialization for std::shared_ptr. std::shared_ptr is nullable in sqlite_orm. + */ + template + struct type_is_nullable> : public std::true_type { + bool operator()(const std::shared_ptr& t) const { + return static_cast(t); + } + }; - template - using getter_by_const_ref = const T &(O::*)(); + /** + * This is a specialization for std::unique_ptr. std::unique_ptr is nullable too. + */ + template + struct type_is_nullable> : public std::true_type { + bool operator()(const std::unique_ptr& t) const { + return static_cast(t); + } + }; - /** - * Setters aliases +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * This is a specialization for std::optional. std::optional is nullable. */ - template - using setter_by_value = void (O::*)(T); + template + struct type_is_nullable> : public std::true_type { + bool operator()(const std::optional& t) const { + return t.has_value(); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - using setter_by_ref = void (O::*)(T &); +} +#pragma once - template - using setter_by_const_ref = void (O::*)(const T &); +#include // std::unique_ptr +#include // std::string +#include // std::stringstream - template - struct is_getter : std::false_type {}; +// #include "constraints.h" - template - struct is_getter> : std::true_type {}; +// #include "serializator_context.h" - template - struct is_getter> : std::true_type {}; +namespace sqlite_orm { - template - struct is_getter> : std::true_type {}; + namespace internal { - template - struct is_getter> : std::true_type {}; + struct serializator_context_base { + bool replace_bindable_with_question = false; + bool skip_table_name = true; + bool use_parentheses = true; - template - struct is_getter> : std::true_type {}; + template + const std::string* column_name(F O::*) const { + return nullptr; + } + }; - template - struct is_getter> : std::true_type {}; + template + struct serializator_context : serializator_context_base { + using impl_type = I; - template - struct is_setter : std::false_type {}; + const impl_type& impl; - template - struct is_setter> : std::true_type {}; + serializator_context(const impl_type& impl_) : impl(impl_) {} - template - struct is_setter> : std::true_type {}; + template + const std::string* column_name(F O::*m) const { + return this->impl.column_name(m); + } + }; - template - struct is_setter> : std::true_type {}; + template + struct serializator_context_builder { + using storage_type = S; + using impl_type = typename storage_type::impl_type; - template - struct getter_traits; + serializator_context_builder(const storage_type& storage_) : storage(storage_) {} - template - struct getter_traits> { - using object_type = O; - using field_type = T; + serializator_context operator()() const { + return {this->storage.impl}; + } - static constexpr const bool returns_lvalue = false; + const storage_type& storage; }; - template - struct getter_traits> { - using object_type = O; - using field_type = T; + } - static constexpr const bool returns_lvalue = false; - }; +} - template - struct getter_traits> { - using object_type = O; - using field_type = T; +namespace sqlite_orm { - static constexpr const bool returns_lvalue = true; - }; + namespace internal { - template - struct getter_traits> { - using object_type = O; - using field_type = T; + /** + * This class is used in tuple interation to know whether tuple constains `default_value_t` + * constraint class and what it's value if it is + */ + struct default_value_extractor { - static constexpr const bool returns_lvalue = true; + template + std::unique_ptr operator()(const A&) { + return {}; + } + + template + std::unique_ptr operator()(const default_t& t) { + serializator_context_base context; + return std::make_unique(serialize(t.value, context)); + } }; - template - struct getter_traits> { - using object_type = O; - using field_type = T; + } - static constexpr const bool returns_lvalue = true; - }; +} +#pragma once - template - struct getter_traits> { - using object_type = O; - using field_type = T; +#include // std::false_type, std::true_type +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::nullopt +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +// #include "tags.h" - static constexpr const bool returns_lvalue = true; - }; +namespace sqlite_orm { + namespace internal { + struct negatable_t {}; - template - struct setter_traits; + /** + * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators + */ + struct condition_t {}; + } +} - template - struct setter_traits> { - using object_type = O; - using field_type = T; - }; +namespace sqlite_orm { - template - struct setter_traits> { - using object_type = O; - using field_type = T; - }; + namespace internal { - template - struct setter_traits> { - using object_type = O; - using field_type = T; - }; + /** + * Inherit this class to support arithmetic types overloading + */ + struct arithmetic_t {}; - template - struct member_traits; + template + struct binary_operator : Ds... { + using left_type = L; + using right_type = R; - template - struct member_traits::value>::type> { - using object_type = typename field_member_traits::object_type; - using field_type = typename field_member_traits::field_type; - }; + left_type lhs; + right_type rhs; - template - struct member_traits::value>::type> { - using object_type = typename getter_traits::object_type; - using field_type = typename getter_traits::field_type; + binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} }; - template - struct member_traits::value>::type> { - using object_type = typename setter_traits::object_type; - using field_type = typename setter_traits::field_type; + struct conc_string { + operator std::string() const { + return "||"; + } }; - } -} -namespace sqlite_orm { + /** + * Result of concatenation || operator + */ + template + using conc_t = binary_operator; - namespace internal { + struct add_string { + operator std::string() const { + return "+"; + } + }; - struct column_base { + /** + * Result of addition + operator + */ + template + using add_t = binary_operator; - /** - * Column name. Specified during construction in `make_column`. - */ - const std::string name; + struct sub_string { + operator std::string() const { + return "-"; + } }; /** - * This class stores single column info. column_t is a pair of [column_name:member_pointer] mapped to a storage - * O is a mapped class, e.g. User - * T is a mapped class'es field type, e.g. &User::name - * Op... is a constraints pack, e.g. primary_key_t, autoincrement_t etc + * Result of substitute - operator */ - template - struct column_t : column_base { - using object_type = O; - using field_type = T; - using constraints_type = std::tuple; - using member_pointer_t = field_type object_type::*; - using getter_type = G; - using setter_type = S; - - /** - * Member pointer used to read/write member - */ - member_pointer_t member_pointer /* = nullptr*/; + template + using sub_t = binary_operator; - /** - * Getter member function pointer to get a value. If member_pointer is null than - * `getter` and `setter` must be not null - */ - getter_type getter /* = nullptr*/; + struct mul_string { + operator std::string() const { + return "*"; + } + }; - /** - * Setter member function - */ - setter_type setter /* = nullptr*/; + /** + * Result of multiply * operator + */ + template + using mul_t = binary_operator; - /** - * Constraints tuple - */ - constraints_type constraints; + struct div_string { + operator std::string() const { + return "/"; + } + }; - column_t(std::string name_, - member_pointer_t member_pointer_, - getter_type getter_, - setter_type setter_, - constraints_type constraints_) : - column_base{std::move(name_)}, - member_pointer(member_pointer_), getter(getter_), setter(setter_), constraints(move(constraints_)) {} + /** + * Result of divide / operator + */ + template + using div_t = binary_operator; - /** - * Simplified interface for `NOT NULL` constraint - */ - bool not_null() const { - return !type_is_nullable::value; + struct mod_operator_string { + operator std::string() const { + return "%"; } + }; - template - constexpr bool has() const { - return tuple_helper::tuple_contains_type::value; - } + /** + * Result of mod % operator + */ + template + using mod_t = binary_operator; - template - constexpr bool has_every() const { - if(has() && has()) { - return true; - } else { - return has_every(); - } + struct bitwise_shift_left_string { + operator std::string() const { + return "<<"; } + }; - template - constexpr bool has_every() const { - return has(); - } + /** + * Result of bitwise shift left << operator + */ + template + using bitwise_shift_left_t = binary_operator; - /** - * Simplified interface for `DEFAULT` constraint - * @return string representation of default value if it exists otherwise nullptr - */ - std::unique_ptr default_value() const { - std::unique_ptr res; - iterate_tuple(this->constraints, [&res](auto &v) { - auto dft = internal::default_value_extractor()(v); - if(dft) { - res = std::move(dft); - } - }); - return res; + struct bitwise_shift_right_string { + operator std::string() const { + return ">>"; } }; /** - * Column traits. Common case. + * Result of bitwise shift right >> operator */ - template - struct is_column : public std::false_type {}; + template + using bitwise_shift_right_t = binary_operator; + + struct bitwise_and_string { + operator std::string() const { + return "&"; + } + }; /** - * Column traits. Specialized case case. + * Result of bitwise and & operator */ - template - struct is_column> : public std::true_type {}; + template + using bitwise_and_t = binary_operator; - template - struct column_field_type { - using type = void; + struct bitwise_or_string { + operator std::string() const { + return "|"; + } }; - template - struct column_field_type> { - using type = typename column_t::field_type; + /** + * Result of bitwise or | operator + */ + template + using bitwise_or_t = binary_operator; + + struct bitwise_not_string { + operator std::string() const { + return "~"; + } }; + /** + * Result of bitwise not ~ operator + */ template - struct column_constraints_type { - using type = std::tuple<>; + struct bitwise_not_t : bitwise_not_string, arithmetic_t, negatable_t { + using argument_type = T; + + argument_type argument; + + bitwise_not_t(argument_type argument_) : argument(std::move(argument_)) {} }; - template - struct column_constraints_type> { - using type = typename column_t::constraints_type; + struct assign_string { + operator std::string() const { + return "="; + } }; + /** + * Result of assign = operator + */ + template + using assign_t = binary_operator; + + /** + * Assign operator traits. Common case + */ + template + struct is_assign_t : public std::false_type {}; + + /** + * Assign operator traits. Specialized case + */ + template + struct is_assign_t> : public std::true_type {}; + + template + struct in_t; + } /** - * Column builder function. You should use it to create columns instead of constructor + * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT + * name || '@gmail.com' FROM users */ - template::value>::type, - class... Op> - internal::column_t - make_column(const std::string &name, T O::*m, Op... constraints) { - static_assert(constraints::template constraints_size::value == std::tuple_size>::value, - "Incorrect constraints pack"); - static_assert(internal::is_field_member_pointer::value, - "second argument expected as a member field pointer, not member function pointer"); - return {name, m, nullptr, nullptr, std::make_tuple(constraints...)}; + template + internal::conc_t conc(L l, R r) { + return {std::move(l), std::move(r)}; } /** - * Column builder function with setter and getter. You should use it to create columns instead of constructor + * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users */ - template::value>::type, - typename = typename std::enable_if::value>::type, - class... Op> - internal::column_t::object_type, - typename internal::setter_traits::field_type, - G, - S, - Op...> - make_column(const std::string &name, S setter, G getter, Op... constraints) { - static_assert(std::is_same::field_type, - typename internal::getter_traits::field_type>::value, - "Getter and setter must get and set same data type"); - static_assert(constraints::template constraints_size::value == std::tuple_size>::value, - "Incorrect constraints pack"); - return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; + template + internal::add_t add(L l, R r) { + return {std::move(l), std::move(r)}; } /** - * Column builder function with getter and setter (reverse order). You should use it to create columns instead of - * constructor + * Public interface for - operator. Example: `select(sub(&User::age, 1));` => SELECT age - 1 FROM users */ - template::value>::type, - typename = typename std::enable_if::value>::type, - class... Op> - internal::column_t::object_type, - typename internal::setter_traits::field_type, - G, - S, - Op...> - make_column(const std::string &name, G getter, S setter, Op... constraints) { - static_assert(std::is_same::field_type, - typename internal::getter_traits::field_type>::value, - "Getter and setter must get and set same data type"); - static_assert(constraints::template constraints_size::value == std::tuple_size>::value, - "Incorrect constraints pack"); - return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; + template + internal::sub_t sub(L l, R r) { + return {std::move(l), std::move(r)}; } -} -#pragma once - -#include // std::string -#include // std::stringstream -#include // std::vector -#include // std::nullptr_t -#include // std::shared_ptr, std::unique_ptr -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include // std::optional -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - -namespace sqlite_orm { + /** + * Public interface for * operator. Example: `select(mul(&User::salary, 2));` => SELECT salary * 2 FROM users + */ + template + internal::mul_t mul(L l, R r) { + return {std::move(l), std::move(r)}; + } /** - * Is used to print members mapped to objects in storage_t::dump member function. - * Other developers can create own specialization to map custom types + * Public interface for / operator. Example: `select(div(&User::salary, 3));` => SELECT salary / 3 FROM users + * @note Please notice that ::div function already exists in pure C standard library inside header. + * If you use `using namespace sqlite_orm` directive you an specify which `div` you call explicitly using `::div` or `sqlite_orm::div` statements. */ - template - struct field_printer { - std::string operator()(const T &t) const { - std::stringstream stream; - stream << t; - return stream.str(); - } - }; - - /** - * Upgrade to integer is required when using unsigned char(uint8_t) - */ - template<> - struct field_printer { - std::string operator()(const unsigned char &t) const { - std::stringstream stream; - stream << +t; - return stream.str(); - } - }; + template + internal::div_t div(L l, R r) { + return {std::move(l), std::move(r)}; + } /** - * Upgrade to integer is required when using signed char(int8_t) + * Public interface for % operator. Example: `select(mod(&User::age, 5));` => SELECT age % 5 FROM users */ - template<> - struct field_printer { - std::string operator()(const signed char &t) const { - std::stringstream stream; - stream << +t; - return stream.str(); - } - }; + template + internal::mod_t mod(L l, R r) { + return {std::move(l), std::move(r)}; + } - /** - * char is neigher signer char nor unsigned char so it has its own specialization - */ - template<> - struct field_printer { - std::string operator()(const char &t) const { - std::stringstream stream; - stream << +t; - return stream.str(); - } - }; + template + internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { + return {std::move(l), std::move(r)}; + } - template<> - struct field_printer { - std::string operator()(const std::string &t) const { - return t; - } - }; + template + internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { + return {std::move(l), std::move(r)}; + } - template<> - struct field_printer> { - std::string operator()(const std::vector &t) const { - std::stringstream ss; - ss << std::hex; - for(auto c: t) { - ss << c; - } - return ss.str(); - } - }; + template + internal::bitwise_and_t bitwise_and(L l, R r) { + return {std::move(l), std::move(r)}; + } - template<> - struct field_printer { - std::string operator()(const std::nullptr_t &) const { - return "null"; - } - }; + template + internal::bitwise_or_t bitwise_or(L l, R r) { + return {std::move(l), std::move(r)}; + } template - struct field_printer> { - std::string operator()(const std::shared_ptr &t) const { - if(t) { - return field_printer()(*t); - } else { - return field_printer()(nullptr); - } - } - }; + internal::bitwise_not_t bitwise_not(T t) { + return {std::move(t)}; + } - template - struct field_printer> { - std::string operator()(const std::unique_ptr &t) const { - if(t) { - return field_printer()(*t); - } else { - return field_printer()(nullptr); - } - } - }; + template + internal::assign_t assign(L l, R r) { + return {std::move(l), std::move(r)}; + } -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct field_printer> { - std::string operator()(const std::optional &t) const { - if(t.has_value()) { - return field_printer()(*t); - } else { - return field_printer()(nullptr); - } - } - }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED } #pragma once -#include // std::string -#include // std::enable_if, std::is_same -#include // std::vector #include // std::tuple +#include // std::string +#include // std::unique_ptr +#include // std::true_type, std::false_type, std::is_same, std::enable_if, std::is_member_pointer, std::is_member_function_pointer -// #include "collate_argument.h" +// #include "type_is_nullable.h" -// #include "constraints.h" +// #include "tuple_helper/tuple_helper.h" -// #include "optional_container.h" +// #include "default_value_extractor.h" -namespace sqlite_orm { +// #include "constraints.h" - namespace internal { +// #include "member_traits/member_traits.h" - /** - * This is a cute class which allows storing something or nothing - * depending on template argument. Useful for optional class members - */ - template - struct optional_container { - using type = T; +#include // std::enable_if - type field; +// #include "is_field_member_pointer.h" - template - void apply(const L &l) const { - l(this->field); - } - }; +#include // std::false_type, std::true_type, std::is_member_pointer, std::is_member_function_pointer - template<> - struct optional_container { - using type = void; +namespace sqlite_orm { + namespace internal { - template - void apply(const L &) const { - //.. - } - }; + template + struct is_field_member_pointer : std::false_type {}; + + template + struct is_field_member_pointer::value && + !std::is_member_function_pointer::value>::type> + : std::true_type {}; } } -// #include "negatable.h" +// #include "is_getter.h" -namespace sqlite_orm { +// #include "field_member_traits.h" - namespace internal { - struct arithmetic_t; - } +#include // std::enable_if + +// #include "is_field_member_pointer.h" +namespace sqlite_orm { namespace internal { - struct limit_string { - operator std::string() const { - return "LIMIT"; - } - }; + template + struct field_member_traits; - /** - * Stores LIMIT/OFFSET info - */ - template - struct limit_t : limit_string { - T lim; - internal::optional_container off; + template + struct field_member_traits::value>::type> { + using object_type = O; + using field_type = F; + }; + } +} - limit_t() = default; +// #include "is_setter.h" - limit_t(decltype(lim) lim_) : lim(std::move(lim_)) {} +// #include "getter_traits.h" - limit_t(decltype(lim) lim_, decltype(off) off_) : lim(std::move(lim_)), off(std::move(off_)) {} - }; +// #include "setter_traits.h" - template - struct is_limit : std::false_type {}; +namespace sqlite_orm { + namespace internal { - template - struct is_limit> : std::true_type {}; + template + struct member_traits; - /** - * Stores OFFSET only info - */ template - struct offset_t { - T off; + struct member_traits::value>::type> { + using object_type = typename field_member_traits::object_type; + using field_type = typename field_member_traits::field_type; }; template - struct is_offset : std::false_type {}; - - template - struct is_offset> : std::true_type {}; - - /** - * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators - */ - struct condition_t {}; + struct member_traits::value>::type> { + using object_type = typename getter_traits::object_type; + using field_type = typename getter_traits::field_type; + }; - /** - * Collated something - */ template - struct collate_t : public condition_t { - T expr; - internal::collate_argument argument; - - collate_t(T expr_, internal::collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} - - operator std::string() const { - return constraints::collate_t{this->argument}; - } + struct member_traits::value>::type> { + using object_type = typename setter_traits::object_type; + using field_type = typename setter_traits::field_type; }; + } +} - struct named_collate_base { - std::string name; - - operator std::string() const { - return "COLLATE " + this->name; - } - }; +namespace sqlite_orm { - /** - * Collated something with custom collate function - */ - template - struct named_collate : named_collate_base { - T expr; + namespace internal { - named_collate(T expr_, std::string name_) : named_collate_base{std::move(name_)}, expr(std::move(expr_)) {} - }; + struct column_base { - struct negated_condition_string { - operator std::string() const { - return "NOT"; - } + /** + * Column name. Specified during construction in `make_column`. + */ + const std::string name; }; /** - * Result of not operator + * This class stores single column info. column_t is a pair of [column_name:member_pointer] mapped to a storage + * O is a mapped class, e.g. User + * T is a mapped class'es field type, e.g. &User::name + * Op... is a constraints pack, e.g. primary_key_t, autoincrement_t etc */ - template - struct negated_condition_t : condition_t, negated_condition_string { - C c; - - negated_condition_t(C c_) : c(std::move(c_)) {} - }; - - /** - * Base class for binary conditions - */ - template - struct binary_condition : public condition_t { - using left_type = L; - using right_type = R; - - left_type l; - right_type r; - - binary_condition() = default; - - binary_condition(left_type l_, right_type r_) : l(std::move(l_)), r(std::move(r_)) {} - }; - - struct and_condition_string { - operator std::string() const { - return "AND"; - } - }; - - /** - * Result of and operator - */ - template - struct and_condition_t : binary_condition, and_condition_string { - using super = binary_condition; - - using super::super; - }; - - struct or_condition_string { - operator std::string() const { - return "OR"; - } - }; + template + struct column_t : column_base { + using object_type = O; + using field_type = T; + using constraints_type = std::tuple; + using member_pointer_t = field_type object_type::*; + using getter_type = G; + using setter_type = S; - /** - * Result of or operator - */ - template - struct or_condition_t : binary_condition, or_condition_string { - using super = binary_condition; + /** + * Member pointer used to read/write member + */ + member_pointer_t member_pointer /* = nullptr*/; - using super::super; - }; + /** + * Getter member function pointer to get a value. If member_pointer is null than + * `getter` and `setter` must be not null + */ + getter_type getter /* = nullptr*/; - struct is_equal_string { - operator std::string() const { - return "="; - } - }; + /** + * Setter member function + */ + setter_type setter /* = nullptr*/; - /** - * = and == operators object - */ - template - struct is_equal_t : binary_condition, is_equal_string, internal::negatable_t { - using self = is_equal_t; + /** + * Constraints tuple + */ + constraints_type constraints; - using binary_condition::binary_condition; + column_t(std::string name_, + member_pointer_t member_pointer_, + getter_type getter_, + setter_type setter_, + constraints_type constraints_) : + column_base{std::move(name_)}, + member_pointer(member_pointer_), getter(getter_), setter(setter_), constraints(move(constraints_)) {} - collate_t collate_binary() const { - return {*this, internal::collate_argument::binary}; + /** + * Simplified interface for `NOT NULL` constraint + */ + bool not_null() const { + return !type_is_nullable::value; } - collate_t collate_nocase() const { - return {*this, internal::collate_argument::nocase}; + template + constexpr bool has() const { + return tuple_helper::tuple_contains_type::value; } - collate_t collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; + template + constexpr bool has_every() const { + if(has() && has()) { + return true; + } else { + return has_every(); + } } - named_collate collate(std::string name) const { - return {*this, std::move(name)}; + template + constexpr bool has_every() const { + return has(); } - }; - struct is_not_equal_string { - operator std::string() const { - return "!="; + /** + * Simplified interface for `DEFAULT` constraint + * @return string representation of default value if it exists otherwise nullptr + */ + std::unique_ptr default_value() const { + std::unique_ptr res; + iterate_tuple(this->constraints, [&res](auto& v) { + auto dft = internal::default_value_extractor()(v); + if(dft) { + res = std::move(dft); + } + }); + return res; } }; - /** - * != operator object - */ - template - struct is_not_equal_t : binary_condition, is_not_equal_string, internal::negatable_t { - using self = is_not_equal_t; - - using binary_condition::binary_condition; + // we are compelled to wrap all sfinae-implemented traits to prevent "error: type/value mismatch at argument 2 in template parameter list" + namespace sfinae { + /** + * Column with insertable primary key traits. Common case. + */ + template + struct is_column_with_insertable_primary_key : public std::false_type {}; - collate_t collate_binary() const { - return {*this, internal::collate_argument::binary}; - } + /** + * Column with insertable primary key traits. Specialized case case. + */ + template + struct is_column_with_insertable_primary_key< + column_t, + typename std::enable_if<(tuple_helper::tuple_contains_type< + primary_key_t<>, + typename column_t::constraints_type>::value)>::type> { + using column_type = column_t; + static constexpr bool value = is_primary_key_insertable::value; + }; - collate_t collate_nocase() const { - return {*this, internal::collate_argument::nocase}; - } + /** + * Column with noninsertable primary key traits. Common case. + */ + template + struct is_column_with_noninsertable_primary_key : public std::false_type {}; - collate_t collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; - } - }; + /** + * Column with noninsertable primary key traits. Specialized case case. + */ + template + struct is_column_with_noninsertable_primary_key< + column_t, + typename std::enable_if<(tuple_helper::tuple_contains_type< + primary_key_t<>, + typename column_t::constraints_type>::value)>::type> { + using column_type = column_t; + static constexpr bool value = !is_primary_key_insertable::value; + }; - struct greater_than_string { - operator std::string() const { - return ">"; - } - }; + } /** - * > operator object. + * Column traits. Common case. */ - template - struct greater_than_t : binary_condition, greater_than_string, internal::negatable_t { - using self = greater_than_t; - - using binary_condition::binary_condition; - - collate_t collate_binary() const { - return {*this, internal::collate_argument::binary}; - } - - collate_t collate_nocase() const { - return {*this, internal::collate_argument::nocase}; - } - - collate_t collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; - } - }; - - struct greater_or_equal_string { - operator std::string() const { - return ">="; - } - }; + template + struct is_column : public std::false_type {}; /** - * >= operator object. + * Column traits. Specialized case case. */ - template - struct greater_or_equal_t : binary_condition, greater_or_equal_string, internal::negatable_t { - using self = greater_or_equal_t; - - using binary_condition::binary_condition; - - collate_t collate_binary() const { - return {*this, internal::collate_argument::binary}; - } - - collate_t collate_nocase() const { - return {*this, internal::collate_argument::nocase}; - } - - collate_t collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; - } - }; - - struct lesser_than_string { - operator std::string() const { - return "<"; - } - }; + template + struct is_column> : public std::true_type {}; /** - * < operator object. + * Column with insertable primary key traits. */ - template - struct lesser_than_t : binary_condition, lesser_than_string, internal::negatable_t { - using self = lesser_than_t; - - using binary_condition::binary_condition; - - collate_t collate_binary() const { - return {*this, internal::collate_argument::binary}; - } - - collate_t collate_nocase() const { - return {*this, internal::collate_argument::nocase}; - } - - collate_t collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; - } - }; - - struct lesser_or_equal_string { - operator std::string() const { - return "<="; - } - }; + template + struct is_column_with_insertable_primary_key : public sfinae::is_column_with_insertable_primary_key {}; /** - * <= operator object. + * Column with noninsertable primary key traits. */ - template - struct lesser_or_equal_t : binary_condition, lesser_or_equal_string, internal::negatable_t { - using self = lesser_or_equal_t; + template + struct is_column_with_noninsertable_primary_key : public sfinae::is_column_with_noninsertable_primary_key {}; - using binary_condition::binary_condition; + template + struct column_field_type { + using type = void; + }; - collate_t collate_binary() const { - return {*this, internal::collate_argument::binary}; - } + template + struct column_field_type> { + using type = typename column_t::field_type; + }; - collate_t collate_nocase() const { - return {*this, internal::collate_argument::nocase}; - } + template + struct column_constraints_type { + using type = std::tuple<>; + }; - collate_t collate_rtrim() const { - return {*this, internal::collate_argument::rtrim}; - } + template + struct column_constraints_type> { + using type = typename column_t::constraints_type; }; - struct in_base { - bool negative = false; // used in not_in + } - operator std::string() const { - if(!this->negative) { - return "IN"; - } else { - return "NOT IN"; - } - } - }; + /** + * Column builder function. You should use it to create columns instead of constructor + */ + template::value>::type, + class... Op> + internal::column_t + make_column(const std::string& name, T O::*m, Op... constraints) { + static_assert(internal::template constraints_size::value == std::tuple_size>::value, + "Incorrect constraints pack"); + static_assert(internal::is_field_member_pointer::value, + "second argument expected as a member field pointer, not member function pointer"); + return {name, m, nullptr, nullptr, std::make_tuple(constraints...)}; + } - /** - * IN operator object. - */ - template - struct in_t : condition_t, in_base, internal::negatable_t { - using self = in_t; + /** + * Column builder function with setter and getter. You should use it to create columns instead of constructor + */ + template::value>::type, + typename = typename std::enable_if::value>::type, + class... Op> + internal::column_t::object_type, + typename internal::setter_traits::field_type, + G, + S, + Op...> + make_column(const std::string& name, S setter, G getter, Op... constraints) { + static_assert(std::is_same::field_type, + typename internal::getter_traits::field_type>::value, + "Getter and setter must get and set same data type"); + static_assert(internal::template constraints_size::value == std::tuple_size>::value, + "Incorrect constraints pack"); + return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; + } - L l; // left expression - A arg; // in arg + /** + * Column builder function with getter and setter (reverse order). You should use it to create columns instead of + * constructor + */ + template::value>::type, + typename = typename std::enable_if::value>::type, + class... Op> + internal::column_t::object_type, + typename internal::setter_traits::field_type, + G, + S, + Op...> + make_column(const std::string& name, G getter, S setter, Op... constraints) { + static_assert(std::is_same::field_type, + typename internal::getter_traits::field_type>::value, + "Getter and setter must get and set same data type"); + static_assert(internal::template constraints_size::value == std::tuple_size>::value, + "Incorrect constraints pack"); + return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; + } - in_t(L l_, A arg_, bool negative_) : in_base{negative_}, l(l_), arg(std::move(arg_)) {} - }; +} +#pragma once - struct is_null_string { - operator std::string() const { - return "IS NULL"; - } - }; +#include // std::string +#include // std::stringstream +#include // std::vector +#include // std::nullptr_t +#include // std::shared_ptr, std::unique_ptr +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - /** - * IS NULL operator object. - */ - template - struct is_null_t : is_null_string, internal::negatable_t { - using self = is_null_t; +namespace sqlite_orm { - T t; + /** + * Is used to print members mapped to objects in storage_t::dump member function. + * Other developers can create own specialization to map custom types + */ + template + struct field_printer { + std::string operator()(const T& t) const { + std::stringstream stream; + stream << t; + return stream.str(); + } + }; - is_null_t(T t_) : t(std::move(t_)) {} - }; + /** + * Upgrade to integer is required when using unsigned char(uint8_t) + */ + template<> + struct field_printer { + std::string operator()(const unsigned char& t) const { + std::stringstream stream; + stream << +t; + return stream.str(); + } + }; - struct is_not_null_string { - operator std::string() const { - return "IS NOT NULL"; + /** + * Upgrade to integer is required when using signed char(int8_t) + */ + template<> + struct field_printer { + std::string operator()(const signed char& t) const { + std::stringstream stream; + stream << +t; + return stream.str(); + } + }; + + /** + * char is neigher signer char nor unsigned char so it has its own specialization + */ + template<> + struct field_printer { + std::string operator()(const char& t) const { + std::stringstream stream; + stream << +t; + return stream.str(); + } + }; + + template<> + struct field_printer { + std::string operator()(const std::string& t) const { + return t; + } + }; + + template<> + struct field_printer, void> { + std::string operator()(const std::vector& t) const { + std::stringstream ss; + ss << std::hex; + for(auto c: t) { + ss << c; } - }; + return ss.str(); + } + }; + + template<> + struct field_printer { + std::string operator()(const std::nullptr_t&) const { + return "null"; + } + }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<> + struct field_printer { + std::string operator()(const std::nullopt_t&) const { + return "null"; + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct field_printer, void> { + std::string operator()(const std::shared_ptr& t) const { + if(t) { + return field_printer()(*t); + } else { + return field_printer()(nullptr); + } + } + }; + + template + struct field_printer, void> { + std::string operator()(const std::unique_ptr& t) const { + if(t) { + return field_printer()(*t); + } else { + return field_printer()(nullptr); + } + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct field_printer, void> { + std::string operator()(const std::optional& t) const { + if(t.has_value()) { + return field_printer()(*t); + } else { + return field_printer()(nullptr); + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +#pragma once + +#include // std::string +#include // std::enable_if, std::is_same +#include // std::vector +#include // std::tuple, std::tuple_size +#include // std::stringstream + +// #include "collate_argument.h" + +// #include "constraints.h" + +// #include "optional_container.h" + +namespace sqlite_orm { + + namespace internal { /** - * IS NOT NULL operator object. + * This is a cute class which allows storing something or nothing + * depending on template argument. Useful for optional class members */ template - struct is_not_null_t : is_not_null_string, internal::negatable_t { - using self = is_not_null_t; + struct optional_container { + using type = T; - T t; + type field; - is_not_null_t(T t_) : t(std::move(t_)) {} + template + void apply(const L& l) const { + l(this->field); + } }; - struct where_string { - operator std::string() const { - return "WHERE"; + template<> + struct optional_container { + using type = void; + + template + void apply(const L&) const { + //.. } }; + } +} - /** - * WHERE argument holder. - * C is conditions type. Can be any condition like: is_equal_t, is_null_t, exists_t etc - */ - template - struct where_t : where_string { - C c; +// #include "tags.h" - where_t(C c_) : c(std::move(c_)) {} - }; +// #include "expression.h" +// #include "operators.h" - template - struct is_where : std::false_type {}; +namespace sqlite_orm { - template - struct is_where> : std::true_type {}; + namespace internal { - struct order_by_base { - int asc_desc = 0; // 1: asc, -1: desc - std::string _collate_argument; - }; + template + struct and_condition_t; - struct order_by_string { - operator std::string() const { - return "ORDER BY"; - } - }; + template + struct or_condition_t; /** - * ORDER BY argument holder. + * Is not an operator but a result of c(...) function. Has operator= overloaded which returns assign_t */ - template - struct order_by_t : order_by_base, order_by_string { - using self = order_by_t; - - O o; + template + struct expression_t : condition_t { + T value; - order_by_t(O o_) : o(std::move(o_)) {} + expression_t(T value_) : value(std::move(value_)) {} - self asc() { - auto res = *this; - res.asc_desc = 1; - return res; + template + assign_t operator=(R r) const { + return {this->value, std::move(r)}; } - self desc() { - auto res = *this; - res.asc_desc = -1; - return res; + assign_t operator=(std::nullptr_t) const { + return {this->value, nullptr}; } - - self collate_binary() const { - auto res = *this; - res._collate_argument = constraints::collate_t::string_from_collate_argument( - sqlite_orm::internal::collate_argument::binary); - return res; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + assign_t operator=(std::nullopt_t) const { + return {this->value, std::nullopt}; + } +#endif + template + in_t in(Args... args) const { + return {this->value, std::make_tuple(std::forward(args)...), false}; } - self collate_nocase() const { - auto res = *this; - res._collate_argument = constraints::collate_t::string_from_collate_argument( - sqlite_orm::internal::collate_argument::nocase); - return res; + template + in_t not_in(Args... args) const { + return {this->value, std::make_tuple(std::forward(args)...), true}; } - self collate_rtrim() const { - auto res = *this; - res._collate_argument = - constraints::collate_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::rtrim); - return res; + template + and_condition_t and_(R right) const { + return {this->value, std::move(right)}; } - self collate(std::string name) const { - auto res = *this; - res._collate_argument = std::move(name); - return res; + template + or_condition_t or_(R right) const { + return {this->value, std::move(right)}; } }; - /** - * ORDER BY pack holder. - */ - template - struct multi_order_by_t : order_by_string { - using args_type = std::tuple; + template + T get_from_expression(T value) { + return std::move(value); + } - args_type args; + template + T get_from_expression(expression_t expression) { + return std::move(expression.value); + } + } - multi_order_by_t(args_type &&args_) : args(std::move(args_)) {} - }; + /** + * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or + * `storage.update(set(c(&User::name) = "Dua Lipa")); + */ + template + internal::expression_t c(T value) { + return {std::move(value)}; + } +} - struct dynamic_order_by_entry_t : order_by_base { - std::string name; +namespace sqlite_orm { - dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) : - order_by_base{asc_desc_, move(collate_argument_)}, name(move(name_)) {} + namespace internal { + + struct limit_string { + operator std::string() const { + return "LIMIT"; + } }; /** - * C - serializator context class + * Stores LIMIT/OFFSET info */ - template - struct dynamic_order_by_t : order_by_string { - using context_t = C; - using entry_t = dynamic_order_by_entry_t; - using const_iterator = typename std::vector::const_iterator; + template + struct limit_t : limit_string { + T lim; + optional_container off; - dynamic_order_by_t(const context_t &context_) : context(context_) {} + limit_t() = default; - template - void push_back(order_by_t order_by) { - auto newContext = this->context; - newContext.skip_table_name = true; - auto columnName = serialize(order_by.o, newContext); - entries.emplace_back(move(columnName), order_by.asc_desc, move(order_by._collate_argument)); - } + limit_t(decltype(lim) lim_) : lim(std::move(lim_)) {} - const_iterator begin() const { - return this->entries.begin(); - } + limit_t(decltype(lim) lim_, decltype(off) off_) : lim(std::move(lim_)), off(std::move(off_)) {} + }; - const_iterator end() const { - return this->entries.end(); - } + template + struct is_limit : std::false_type {}; - void clear() { - this->entries.clear(); - } + template + struct is_limit> : std::true_type {}; - protected: - std::vector entries; - context_t context; + /** + * Stores OFFSET only info + */ + template + struct offset_t { + T off; }; template - struct is_order_by : std::false_type {}; + struct is_offset : std::false_type {}; - template - struct is_order_by> : std::true_type {}; + template + struct is_offset> : std::true_type {}; - template - struct is_order_by> : std::true_type {}; + /** + * Collated something + */ + template + struct collate_t : public condition_t { + T expr; + collate_argument argument; - template - struct is_order_by> : std::true_type {}; + collate_t(T expr_, collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} - struct group_by_string { operator std::string() const { - return "GROUP BY"; + return collate_constraint_t{this->argument}; } }; - /** - * GROUP BY pack holder. - */ - template - struct group_by_t : group_by_string { - using args_type = std::tuple; - args_type args; - - group_by_t(args_type &&args_) : args(std::move(args_)) {} - }; - - template - struct is_group_by : std::false_type {}; - - template - struct is_group_by> : std::true_type {}; + struct named_collate_base { + std::string name; - struct between_string { operator std::string() const { - return "BETWEEN"; + return "COLLATE " + this->name; } }; /** - * BETWEEN operator object. + * Collated something with custom collate function */ - template - struct between_t : condition_t, between_string { - using expression_type = A; - using lower_type = T; - using upper_type = T; - - expression_type expr; - lower_type b1; - upper_type b2; + template + struct named_collate : named_collate_base { + T expr; - between_t(expression_type expr_, lower_type b1_, upper_type b2_) : - expr(std::move(expr_)), b1(std::move(b1_)), b2(std::move(b2_)) {} + named_collate(T expr_, std::string name_) : named_collate_base{std::move(name_)}, expr(std::move(expr_)) {} }; - struct like_string { + struct negated_condition_string { operator std::string() const { - return "LIKE"; + return "NOT"; } }; /** - * LIKE operator object. + * Result of not operator */ - template - struct like_t : condition_t, like_string, internal::negatable_t { - using self = like_t; - using arg_t = A; - using pattern_t = T; - using escape_t = E; - - arg_t arg; - pattern_t pattern; - sqlite_orm::internal::optional_container - arg3; // not escape cause escape exists as a function here - - like_t(arg_t arg_, pattern_t pattern_, sqlite_orm::internal::optional_container escape_) : - arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {} + template + struct negated_condition_t : condition_t, negated_condition_string { + C c; - template - like_t escape(C c) const { - sqlite_orm::internal::optional_container newArg3{std::move(c)}; - return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; - } + negated_condition_t(C c_) : c(std::move(c_)) {} }; - struct glob_string { - operator std::string() const { - return "GLOB"; - } - }; + /** + * Base class for binary conditions + * L is left argument type + * R is right argument type + * S is 'string' class (a class which has cast to `std::string` operator) + * Res is result type + */ + template + struct binary_condition : condition_t, S { + using left_type = L; + using right_type = R; + using result_type = Res; - template - struct glob_t : condition_t, glob_string, internal::negatable_t { - using self = glob_t; - using arg_t = A; - using pattern_t = T; + left_type l; + right_type r; - arg_t arg; - pattern_t pattern; + binary_condition() = default; - glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {} + binary_condition(left_type l_, right_type r_) : l(std::move(l_)), r(std::move(r_)) {} }; - struct cross_join_string { + struct and_condition_string { operator std::string() const { - return "CROSS JOIN"; + return "AND"; } }; /** - * CROSS JOIN holder. - * T is joined type which represents any mapped table. + * Result of and operator */ - template - struct cross_join_t : cross_join_string { - using type = T; + template + struct and_condition_t : binary_condition { + using super = binary_condition; + + using super::super; }; - struct natural_join_string { + template + and_condition_t make_and_condition(L left, R right) { + return {std::move(left), std::move(right)}; + } + + struct or_condition_string { operator std::string() const { - return "NATURAL JOIN"; + return "OR"; } }; /** - * NATURAL JOIN holder. - * T is joined type which represents any mapped table. + * Result of or operator */ - template - struct natural_join_t : natural_join_string { - using type = T; + template + struct or_condition_t : binary_condition { + using super = binary_condition; + + using super::super; }; - struct left_join_string { + template + or_condition_t make_or_condition(L left, R right) { + return {std::move(left), std::move(right)}; + } + + struct is_equal_string { operator std::string() const { - return "LEFT JOIN"; + return "="; } }; /** - * LEFT JOIN holder. - * T is joined type which represents any mapped table. - * O is on(...) argument type. + * = and == operators object */ - template - struct left_join_t : left_join_string { - using type = T; - using on_type = O; + template + struct is_equal_t : binary_condition, negatable_t { + using self = is_equal_t; - on_type constraint; + using binary_condition::binary_condition; - left_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} - }; + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } - struct join_string { - operator std::string() const { - return "JOIN"; + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; } - }; - /** - * Simple JOIN holder. - * T is joined type which represents any mapped table. - * O is on(...) argument type. - */ - template - struct join_t : join_string { - using type = T; - using on_type = O; + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } - on_type constraint; + named_collate collate(std::string name) const { + return {*this, std::move(name)}; + } - join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + template + named_collate collate() const { + std::stringstream ss; + ss << C::name(); + auto name = ss.str(); + ss.flush(); + return {*this, std::move(name)}; + } }; - struct left_outer_join_string { + struct is_not_equal_string { operator std::string() const { - return "LEFT OUTER JOIN"; + return "!="; } }; /** - * LEFT OUTER JOIN holder. - * T is joined type which represents any mapped table. - * O is on(...) argument type. + * != operator object */ - template - struct left_outer_join_t : left_outer_join_string { - using type = T; - using on_type = O; + template + struct is_not_equal_t : binary_condition, negatable_t { + using self = is_not_equal_t; - on_type constraint; + using binary_condition::binary_condition; - left_outer_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } }; - struct on_string { + struct greater_than_string { operator std::string() const { - return "ON"; + return ">"; } }; /** - * on(...) argument holder used for JOIN, LEFT JOIN, LEFT OUTER JOIN and INNER JOIN - * T is on type argument. + * > operator object. */ - template - struct on_t : on_string { - using arg_type = T; + template + struct greater_than_t : binary_condition, negatable_t { + using self = greater_than_t; - arg_type arg; + using binary_condition::binary_condition; - on_t(arg_type arg_) : arg(std::move(arg_)) {} + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; + + struct greater_or_equal_string { + operator std::string() const { + return ">="; + } }; /** - * USING argument holder. + * >= operator object. */ - template - struct using_t { - F O::*column = nullptr; + template + struct greater_or_equal_t : binary_condition, negatable_t { + using self = greater_or_equal_t; - operator std::string() const { - return "USING"; + using binary_condition::binary_condition; + + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; } }; - struct inner_join_string { + struct lesser_than_string { operator std::string() const { - return "INNER JOIN"; + return "<"; } }; /** - * INNER JOIN holder. - * T is joined type which represents any mapped table. - * O is on(...) argument type. + * < operator object. */ - template - struct inner_join_t : inner_join_string { - using type = T; - using on_type = O; + template + struct lesser_than_t : binary_condition, negatable_t { + using self = lesser_than_t; - on_type constraint; + using binary_condition::binary_condition; - inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } }; - struct exists_string { + struct lesser_or_equal_string { operator std::string() const { - return "EXISTS"; + return "<="; } }; - template - struct exists_t : condition_t, exists_string, internal::negatable_t { - using type = T; - using self = exists_t; + /** + * <= operator object. + */ + template + struct lesser_or_equal_t : binary_condition, negatable_t { + using self = lesser_or_equal_t; - type t; + using binary_condition::binary_condition; - exists_t(T t_) : t(std::move(t_)) {} + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } + + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } + + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } }; - struct having_string { + struct in_base { + bool negative = false; // used in not_in + operator std::string() const { - return "HAVING"; + if(!this->negative) { + return "IN"; + } else { + return "NOT IN"; + } } }; /** - * HAVING holder. - * T is having argument type. + * IN operator object. */ - template - struct having_t : having_string { - using type = T; + template + struct dynamic_in_t : condition_t, in_base, negatable_t { + using self = dynamic_in_t; - type t; + L left; // left expression + A argument; // in arg - having_t(type t_) : t(std::move(t_)) {} + dynamic_in_t(L left_, A argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} }; - template - struct is_having : std::false_type {}; + template + struct in_t : condition_t, in_base, negatable_t { + L left; + std::tuple argument; - template - struct is_having> : std::true_type {}; + in_t(L left_, decltype(argument) argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} + }; - struct cast_string { + struct is_null_string { operator std::string() const { - return "CAST"; + return "IS NULL"; } }; /** - * CAST holder. - * T is a type to cast to - * E is an expression type - * Example: cast(&User::id) + * IS NULL operator object. */ - template - struct cast_t : cast_string { - using to_type = T; - using expression_type = E; + template + struct is_null_t : is_null_string, negatable_t { + using self = is_null_t; - expression_type expression; + T t; - cast_t(expression_type expression_) : expression(std::move(expression_)) {} + is_null_t(T t_) : t(std::move(t_)) {} }; - } - - template::value>::type> - internal::negated_condition_t operator!(T arg) { - return {std::move(arg)}; - } + struct is_not_null_string { + operator std::string() const { + return "IS NOT NULL"; + } + }; - /** - * Cute operators for columns - */ - template - internal::lesser_than_t operator<(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; - } + /** + * IS NOT NULL operator object. + */ + template + struct is_not_null_t : is_not_null_string, negatable_t { + using self = is_not_null_t; - template - internal::lesser_than_t operator<(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; - } + T t; - template - internal::lesser_or_equal_t operator<=(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; - } + is_not_null_t(T t_) : t(std::move(t_)) {} + }; - template - internal::lesser_or_equal_t operator<=(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; - } + struct order_by_base { + int asc_desc = 0; // 1: asc, -1: desc + std::string _collate_argument; - template - internal::greater_than_t operator>(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; - } + order_by_base() = default; - template - internal::greater_than_t operator>(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; - } + order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : + asc_desc(asc_desc_), _collate_argument(move(_collate_argument_)) {} + }; - template - internal::greater_or_equal_t operator>=(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; - } + struct order_by_string { + operator std::string() const { + return "ORDER BY"; + } + }; - template - internal::greater_or_equal_t operator>=(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; - } + /** + * ORDER BY argument holder. + */ + template + struct order_by_t : order_by_base, order_by_string { + using expression_type = O; + using self = order_by_t; - template - internal::is_equal_t operator==(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; - } + expression_type expression; - template - internal::is_equal_t operator==(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; - } + order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} - template - internal::is_not_equal_t operator!=(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; - } + self asc() { + auto res = *this; + res.asc_desc = 1; + return res; + } - template - internal::is_not_equal_t operator!=(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; - } + self desc() { + auto res = *this; + res.asc_desc = -1; + return res; + } - template - internal::conc_t operator||(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; - } + self collate_binary() const { + auto res = *this; + res._collate_argument = + collate_constraint_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::binary); + return res; + } - template - internal::conc_t operator||(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; - } + self collate_nocase() const { + auto res = *this; + res._collate_argument = + collate_constraint_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::nocase); + return res; + } - template - internal::conc_t operator||(internal::expression_t l, internal::expression_t r) { - return {std::move(l.t), std::move(r.t)}; - } + self collate_rtrim() const { + auto res = *this; + res._collate_argument = + collate_constraint_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::rtrim); + return res; + } - template - internal::add_t operator+(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; - } + self collate(std::string name) const { + auto res = *this; + res._collate_argument = std::move(name); + return res; + } - template - internal::add_t operator+(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; - } + template + self collate() const { + std::stringstream ss; + ss << C::name(); + auto name = ss.str(); + ss.flush(); + return this->collate(move(name)); + } + }; - template - internal::add_t operator+(internal::expression_t l, internal::expression_t r) { - return {std::move(l.t), std::move(r.t)}; - } + /** + * ORDER BY pack holder. + */ + template + struct multi_order_by_t : order_by_string { + using args_type = std::tuple; - template - internal::sub_t operator-(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; - } + args_type args; - template - internal::sub_t operator-(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; - } + multi_order_by_t(args_type&& args_) : args(std::move(args_)) {} + }; - template - internal::sub_t operator-(internal::expression_t l, internal::expression_t r) { - return {std::move(l.t), std::move(r.t)}; - } + struct dynamic_order_by_entry_t : order_by_base { + std::string name; - template - internal::mul_t operator*(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; - } + dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) : + order_by_base{asc_desc_, move(collate_argument_)}, name(move(name_)) {} + }; - template - internal::mul_t operator*(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; - } + /** + * C - serializator context class + */ + template + struct dynamic_order_by_t : order_by_string { + using context_t = C; + using entry_t = dynamic_order_by_entry_t; + using const_iterator = typename std::vector::const_iterator; - template - internal::mul_t operator*(internal::expression_t l, internal::expression_t r) { - return {std::move(l.t), std::move(r.t)}; - } + dynamic_order_by_t(const context_t& context_) : context(context_) {} - template - internal::div_t operator/(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; - } + template + void push_back(order_by_t order_by) { + auto newContext = this->context; + newContext.skip_table_name = true; + auto columnName = serialize(order_by.expression, newContext); + entries.emplace_back(move(columnName), order_by.asc_desc, move(order_by._collate_argument)); + } - template - internal::div_t operator/(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; - } + const_iterator begin() const { + return this->entries.begin(); + } - template - internal::div_t operator/(internal::expression_t l, internal::expression_t r) { - return {std::move(l.t), std::move(r.t)}; - } + const_iterator end() const { + return this->entries.end(); + } - template - internal::mod_t operator%(internal::expression_t expr, R r) { - return {std::move(expr.t), std::move(r)}; - } + void clear() { + this->entries.clear(); + } - template - internal::mod_t operator%(L l, internal::expression_t expr) { - return {std::move(l), std::move(expr.t)}; - } + protected: + std::vector entries; + context_t context; + }; - template - internal::mod_t operator%(internal::expression_t l, internal::expression_t r) { - return {std::move(l.t), std::move(r.t)}; - } + template + struct is_order_by : std::false_type {}; - template - internal::using_t using_(F O::*p) { - return {std::move(p)}; - } + template + struct is_order_by> : std::true_type {}; - template - internal::on_t on(T t) { - return {std::move(t)}; - } + template + struct is_order_by> : std::true_type {}; - template - internal::cross_join_t cross_join() { - return {}; - } + template + struct is_order_by> : std::true_type {}; - template - internal::natural_join_t natural_join() { - return {}; - } + struct group_by_string { + operator std::string() const { + return "GROUP BY"; + } + }; - template - internal::left_join_t left_join(O o) { - return {std::move(o)}; - } + /** + * GROUP BY pack holder. + */ + template + struct group_by_t : group_by_string { + using args_type = std::tuple; + args_type args; - template - internal::join_t join(O o) { - return {std::move(o)}; - } + group_by_t(args_type&& args_) : args(std::move(args_)) {} + }; - template - internal::left_outer_join_t left_outer_join(O o) { - return {std::move(o)}; - } + template + struct is_group_by : std::false_type {}; - template - internal::inner_join_t inner_join(O o) { - return {std::move(o)}; - } + template + struct is_group_by> : std::true_type {}; - template - internal::offset_t offset(T off) { - return {std::move(off)}; - } + struct between_string { + operator std::string() const { + return "BETWEEN"; + } + }; - template - internal::limit_t limit(T lim) { - return {std::move(lim)}; - } + /** + * BETWEEN operator object. + */ + template + struct between_t : condition_t, between_string { + using expression_type = A; + using lower_type = T; + using upper_type = T; - template - typename std::enable_if::value, internal::limit_t>::type limit(O off, - T lim) { - return {std::move(lim), {std::move(off)}}; - } + expression_type expr; + lower_type b1; + upper_type b2; - template - internal::limit_t limit(T lim, internal::offset_t offt) { - return {std::move(lim), {std::move(offt.off)}}; - } + between_t(expression_type expr_, lower_type b1_, upper_type b2_) : + expr(std::move(expr_)), b1(std::move(b1_)), b2(std::move(b2_)) {} + }; - template::value || - std::is_base_of::value>::type> - internal::and_condition_t operator&&(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct like_string { + operator std::string() const { + return "LIKE"; + } + }; - template::value || - std::is_base_of::value>::type> - internal::or_condition_t operator||(L l, R r) { - return {std::move(l), std::move(r)}; - } + /** + * LIKE operator object. + */ + template + struct like_t : condition_t, like_string, negatable_t { + using self = like_t; + using arg_t = A; + using pattern_t = T; + using escape_t = E; - template - internal::is_not_null_t is_not_null(T t) { - return {std::move(t)}; - } + arg_t arg; + pattern_t pattern; + optional_container arg3; // not escape cause escape exists as a function here - template - internal::is_null_t is_null(T t) { - return {std::move(t)}; - } + like_t(arg_t arg_, pattern_t pattern_, optional_container escape_) : + arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {} - template - internal::in_t> in(L l, std::vector values) { - return {std::move(l), std::move(values), false}; - } - - template - internal::in_t> in(L l, std::initializer_list values) { - return {std::move(l), std::move(values), false}; - } + template + like_t escape(C c) const { + optional_container newArg3{std::move(c)}; + return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; + } + }; - template - internal::in_t in(L l, A arg) { - return {std::move(l), std::move(arg), false}; - } + struct glob_string { + operator std::string() const { + return "GLOB"; + } + }; - template - internal::in_t> not_in(L l, std::vector values) { - return {std::move(l), std::move(values), true}; - } + template + struct glob_t : condition_t, glob_string, internal::negatable_t { + using self = glob_t; + using arg_t = A; + using pattern_t = T; - template - internal::in_t> not_in(L l, std::initializer_list values) { - return {std::move(l), std::move(values), true}; - } + arg_t arg; + pattern_t pattern; - template - internal::in_t not_in(L l, A arg) { - return {std::move(l), std::move(arg), true}; - } + glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {} + }; - template - internal::is_equal_t is_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct cross_join_string { + operator std::string() const { + return "CROSS JOIN"; + } + }; - template - internal::is_equal_t eq(L l, R r) { - return {std::move(l), std::move(r)}; - } + /** + * CROSS JOIN holder. + * T is joined type which represents any mapped table. + */ + template + struct cross_join_t : cross_join_string { + using type = T; + }; - template - internal::is_not_equal_t is_not_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct natural_join_string { + operator std::string() const { + return "NATURAL JOIN"; + } + }; - template - internal::is_not_equal_t ne(L l, R r) { - return {std::move(l), std::move(r)}; - } + /** + * NATURAL JOIN holder. + * T is joined type which represents any mapped table. + */ + template + struct natural_join_t : natural_join_string { + using type = T; + }; - template - internal::greater_than_t greater_than(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct left_join_string { + operator std::string() const { + return "LEFT JOIN"; + } + }; - template - internal::greater_than_t gt(L l, R r) { - return {std::move(l), std::move(r)}; - } + /** + * LEFT JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct left_join_t : left_join_string { + using type = T; + using on_type = O; - template - internal::greater_or_equal_t greater_or_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + on_type constraint; - template - internal::greater_or_equal_t ge(L l, R r) { - return {std::move(l), std::move(r)}; - } + left_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; - template - internal::lesser_than_t lesser_than(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct join_string { + operator std::string() const { + return "JOIN"; + } + }; - template - internal::lesser_than_t lt(L l, R r) { - return {std::move(l), std::move(r)}; - } + /** + * Simple JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct join_t : join_string { + using type = T; + using on_type = O; - template - internal::lesser_or_equal_t lesser_or_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + on_type constraint; - template - internal::lesser_or_equal_t le(L l, R r) { - return {std::move(l), std::move(r)}; - } + join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; - template - internal::where_t where(C c) { - return {std::move(c)}; - } + struct left_outer_join_string { + operator std::string() const { + return "LEFT OUTER JOIN"; + } + }; - /** - * ORDER BY column - * Example: storage.select(&User::name, order_by(&User::id)) - */ - template - internal::order_by_t order_by(O o) { - return {std::move(o)}; - } + /** + * LEFT OUTER JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct left_outer_join_t : left_outer_join_string { + using type = T; + using on_type = O; - /** - * ORDER BY column1, column2 - * Example: storage.get_all(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) - */ - template - internal::multi_order_by_t multi_order_by(Args &&... args) { - return {std::make_tuple(std::forward(args)...)}; - } + on_type constraint; - /** - * ORDER BY column1, column2 - * Difference from `multi_order_by` is that `dynamic_order_by` can be changed at runtime using `push_back` member - * function Example: - * auto orderBy = dynamic_order_by(storage); - * if(someCondition) { - * orderBy.push_back(&User::id); - * } else { - * orderBy.push_back(&User::name); - * orderBy.push_back(&User::birthDate); - * } - */ - template - internal::dynamic_order_by_t> - dynamic_order_by(const S &storage) { - internal::serializator_context_builder builder(storage); - return builder(); - } + left_outer_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; - /** - * GROUP BY column. - * Example: storage.get_all(group_by(&Employee::name)) - */ - template - internal::group_by_t group_by(Args &&... args) { - return {std::make_tuple(std::forward(args)...)}; - } + struct on_string { + operator std::string() const { + return "ON"; + } + }; - /** - * X BETWEEN Y AND Z - * Example: storage.select(between(&User::id, 10, 20)) - */ - template - internal::between_t between(A expr, T b1, T b2) { - return {std::move(expr), std::move(b1), std::move(b2)}; - } + /** + * on(...) argument holder used for JOIN, LEFT JOIN, LEFT OUTER JOIN and INNER JOIN + * T is on type argument. + */ + template + struct on_t : on_string { + using arg_type = T; - /** - * X LIKE Y - * Example: storage.select(like(&User::name, "T%")) - */ - template - internal::like_t like(A a, T t) { - return {std::move(a), std::move(t), {}}; - } + arg_type arg; - /** - * X GLOB Y - * Example: storage.select(glob(&User::name, "*S")) - */ - template - internal::glob_t glob(A a, T t) { - return {std::move(a), std::move(t)}; - } + on_t(arg_type arg_) : arg(std::move(arg_)) {} + }; - /** - * X LIKE Y ESCAPE Z - * Example: storage.select(like(&User::name, "T%", "%")) - */ - template - internal::like_t like(A a, T t, E e) { - return {std::move(a), std::move(t), {std::move(e)}}; - } + /** + * USING argument holder. + */ + template + struct using_t { + F O::*column = nullptr; - /** - * EXISTS(condition). - * Example: storage.select(columns(&Agent::code, &Agent::name, &Agent::workingArea, &Agent::comission), - where(exists(select(asterisk(), - where(is_equal(&Customer::grade, 3) and - is_equal(&Agent::code, &Customer::agentCode))))), - order_by(&Agent::comission)); - */ - template - internal::exists_t exists(T t) { - return {std::move(t)}; - } - - /** - * HAVING(expression). - * Example: storage.get_all(group_by(&Employee::name), having(greater_than(count(&Employee::name), 2))); - */ - template - internal::having_t having(T t) { - return {std::move(t)}; - } + operator std::string() const { + return "USING"; + } + }; - /** - * CAST(X AS type). - * Example: cast(&User::id) - */ - template - internal::cast_t cast(E e) { - return {std::move(e)}; - } -} -#pragma once + struct inner_join_string { + operator std::string() const { + return "INNER JOIN"; + } + }; -#include // std::enable_if, std::is_base_of, std::is_member_pointer -#include // std::stringstream -#include // std::string + /** + * INNER JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct inner_join_t : inner_join_string { + using type = T; + using on_type = O; -namespace sqlite_orm { + on_type constraint; - /** - * This is base class for every class which is used as a custom table alias. - * For more information please look through self_join.cpp example - */ - struct alias_tag {}; + inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; - namespace internal { + struct exists_string { + operator std::string() const { + return "EXISTS"; + } + }; - /** - * This is a common built-in class used for custom single character table aliases. - * Also you can use language aliases `alias_a`, `alias_b` etc. instead - */ - template - struct table_alias : alias_tag { + template + struct exists_t : condition_t, exists_string, internal::negatable_t { using type = T; + using self = exists_t; - static char get() { - return A; + type t; + + exists_t(T t_) : t(std::move(t_)) {} + }; + + struct having_string { + operator std::string() const { + return "HAVING"; } }; /** - * Column expression with table alias attached like 'C.ID'. This is not a column alias + * HAVING holder. + * T is having argument type. */ - template - struct alias_column_t { - using alias_type = T; - using column_type = C; - - column_type column; + template + struct having_t : having_string { + using type = T; - alias_column_t(){}; + type t; - alias_column_t(column_type column_) : column(column_) {} + having_t(type t_) : t(std::move(t_)) {} }; - template - struct alias_extractor; - template - struct alias_extractor::value>::type> { - static std::string get() { - std::stringstream ss; - ss << T::get(); - return ss.str(); - } - }; + struct is_having : std::false_type {}; template - struct alias_extractor::value>::type> { - static std::string get() { - return {}; + struct is_having> : std::true_type {}; + + struct cast_string { + operator std::string() const { + return "CAST"; } }; + /** + * CAST holder. + * T is a type to cast to + * E is an expression type + * Example: cast(&User::id) + */ template - struct as_t { - using alias_type = T; + struct cast_t : cast_string { + using to_type = T; using expression_type = E; expression_type expression; + + cast_t(expression_type expression_) : expression(std::move(expression_)) {} }; - template - struct alias_holder { - using type = T; + template + struct from_t { + using tuple_type = std::tuple; }; + + template + struct is_from : std::false_type {}; + + template + struct is_from> : std::true_type {}; } /** - * @return column with table alias attached. Place it instead of a column statement in case you need to specify a - * column with table alias prefix like 'a.column'. For more information please look through self_join.cpp example + * Explicit FROM function. Usage: + * `storage.select(&User::id, from());` */ - template - internal::alias_column_t alias_column(C c) { - static_assert(std::is_member_pointer::value, - "alias_column argument must be a member pointer mapped to a storage"); - return {c}; + template + internal::from_t from() { + static_assert(std::tuple_size>::value > 0, ""); + return {}; } - template - internal::as_t as(E expression) { - return {std::move(expression)}; + template::value>::type> + internal::negated_condition_t operator!(T arg) { + return {std::move(arg)}; } - template - internal::alias_holder get() { - return {}; + /** + * Cute operators for columns + */ + template + internal::lesser_than_t operator<(internal::expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; } - template - using alias_a = internal::table_alias; - template - using alias_b = internal::table_alias; - template - using alias_c = internal::table_alias; - template - using alias_d = internal::table_alias; - template - using alias_e = internal::table_alias; - template - using alias_f = internal::table_alias; - template - using alias_g = internal::table_alias; - template - using alias_h = internal::table_alias; - template - using alias_i = internal::table_alias; - template - using alias_j = internal::table_alias; - template - using alias_k = internal::table_alias; - template - using alias_l = internal::table_alias; - template - using alias_m = internal::table_alias; - template - using alias_n = internal::table_alias; - template - using alias_o = internal::table_alias; - template - using alias_p = internal::table_alias; - template - using alias_q = internal::table_alias; - template - using alias_r = internal::table_alias; - template - using alias_s = internal::table_alias; - template - using alias_t = internal::table_alias; - template - using alias_u = internal::table_alias; - template - using alias_v = internal::table_alias; - template - using alias_w = internal::table_alias; - template - using alias_x = internal::table_alias; - template - using alias_y = internal::table_alias; - template - using alias_z = internal::table_alias; -} -#pragma once + template + internal::lesser_than_t operator<(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } -// #include "conditions.h" + template + internal::lesser_or_equal_t operator<=(internal::expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } -namespace sqlite_orm { + template + internal::lesser_or_equal_t operator<=(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } - namespace internal { + template + internal::greater_than_t operator>(internal::expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } - template - struct join_iterator { + template + internal::greater_than_t operator>(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } - template - void operator()(const L &) { - //.. - } - }; + template + internal::greater_or_equal_t operator>=(internal::expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } - template<> - struct join_iterator<> { + template + internal::greater_or_equal_t operator>=(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } - template - void operator()(const L &) { - //.. - } - }; + template + internal::is_equal_t operator==(internal::expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } - template - struct join_iterator : public join_iterator { - using super = join_iterator; + template + internal::is_equal_t operator==(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } - template - void operator()(const L &l) { - this->super::operator()(l); - } - }; + template + internal::is_not_equal_t operator!=(internal::expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } - template - struct join_iterator, Tail...> : public join_iterator { - using super = join_iterator; - using join_type = cross_join_t; + template + internal::is_not_equal_t operator!=(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } - template - void operator()(const L &l) { - l(*this); - this->super::operator()(l); - } - }; + template + internal::conc_t operator||(internal::expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } - template - struct join_iterator, Tail...> : public join_iterator { - using super = join_iterator; - using join_type = natural_join_t; + template + internal::conc_t operator||(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } - template - void operator()(const L &l) { - l(*this); - this->super::operator()(l); - } - }; + template + internal::conc_t operator||(internal::expression_t l, internal::expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } - template - struct join_iterator, Tail...> : public join_iterator { - using super = join_iterator; - using join_type = left_join_t; + template + internal::add_t operator+(internal::expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } - template - void operator()(const L &l) { - l(*this); - this->super::operator()(l); - } - }; + template + internal::add_t operator+(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } - template - struct join_iterator, Tail...> : public join_iterator { - using super = join_iterator; - using join_type = join_t; + template + internal::add_t operator+(internal::expression_t l, internal::expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } - template - void operator()(const L &l) { - l(*this); - this->super::operator()(l); - } - }; + template + internal::sub_t operator-(internal::expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } - template - struct join_iterator, Tail...> : public join_iterator { - using super = join_iterator; - using join_type = left_outer_join_t; + template + internal::sub_t operator-(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } - template - void operator()(const L &l) { - l(*this); - this->super::operator()(l); - } - }; + template + internal::sub_t operator-(internal::expression_t l, internal::expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } - template - struct join_iterator, Tail...> : public join_iterator { - using super = join_iterator; - using join_type = inner_join_t; + template + internal::mul_t operator*(internal::expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } - template - void operator()(const L &l) { - l(*this); - this->super::operator()(l); - } - }; + template + internal::mul_t operator*(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.value)}; } -} -#pragma once -#include // std::string -#include // std::make_tuple, std::tuple_size -#include // std::forward, std::is_base_of, std::enable_if -#include // std::unique_ptr -#include // std::vector + template + internal::mul_t operator*(internal::expression_t l, internal::expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } -// #include "conditions.h" + template + internal::div_t operator/(internal::expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } -// #include "operators.h" + template + internal::div_t operator/(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } -// #include "is_base_of_template.h" + template + internal::div_t operator/(internal::expression_t l, internal::expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } -#include // std::true_type, std::false_type, std::declval + template + internal::mod_t operator%(internal::expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } -namespace sqlite_orm { + template + internal::mod_t operator%(L l, internal::expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } - namespace internal { + template + internal::mod_t operator%(internal::expression_t l, internal::expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } - /* - * This is because of bug in MSVC, for more information, please visit - * https://stackoverflow.com/questions/34672441/stdis-base-of-for-template-classes/34672753#34672753 - */ -#if defined(_MSC_VER) - template class Base, typename Derived> - struct is_base_of_template_impl { - template - static constexpr std::true_type test(const Base *); + template + internal::using_t using_(F O::*p) { + return {std::move(p)}; + } - static constexpr std::false_type test(...); + template + internal::on_t on(T t) { + return {std::move(t)}; + } - using type = decltype(test(std::declval())); - }; + template + internal::cross_join_t cross_join() { + return {}; + } - template class Base> - using is_base_of_template = typename is_base_of_template_impl::type; + template + internal::natural_join_t natural_join() { + return {}; + } -#else - template class C, typename... Ts> - std::true_type is_base_of_template_impl(const C *); + template + internal::left_join_t left_join(O o) { + return {std::move(o)}; + } - template class C> - std::false_type is_base_of_template_impl(...); + template + internal::join_t join(O o) { + return {std::move(o)}; + } - template class C> - using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); -#endif + template + internal::left_outer_join_t left_outer_join(O o) { + return {std::move(o)}; } -} -namespace sqlite_orm { + template + internal::inner_join_t inner_join(O o) { + return {std::move(o)}; + } - namespace internal { + template + internal::offset_t offset(T off) { + return {std::move(off)}; + } - template - struct unique_ptr_result_of {}; + template + internal::limit_t limit(T lim) { + return {std::move(lim)}; + } - /** - * Base class for operator overloading - * R - return type - * S - class with operator std::string - * Args - function arguments types - */ - template - struct core_function_t : S, internal::arithmetic_t { - using return_type = R; - using string_type = S; - using args_type = std::tuple; + template + typename std::enable_if::value, internal::limit_t>::type limit(O off, + T lim) { + return {std::move(lim), {std::move(off)}}; + } - static constexpr const size_t args_size = std::tuple_size::value; + template + internal::limit_t limit(T lim, internal::offset_t offt) { + return {std::move(lim), {std::move(offt.off)}}; + } - args_type args; + template::value || + std::is_base_of::value>::type> + auto operator&&(L l, R r) { + using internal::get_from_expression; + return internal::make_and_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); + } - core_function_t(args_type &&args_) : args(std::move(args_)) {} - }; + template + auto and_(L l, R r) { + using internal::get_from_expression; + return internal::make_and_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); + } - struct length_string { - operator std::string() const { - return "LENGTH"; - } - }; + template::value || + std::is_base_of::value>::type> + auto operator||(L l, R r) { + using internal::get_from_expression; + return internal::make_or_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); + } - struct abs_string { - operator std::string() const { - return "ABS"; - } - }; + template + auto or_(L l, R r) { + using internal::get_from_expression; + return internal::make_or_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); + } - struct lower_string { - operator std::string() const { - return "LOWER"; - } - }; + template + internal::is_not_null_t is_not_null(T t) { + return {std::move(t)}; + } - struct upper_string { - operator std::string() const { - return "UPPER"; - } + template + internal::is_null_t is_null(T t) { + return {std::move(t)}; + } + + template + internal::dynamic_in_t> in(L l, std::vector values) { + return {std::move(l), std::move(values), false}; + } + + template + internal::dynamic_in_t> in(L l, std::initializer_list values) { + return {std::move(l), std::move(values), false}; + } + + template + internal::dynamic_in_t in(L l, A arg) { + return {std::move(l), std::move(arg), false}; + } + + template + internal::dynamic_in_t> not_in(L l, std::vector values) { + return {std::move(l), std::move(values), true}; + } + + template + internal::dynamic_in_t> not_in(L l, std::initializer_list values) { + return {std::move(l), std::move(values), true}; + } + + template + internal::dynamic_in_t not_in(L l, A arg) { + return {std::move(l), std::move(arg), true}; + } + + template + internal::is_equal_t is_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::is_equal_t eq(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::is_not_equal_t is_not_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::is_not_equal_t ne(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::greater_than_t greater_than(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::greater_than_t gt(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::greater_or_equal_t greater_or_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::greater_or_equal_t ge(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::lesser_than_t lesser_than(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::lesser_than_t lt(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::lesser_or_equal_t lesser_or_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::lesser_or_equal_t le(L l, R r) { + return {std::move(l), std::move(r)}; + } + + /** + * ORDER BY column + * Example: storage.select(&User::name, order_by(&User::id)) + */ + template + internal::order_by_t order_by(O o) { + return {std::move(o)}; + } + + /** + * ORDER BY column1, column2 + * Example: storage.get_all(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) + */ + template + internal::multi_order_by_t multi_order_by(Args&&... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * ORDER BY column1, column2 + * Difference from `multi_order_by` is that `dynamic_order_by` can be changed at runtime using `push_back` member + * function Example: + * auto orderBy = dynamic_order_by(storage); + * if(someCondition) { + * orderBy.push_back(&User::id); + * } else { + * orderBy.push_back(&User::name); + * orderBy.push_back(&User::birthDate); + * } + */ + template + internal::dynamic_order_by_t> + dynamic_order_by(const S& storage) { + internal::serializator_context_builder builder(storage); + return builder(); + } + + /** + * GROUP BY column. + * Example: storage.get_all(group_by(&Employee::name)) + */ + template + internal::group_by_t group_by(Args&&... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * X BETWEEN Y AND Z + * Example: storage.select(between(&User::id, 10, 20)) + */ + template + internal::between_t between(A expr, T b1, T b2) { + return {std::move(expr), std::move(b1), std::move(b2)}; + } + + /** + * X LIKE Y + * Example: storage.select(like(&User::name, "T%")) + */ + template + internal::like_t like(A a, T t) { + return {std::move(a), std::move(t), {}}; + } + + /** + * X GLOB Y + * Example: storage.select(glob(&User::name, "*S")) + */ + template + internal::glob_t glob(A a, T t) { + return {std::move(a), std::move(t)}; + } + + /** + * X LIKE Y ESCAPE Z + * Example: storage.select(like(&User::name, "T%", "%")) + */ + template + internal::like_t like(A a, T t, E e) { + return {std::move(a), std::move(t), {std::move(e)}}; + } + + /** + * EXISTS(condition). + * Example: storage.select(columns(&Agent::code, &Agent::name, &Agent::workingArea, &Agent::comission), + where(exists(select(asterisk(), + where(is_equal(&Customer::grade, 3) and + is_equal(&Agent::code, &Customer::agentCode))))), + order_by(&Agent::comission)); + */ + template + internal::exists_t exists(T t) { + return {std::move(t)}; + } + + /** + * HAVING(expression). + * Example: storage.get_all(group_by(&Employee::name), having(greater_than(count(&Employee::name), 2))); + */ + template + internal::having_t having(T t) { + return {std::move(t)}; + } + + /** + * CAST(X AS type). + * Example: cast(&User::id) + */ + template + internal::cast_t cast(E e) { + return {std::move(e)}; + } +} +#pragma once + +#include // std::enable_if, std::is_base_of, std::is_member_pointer +#include // std::stringstream +#include // std::string + +namespace sqlite_orm { + + /** + * This is base class for every class which is used as a custom table alias. + * For more information please look through self_join.cpp example + */ + struct alias_tag {}; + + namespace internal { + + /** + * This is a common built-in class used for custom single character table aliases. + * Also you can use language aliases `alias_a`, `alias_b` etc. instead + */ + template + struct table_alias : alias_tag { + using type = T; + + static char get() { + return A; + } + }; + + /** + * Column expression with table alias attached like 'C.ID'. This is not a column alias + */ + template + struct alias_column_t { + using alias_type = T; + using column_type = C; + + column_type column; + + alias_column_t(){}; + + alias_column_t(column_type column_) : column(std::move(column_)) {} + }; + + template + struct alias_extractor; + + template + struct alias_extractor::value>::type> { + static std::string get() { + std::stringstream ss; + ss << T::get(); + return ss.str(); + } + }; + + template + struct alias_extractor::value>::type> { + static std::string get() { + return {}; + } + }; + + /** + * Used to store alias for expression + */ + template + struct as_t { + using alias_type = T; + using expression_type = E; + + expression_type expression; + }; + + template + struct alias_holder { + using type = T; + }; + } + + /** + * @return column with table alias attached. Place it instead of a column statement in case you need to specify a + * column with table alias prefix like 'a.column'. For more information please look through self_join.cpp example + */ + template + internal::alias_column_t alias_column(C c) { + static_assert(std::is_member_pointer::value, + "alias_column argument must be a member pointer mapped to a storage"); + return {c}; + } + + template + internal::as_t as(E expression) { + return {std::move(expression)}; + } + + template + internal::alias_holder get() { + return {}; + } + + template + using alias_a = internal::table_alias; + template + using alias_b = internal::table_alias; + template + using alias_c = internal::table_alias; + template + using alias_d = internal::table_alias; + template + using alias_e = internal::table_alias; + template + using alias_f = internal::table_alias; + template + using alias_g = internal::table_alias; + template + using alias_h = internal::table_alias; + template + using alias_i = internal::table_alias; + template + using alias_j = internal::table_alias; + template + using alias_k = internal::table_alias; + template + using alias_l = internal::table_alias; + template + using alias_m = internal::table_alias; + template + using alias_n = internal::table_alias; + template + using alias_o = internal::table_alias; + template + using alias_p = internal::table_alias; + template + using alias_q = internal::table_alias; + template + using alias_r = internal::table_alias; + template + using alias_s = internal::table_alias; + template + using alias_t = internal::table_alias; + template + using alias_u = internal::table_alias; + template + using alias_v = internal::table_alias; + template + using alias_w = internal::table_alias; + template + using alias_x = internal::table_alias; + template + using alias_y = internal::table_alias; + template + using alias_z = internal::table_alias; +} +#pragma once + +// #include "conditions.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct join_iterator; + + template<> + struct join_iterator<> { + + template + void operator()(const L&) const { + //.. + } + }; + + template + struct join_iterator : public join_iterator { + using super = join_iterator; + + template + void operator()(const L& l) const { + this->super::operator()(l); + } + }; + + template + struct join_iterator, Tail...> : public join_iterator { + using super = join_iterator; + using join_type = cross_join_t; + + template + void operator()(const L& l) const { + l(*this); + this->super::operator()(l); + } + }; + + template + struct join_iterator, Tail...> : public join_iterator { + using super = join_iterator; + using join_type = natural_join_t; + + template + void operator()(const L& l) const { + l(*this); + this->super::operator()(l); + } + }; + + template + struct join_iterator, Tail...> : public join_iterator { + using super = join_iterator; + using join_type = left_join_t; + + template + void operator()(const L& l) const { + l(*this); + this->super::operator()(l); + } + }; + + template + struct join_iterator, Tail...> : public join_iterator { + using super = join_iterator; + using join_type = join_t; + + template + void operator()(const L& l) const { + l(*this); + this->super::operator()(l); + } + }; + + template + struct join_iterator, Tail...> : public join_iterator { + using super = join_iterator; + using join_type = left_outer_join_t; + + template + void operator()(const L& l) const { + l(*this); + this->super::operator()(l); + } + }; + + template + struct join_iterator, Tail...> : public join_iterator { + using super = join_iterator; + using join_type = inner_join_t; + + template + void operator()(const L& l) const { + l(*this); + this->super::operator()(l); + } + }; + } +} +#pragma once + +#include // std::string +#include // std::make_tuple, std::tuple_size +#include // std::forward, std::is_base_of, std::enable_if +#include // std::unique_ptr +#include // std::vector + +// #include "conditions.h" + +// #include "operators.h" + +// #include "is_base_of_template.h" + +#include // std::true_type, std::false_type, std::declval + +namespace sqlite_orm { + + namespace internal { + + /* + * This is because of bug in MSVC, for more information, please visit + * https://stackoverflow.com/questions/34672441/stdis-base-of-for-template-classes/34672753#34672753 + */ +#if defined(_MSC_VER) + template class Base, typename Derived> + struct is_base_of_template_impl { + template + static constexpr std::true_type test(const Base*); + + static constexpr std::false_type test(...); + + using type = decltype(test(std::declval())); + }; + + template class Base> + using is_base_of_template = typename is_base_of_template_impl::type; + +#else + template class C, typename... Ts> + std::true_type is_base_of_template_impl(const C*); + + template class C> + std::false_type is_base_of_template_impl(...); + + template class C> + using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); +#endif + } +} + +// #include "serialize_result_type.h" + +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // string_view +#else +#include // std::string +#endif + +namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + using serialize_result_type = std::string_view; +#else + using serialize_result_type = std::string; +#endif + } +} + +namespace sqlite_orm { + + using int64 = sqlite_int64; + using uint64 = sqlite_uint64; + + namespace internal { + + template + struct is_into; + + template + struct unique_ptr_result_of {}; + + /** + * Base class for operator overloading + * R - return type + * S - class with operator std::string + * Args - function arguments types + */ + template + struct built_in_function_t : S, arithmetic_t { + using return_type = R; + using string_type = S; + using args_type = std::tuple; + + static constexpr const size_t args_size = std::tuple_size::value; + + args_type args; + + built_in_function_t(args_type&& args_) : args(std::move(args_)) {} + }; + + struct typeof_string { + serialize_result_type serialize() const { + return "TYPEOF"; + } + }; + + struct unicode_string { + serialize_result_type serialize() const { + return "UNICODE"; + } + }; + + struct length_string { + serialize_result_type serialize() const { + return "LENGTH"; + } + }; + + struct abs_string { + serialize_result_type serialize() const { + return "ABS"; + } + }; + + struct lower_string { + serialize_result_type serialize() const { + return "LOWER"; + } + }; + + struct upper_string { + serialize_result_type serialize() const { + return "UPPER"; + } + }; + + struct last_insert_rowid_string { + serialize_result_type serialize() const { + return "LAST_INSERT_ROWID"; + } + }; + + struct total_changes_string { + serialize_result_type serialize() const { + return "TOTAL_CHANGES"; + } + }; + + struct changes_string { + serialize_result_type serialize() const { + return "CHANGES"; + } + }; + + struct trim_string { + serialize_result_type serialize() const { + return "TRIM"; + } + }; + + struct ltrim_string { + serialize_result_type serialize() const { + return "LTRIM"; + } + }; + + struct rtrim_string { + serialize_result_type serialize() const { + return "RTRIM"; + } + }; + + struct hex_string { + serialize_result_type serialize() const { + return "HEX"; + } + }; + + struct quote_string { + serialize_result_type serialize() const { + return "QUOTE"; + } + }; + + struct randomblob_string { + serialize_result_type serialize() const { + return "RANDOMBLOB"; + } + }; + + struct instr_string { + serialize_result_type serialize() const { + return "INSTR"; + } + }; + + struct replace_string { + serialize_result_type serialize() const { + return "REPLACE"; + } + }; + + struct round_string { + serialize_result_type serialize() const { + return "ROUND"; + } + }; + +#if SQLITE_VERSION_NUMBER >= 3007016 + + struct char_string { + serialize_result_type serialize() const { + return "CHAR"; + } + }; + + struct random_string { + serialize_result_type serialize() const { + return "RANDOM"; + } + }; + +#endif + + struct coalesce_string { + serialize_result_type serialize() const { + return "COALESCE"; + } + }; + + struct ifnull_string { + serialize_result_type serialize() const { + return "IFNULL"; + } + }; + + struct date_string { + serialize_result_type serialize() const { + return "DATE"; + } + }; + + struct time_string { + serialize_result_type serialize() const { + return "TIME"; + } + }; + + struct datetime_string { + serialize_result_type serialize() const { + return "DATETIME"; + } + }; + + struct julianday_string { + serialize_result_type serialize() const { + return "JULIANDAY"; + } + }; + + struct strftime_string { + serialize_result_type serialize() const { + return "STRFTIME"; + } + }; + + struct zeroblob_string { + serialize_result_type serialize() const { + return "ZEROBLOB"; + } + }; + + struct substr_string { + serialize_result_type serialize() const { + return "SUBSTR"; + } + }; +#ifdef SQLITE_SOUNDEX + struct soundex_string { + serialize_result_type serialize() const { + return "SOUNDEX"; + } + }; +#endif + struct total_string { + serialize_result_type serialize() const { + return "TOTAL"; + } + }; + + struct sum_string { + serialize_result_type serialize() const { + return "SUM"; + } + }; + + struct count_string { + serialize_result_type serialize() const { + return "COUNT"; + } + }; + + /** + * T is use to specify type explicitly for queries like + * SELECT COUNT(*) FROM table_name; + * T can be omitted with void. + */ + template + struct count_asterisk_t : count_string { + using type = T; + }; + + /** + * The same thing as count() but without T arg. + * Is used in cases like this: + * SELECT cust_code, cust_name, cust_city, grade + * FROM customer + * WHERE grade=2 AND EXISTS + * (SELECT COUNT(*) + * FROM customer + * WHERE grade=2 + * GROUP BY grade + * HAVING COUNT(*)>2); + * `c++` + * auto rows = + * storage.select(columns(&Customer::code, &Customer::name, &Customer::city, &Customer::grade), + * where(is_equal(&Customer::grade, 2) + * and exists(select(count(), + * where(is_equal(&Customer::grade, 2)), + * group_by(&Customer::grade), + * having(greater_than(count(), 2)))))); + */ + struct count_asterisk_without_type : count_string {}; + + struct avg_string { + serialize_result_type serialize() const { + return "AVG"; + } + }; + + struct max_string { + serialize_result_type serialize() const { + return "MAX"; + } + }; + + struct min_string { + serialize_result_type serialize() const { + return "MIN"; + } + }; + + struct group_concat_string { + serialize_result_type serialize() const { + return "GROUP_CONCAT"; + } + }; +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + struct acos_string { + serialize_result_type serialize() const { + return "ACOS"; + } + }; + + struct acosh_string { + serialize_result_type serialize() const { + return "ACOSH"; + } + }; + + struct asin_string { + serialize_result_type serialize() const { + return "ASIN"; + } + }; + + struct asinh_string { + serialize_result_type serialize() const { + return "ASINH"; + } + }; + + struct atan_string { + serialize_result_type serialize() const { + return "ATAN"; + } + }; + + struct atan2_string { + serialize_result_type serialize() const { + return "ATAN2"; + } + }; + + struct atanh_string { + serialize_result_type serialize() const { + return "ATANH"; + } + }; + + struct ceil_string { + serialize_result_type serialize() const { + return "CEIL"; + } + }; + + struct ceiling_string { + serialize_result_type serialize() const { + return "CEILING"; + } + }; + + struct cos_string { + serialize_result_type serialize() const { + return "COS"; + } + }; + + struct cosh_string { + serialize_result_type serialize() const { + return "COSH"; + } + }; + + struct degrees_string { + serialize_result_type serialize() const { + return "DEGREES"; + } + }; + + struct exp_string { + serialize_result_type serialize() const { + return "EXP"; + } + }; + + struct floor_string { + serialize_result_type serialize() const { + return "FLOOR"; + } + }; + + struct ln_string { + serialize_result_type serialize() const { + return "LN"; + } + }; + + struct log_string { + serialize_result_type serialize() const { + return "LOG"; + } + }; + + struct log10_string { + serialize_result_type serialize() const { + return "LOG10"; + } + }; + + struct log2_string { + serialize_result_type serialize() const { + return "LOG2"; + } + }; + + struct mod_string { + serialize_result_type serialize() const { + return "MOD"; + } + }; + + struct pi_string { + serialize_result_type serialize() const { + return "PI"; + } + }; + + struct pow_string { + serialize_result_type serialize() const { + return "POW"; + } + }; + + struct power_string { + serialize_result_type serialize() const { + return "POWER"; + } + }; + + struct radians_string { + serialize_result_type serialize() const { + return "RADIANS"; + } + }; + + struct sin_string { + serialize_result_type serialize() const { + return "SIN"; + } + }; + + struct sinh_string { + serialize_result_type serialize() const { + return "SINH"; + } + }; + + struct sqrt_string { + serialize_result_type serialize() const { + return "SQRT"; + } + }; + + struct tan_string { + serialize_result_type serialize() const { + return "TAN"; + } + }; + + struct tanh_string { + serialize_result_type serialize() const { + return "TANH"; + } + }; + + struct trunc_string { + serialize_result_type serialize() const { + return "TRUNC"; + } + }; + +#endif // SQLITE_ENABLE_MATH_FUNCTIONS +#ifdef SQLITE_ENABLE_JSON1 + struct json_string { + serialize_result_type serialize() const { + return "JSON"; + } + }; + + struct json_array_string { + serialize_result_type serialize() const { + return "JSON_ARRAY"; + } + }; + + struct json_array_length_string { + serialize_result_type serialize() const { + return "JSON_ARRAY_LENGTH"; + } + }; + + struct json_extract_string { + serialize_result_type serialize() const { + return "JSON_EXTRACT"; + } + }; + + struct json_insert_string { + serialize_result_type serialize() const { + return "JSON_INSERT"; + } + }; + + struct json_replace_string { + serialize_result_type serialize() const { + return "JSON_REPLACE"; + } + }; + + struct json_set_string { + serialize_result_type serialize() const { + return "JSON_SET"; + } + }; + + struct json_object_string { + serialize_result_type serialize() const { + return "JSON_OBJECT"; + } + }; + + struct json_patch_string { + serialize_result_type serialize() const { + return "JSON_PATCH"; + } + }; + + struct json_remove_string { + serialize_result_type serialize() const { + return "JSON_REMOVE"; + } + }; + + struct json_type_string { + serialize_result_type serialize() const { + return "JSON_TYPE"; + } + }; + + struct json_valid_string { + serialize_result_type serialize() const { + return "JSON_VALID"; + } + }; + + struct json_quote_string { + serialize_result_type serialize() const { + return "JSON_QUOTE"; + } + }; + + struct json_group_array_string { + serialize_result_type serialize() const { + return "JSON_GROUP_ARRAY"; + } }; - struct changes_string { - operator std::string() const { - return "CHANGES"; - } - }; + struct json_group_object_string { + serialize_result_type serialize() const { + return "JSON_GROUP_OBJECT"; + } + }; +#endif // SQLITE_ENABLE_JSON1 + } + + /** + * Cute operators for core functions + */ + template::value>::type> + internal::lesser_than_t operator<(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template::value>::type> + internal::lesser_or_equal_t operator<=(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template::value>::type> + internal::greater_than_t operator>(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template::value>::type> + internal::greater_or_equal_t operator>=(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template::value>::type> + internal::is_equal_t operator==(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template::value>::type> + internal::is_not_equal_t operator!=(F f, R r) { + return {std::move(f), std::move(r)}; + } +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + + /** + * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acos(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t acos(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acos>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t acos(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acosh(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t acosh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acosh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t acosh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asin(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t asin(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asin>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t asin(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asinh(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t asinh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asinh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t asinh(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan(1)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t atan(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan>(1)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t atan(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan2(1, 3)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t atan2(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan2>(1, 3)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t atan2(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - struct trim_string { - operator std::string() const { - return "TRIM"; - } - }; + /** + * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atanh(1)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t atanh(X x) { + return {std::tuple{std::forward(x)}}; + } - struct ltrim_string { - operator std::string() const { - return "LTRIM"; - } - }; + /** + * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atanh>(1)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t atanh(X x) { + return {std::tuple{std::forward(x)}}; + } - struct rtrim_string { - operator std::string() const { - return "RTRIM"; - } - }; + /** + * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceil(&User::rating)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t ceil(X x) { + return {std::tuple{std::forward(x)}}; + } - struct hex_string { - operator std::string() const { - return "HEX"; - } - }; + /** + * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceil>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t ceil(X x) { + return {std::tuple{std::forward(x)}}; + } - struct quote_string { - operator std::string() const { - return "QUOTE"; - } - }; + /** + * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceiling(&User::rating)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t ceiling(X x) { + return {std::tuple{std::forward(x)}}; + } - struct randomblob_string { - operator std::string() const { - return "RANDOMBLOB"; - } - }; + /** + * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceiling>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t ceiling(X x) { + return {std::tuple{std::forward(x)}}; + } - struct instr_string { - operator std::string() const { - return "INSTR"; - } - }; + /** + * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cos(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t cos(X x) { + return {std::tuple{std::forward(x)}}; + } - struct replace_string { - operator std::string() const { - return "REPLACE"; - } - }; + /** + * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cos>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t cos(X x) { + return {std::tuple{std::forward(x)}}; + } - struct round_string { - operator std::string() const { - return "ROUND"; - } - }; + /** + * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cosh(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t cosh(X x) { + return {std::tuple{std::forward(x)}}; + } -#if SQLITE_VERSION_NUMBER >= 3007016 + /** + * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cosh>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t cosh(X x) { + return {std::tuple{std::forward(x)}}; + } - struct char_string { - operator std::string() const { - return "CHAR"; - } - }; + /** + * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees + * + * Example: + * + * auto rows = storage.select(sqlite_orm::degrees(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t degrees(X x) { + return {std::tuple{std::forward(x)}}; + } - struct random_string { - operator std::string() const { - return "RANDOM"; - } - }; + /** + * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::degrees>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t degrees(X x) { + return {std::tuple{std::forward(x)}}; + } -#endif + /** + * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp + * + * Example: + * + * auto rows = storage.select(sqlite_orm::exp(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t exp(X x) { + return {std::tuple{std::forward(x)}}; + } - struct coalesce_string { - operator std::string() const { - return "COALESCE"; - } - }; + /** + * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::exp>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t exp(X x) { + return {std::tuple{std::forward(x)}}; + } - struct date_string { - operator std::string() const { - return "DATE"; - } - }; + /** + * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor + * + * Example: + * + * auto rows = storage.select(sqlite_orm::floor(&User::rating)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t floor(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::floor>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t floor(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ln(200)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t ln(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ln>(200)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t ln(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log(100)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log>(100)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log10(100)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log10(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log10>(100)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log10(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log(10, 100)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log(B b, X x) { + return {std::tuple{std::forward(b), std::forward(x)}}; + } + + /** + * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log>(10, 100)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log(B b, X x) { + return {std::tuple{std::forward(b), std::forward(x)}}; + } + + /** + * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log2(64)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log2(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log2>(64)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log2(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod + * + * Example: + * + * auto rows = storage.select(sqlite_orm::mod_f(6, 5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t mod_f(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - struct time_string { - operator std::string() const { - return "TIME"; - } - }; + /** + * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::mod_f>(6, 5)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t mod_f(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - struct datetime_string { - operator std::string() const { - return "DATETIME"; - } - }; + /** + * PI() function https://www.sqlite.org/lang_mathfunc.html#pi + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pi()); // decltype(rows) is std::vector + */ + inline internal::built_in_function_t pi() { + return {{}}; + } - struct julianday_string { - operator std::string() const { - return "JULIANDAY"; - } - }; + /** + * PI() function https://www.sqlite.org/lang_mathfunc.html#pi + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, etc. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pi()); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t pi() { + return {{}}; + } - struct strftime_string { - operator std::string() const { - return "STRFTIME"; - } - }; + /** + * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pow(2, 5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t pow(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - struct zeroblob_string { - operator std::string() const { - return "ZEROBLOB"; - } - }; + /** + * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pow>(2, 5)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t pow(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - struct substr_string { - operator std::string() const { - return "SUBSTR"; - } - }; -#ifdef SQLITE_SOUNDEX - struct soundex_string { - operator std::string() const { - return "SOUNDEX"; - } - }; -#endif - struct total_string { - operator std::string() const { - return "TOTAL"; - } - }; + /** + * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Example: + * + * auto rows = storage.select(sqlite_orm::power(2, 5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t power(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - struct sum_string { - operator std::string() const { - return "SUM"; - } - }; + /** + * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::power>(2, 5)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t power(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - struct count_string { - operator std::string() const { - return "COUNT"; - } - }; + /** + * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians + * + * Example: + * + * auto rows = storage.select(sqlite_orm::radians(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t radians(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * T is use to specify type explicitly for queries like - * SELECT COUNT(*) FROM table_name; - * T can be omitted with void. - */ - template - struct count_asterisk_t : count_string { - using type = T; - }; + /** + * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::radians>(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t radians(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * The same thing as count() but without T arg. - * Is used in cases like this: - * SELECT cust_code, cust_name, cust_city, grade - * FROM customer - * WHERE grade=2 AND EXISTS - * (SELECT COUNT(*) - * FROM customer - * WHERE grade=2 - * GROUP BY grade - * HAVING COUNT(*)>2); - * `c++` - * auto rows = - * storage.select(columns(&Customer::code, &Customer::name, &Customer::city, &Customer::grade), - * where(is_equal(&Customer::grade, 2) - * and exists(select(count(), - * where(is_equal(&Customer::grade, 2)), - * group_by(&Customer::grade), - * having(greater_than(count(), 2)))))); - */ - struct count_asterisk_without_type : count_string {}; + /** + * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sin(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sin(X x) { + return {std::tuple{std::forward(x)}}; + } - struct avg_string { - operator std::string() const { - return "AVG"; - } - }; + /** + * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sin>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t sin(X x) { + return {std::tuple{std::forward(x)}}; + } - struct max_string { - operator std::string() const { - return "MAX"; - } - }; + /** + * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sinh(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sinh(X x) { + return {std::tuple{std::forward(x)}}; + } - struct min_string { - operator std::string() const { - return "MIN"; - } - }; + /** + * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sinh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t sinh(X x) { + return {std::tuple{std::forward(x)}}; + } - struct group_concat_string { - operator std::string() const { - return "GROUP_CONCAT"; - } - }; + /** + * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sqrt(25)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sqrt(X x) { + return {std::tuple{std::forward(x)}}; + } + /** + * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sqrt(25)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sqrt(X x) { + return {std::tuple{std::forward(x)}}; } /** - * Cute operators for core functions + * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tan(&Triangle::cornerC)); // decltype(rows) is std::vector */ + template + internal::built_in_function_t tan(X x) { + return {std::tuple{std::forward(x)}}; + } - template< - class F, - class R, - typename = typename std::enable_if::value>::type> - internal::lesser_than_t operator<(F f, R r) { - return {std::move(f), std::move(r)}; + /** + * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tan(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tan(X x) { + return {std::tuple{std::forward(x)}}; } - template< - class F, - class R, - typename = typename std::enable_if::value>::type> - internal::lesser_or_equal_t operator<=(F f, R r) { - return {std::move(f), std::move(r)}; + /** + * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tanh(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tanh(X x) { + return {std::tuple{std::forward(x)}}; } - template< - class F, - class R, - typename = typename std::enable_if::value>::type> - internal::greater_than_t operator>(F f, R r) { - return {std::move(f), std::move(r)}; + /** + * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tanh(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tanh(X x) { + return {std::tuple{std::forward(x)}}; } - template< - class F, - class R, - typename = typename std::enable_if::value>::type> - internal::greater_or_equal_t operator>=(F f, R r) { - return {std::move(f), std::move(r)}; + /** + * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc + * + * Example: + * + * auto rows = storage.select(sqlite_orm::trunc(5.5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t trunc(X x) { + return {std::tuple{std::forward(x)}}; } - template< - class F, - class R, - typename = typename std::enable_if::value>::type> - internal::is_equal_t operator==(F f, R r) { - return {std::move(f), std::move(r)}; + /** + * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::trunc(5.5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t trunc(X x) { + return {std::tuple{std::forward(x)}}; + } +#endif // SQLITE_ENABLE_MATH_FUNCTIONS + /** + * TYPEOF(x) function https://sqlite.org/lang_corefunc.html#typeof + */ + template + internal::built_in_function_t typeof_(T t) { + return {std::tuple{std::forward(t)}}; } - template< - class F, - class R, - typename = typename std::enable_if::value>::type> - internal::is_not_equal_t operator!=(F f, R r) { - return {std::move(f), std::move(r)}; + /** + * UNICODE(x) function https://sqlite.org/lang_corefunc.html#unicode + */ + template + internal::built_in_function_t unicode(T t) { + return {std::tuple{std::forward(t)}}; } /** * LENGTH(x) function https://sqlite.org/lang_corefunc.html#length */ template - internal::core_function_t length(T t) { - std::tuple args{std::forward(t)}; - return {move(args)}; + internal::built_in_function_t length(T t) { + return {std::tuple{std::forward(t)}}; } /** * ABS(x) function https://sqlite.org/lang_corefunc.html#abs */ template - internal::core_function_t, internal::abs_string, T> abs(T t) { - std::tuple args{std::forward(t)}; - return {move(args)}; + internal::built_in_function_t, internal::abs_string, T> abs(T t) { + return {std::tuple{std::forward(t)}}; } /** * LOWER(x) function https://sqlite.org/lang_corefunc.html#lower */ template - internal::core_function_t lower(T t) { - std::tuple args{std::forward(t)}; - return {move(args)}; + internal::built_in_function_t lower(T t) { + return {std::tuple{std::forward(t)}}; } /** * UPPER(x) function https://sqlite.org/lang_corefunc.html#upper */ template - internal::core_function_t upper(T t) { - std::tuple args{std::forward(t)}; - return {move(args)}; + internal::built_in_function_t upper(T t) { + return {std::tuple{std::forward(t)}}; + } + + /** + * LAST_INSERT_ROWID(x) function https://www.sqlite.org/lang_corefunc.html#last_insert_rowid + */ + inline internal::built_in_function_t last_insert_rowid() { + return {{}}; + } + + /** + * TOTAL_CHANGES() function https://sqlite.org/lang_corefunc.html#total_changes + */ + inline internal::built_in_function_t total_changes() { + return {{}}; } /** * CHANGES() function https://sqlite.org/lang_corefunc.html#changes */ - inline internal::core_function_t changes() { + inline internal::built_in_function_t changes() { return {{}}; } @@ -3820,117 +5643,106 @@ namespace sqlite_orm { * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim */ template - internal::core_function_t trim(T t) { - std::tuple args{std::forward(t)}; - return {move(args)}; + internal::built_in_function_t trim(T t) { + return {std::tuple{std::forward(t)}}; } /** * TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim */ template - internal::core_function_t trim(X x, Y y) { - std::tuple args{std::forward(x), std::forward(y)}; - return {move(args)}; + internal::built_in_function_t trim(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } /** * LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim */ template - internal::core_function_t ltrim(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t ltrim(X x) { + return {std::tuple{std::forward(x)}}; } /** * LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim */ template - internal::core_function_t ltrim(X x, Y y) { - std::tuple args{std::forward(x), std::forward(y)}; - return {move(args)}; + internal::built_in_function_t ltrim(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } /** * RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim */ template - internal::core_function_t rtrim(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t rtrim(X x) { + return {std::tuple{std::forward(x)}}; } /** * RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim */ template - internal::core_function_t rtrim(X x, Y y) { - std::tuple args{std::forward(x), std::forward(y)}; - return {move(args)}; + internal::built_in_function_t rtrim(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } /** * HEX(X) function https://sqlite.org/lang_corefunc.html#hex */ template - internal::core_function_t hex(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t hex(X x) { + return {std::tuple{std::forward(x)}}; } /** * QUOTE(X) function https://sqlite.org/lang_corefunc.html#quote */ template - internal::core_function_t quote(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t quote(X x) { + return {std::tuple{std::forward(x)}}; } /** * RANDOMBLOB(X) function https://sqlite.org/lang_corefunc.html#randomblob */ template - internal::core_function_t, internal::randomblob_string, X> randomblob(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t, internal::randomblob_string, X> randomblob(X x) { + return {std::tuple{std::forward(x)}}; } /** * INSTR(X) function https://sqlite.org/lang_corefunc.html#instr */ template - internal::core_function_t instr(X x, Y y) { - std::tuple args{std::forward(x), std::forward(y)}; - return {move(args)}; + internal::built_in_function_t instr(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } /** * REPLACE(X) function https://sqlite.org/lang_corefunc.html#replace */ template - internal::core_function_t replace(X x, Y y, Z z) { - std::tuple args{std::forward(x), std::forward(y), std::forward(z)}; - return {move(args)}; + typename std::enable_if, internal::is_into>::value == 0, + internal::built_in_function_t>::type + replace(X x, Y y, Z z) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(z)}}; } /** * ROUND(X) function https://sqlite.org/lang_corefunc.html#round */ template - internal::core_function_t round(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t round(X x) { + return {std::tuple{std::forward(x)}}; } /** * ROUND(X, Y) function https://sqlite.org/lang_corefunc.html#round */ template - internal::core_function_t round(X x, Y y) { - std::tuple args{std::forward(x), std::forward(y)}; - return {move(args)}; + internal::built_in_function_t round(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } #if SQLITE_VERSION_NUMBER >= 3007016 @@ -3939,14 +5751,14 @@ namespace sqlite_orm { * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char */ template - internal::core_function_t char_(Args... args) { + internal::built_in_function_t char_(Args... args) { return {std::make_tuple(std::forward(args)...)}; } /** * RANDOM() function https://www.sqlite.org/lang_corefunc.html#random */ - inline internal::core_function_t random() { + inline internal::built_in_function_t random() { return {{}}; } @@ -3956,80 +5768,80 @@ namespace sqlite_orm { * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce */ template - internal::core_function_t coalesce(Args... args) { + internal::built_in_function_t coalesce(Args... args) { return {std::make_tuple(std::forward(args)...)}; } + /** + * IFNULL(X,Y) function https://www.sqlite.org/lang_corefunc.html#ifnull + */ + template + internal::built_in_function_t ifnull(X x, Y y) { + return {std::make_tuple(std::move(x), std::move(y))}; + } + /** * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - internal::core_function_t date(Args... args) { - std::tuple t{std::forward(args)...}; - return {move(t)}; + internal::built_in_function_t date(Args... args) { + return {std::tuple{std::forward(args)...}}; } /** * TIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - internal::core_function_t time(Args... args) { - std::tuple t{std::forward(args)...}; - return {move(t)}; + internal::built_in_function_t time(Args... args) { + return {std::tuple{std::forward(args)...}}; } /** * DATETIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - internal::core_function_t datetime(Args... args) { - std::tuple t{std::forward(args)...}; - return {move(t)}; + internal::built_in_function_t datetime(Args... args) { + return {std::tuple{std::forward(args)...}}; } /** * JULIANDAY(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - internal::core_function_t julianday(Args... args) { - std::tuple t{std::forward(args)...}; - return {move(t)}; + internal::built_in_function_t julianday(Args... args) { + return {std::tuple{std::forward(args)...}}; } /** * STRFTIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - internal::core_function_t strftime(Args... args) { - std::tuple t{std::forward(args)...}; - return {move(t)}; + internal::built_in_function_t strftime(Args... args) { + return {std::tuple{std::forward(args)...}}; } /** * ZEROBLOB(N) function https://www.sqlite.org/lang_corefunc.html#zeroblob */ template - internal::core_function_t, internal::zeroblob_string, N> zeroblob(N n) { - std::tuple args{std::forward(n)}; - return {move(args)}; + internal::built_in_function_t, internal::zeroblob_string, N> zeroblob(N n) { + return {std::tuple{std::forward(n)}}; } /** * SUBSTR(X,Y) function https://www.sqlite.org/lang_corefunc.html#substr */ template - internal::core_function_t substr(X x, Y y) { - std::tuple args{std::forward(x), std::forward(y)}; - return {move(args)}; + internal::built_in_function_t substr(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } /** * SUBSTR(X,Y,Z) function https://www.sqlite.org/lang_corefunc.html#substr */ template - internal::core_function_t substr(X x, Y y, Z z) { - std::tuple args{std::forward(x), std::forward(y), std::forward(z)}; - return {move(args)}; + internal::built_in_function_t substr(X x, Y y, Z z) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(z)}}; } #ifdef SQLITE_SOUNDEX @@ -4038,8 +5850,7 @@ namespace sqlite_orm { */ template internal::core_function_t soundex(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + return {std::tuple{std::forward(x)}}; } #endif @@ -4047,27 +5858,24 @@ namespace sqlite_orm { * TOTAL(X) aggregate function. */ template - internal::core_function_t total(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t total(X x) { + return {std::tuple{std::forward(x)}}; } /** * SUM(X) aggregate function. */ template - internal::core_function_t, internal::sum_string, X> sum(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t, internal::sum_string, X> sum(X x) { + return {std::tuple{std::forward(x)}}; } /** * COUNT(X) aggregate function. */ template - internal::core_function_t count(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t count(X x) { + return {std::tuple{std::forward(x)}}; } /** @@ -4090,47 +5898,164 @@ namespace sqlite_orm { * AVG(X) aggregate function. */ template - internal::core_function_t avg(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t avg(X x) { + return {std::tuple{std::forward(x)}}; } /** * MAX(X) aggregate function. */ template - internal::core_function_t, internal::max_string, X> max(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t, internal::max_string, X> max(X x) { + return {std::tuple{std::forward(x)}}; } /** * MIN(X) aggregate function. */ template - internal::core_function_t, internal::min_string, X> min(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t, internal::min_string, X> min(X x) { + return {std::tuple{std::forward(x)}}; } /** * GROUP_CONCAT(X) aggregate function. */ template - internal::core_function_t group_concat(X x) { - std::tuple args{std::forward(x)}; - return {move(args)}; + internal::built_in_function_t group_concat(X x) { + return {std::tuple{std::forward(x)}}; } /** * GROUP_CONCAT(X, Y) aggregate function. */ template - internal::core_function_t group_concat(X x, Y y) { - std::tuple args{std::forward(x), std::forward(y)}; - return {move(args)}; + internal::built_in_function_t group_concat(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } +#ifdef SQLITE_ENABLE_JSON1 + template + internal::built_in_function_t json(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_array(Args... args) { + return {std::tuple{std::forward(args)...}}; + } + + template + internal::built_in_function_t json_array_length(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_array_length(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_array_length(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_array_length(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_extract(X x, Args... args) { + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_insert(X x, + Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_insert must be odd"); + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_replace(X x, + Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_replace must be odd"); + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_set(X x, Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_set must be odd"); + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_object(Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_object must be even"); + return {std::tuple{std::forward(args)...}}; + } + + template + internal::built_in_function_t json_patch(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_remove(X x, + Args... args) { + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_remove(X x, Args... args) { + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_type(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_type(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_type(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_type(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_valid(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_quote(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_group_array(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_group_object(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } +#endif // SQLITE_ENABLE_JSON1 template::value + @@ -4187,20 +6112,20 @@ namespace sqlite_orm { */ template struct typed_comparator { - bool operator()(const L &, const R &) const { + bool operator()(const L&, const R&) const { return false; } }; template struct typed_comparator { - bool operator()(const O &lhs, const O &rhs) const { + bool operator()(const O& lhs, const O& rhs) const { return lhs == rhs; } }; template - bool compare_any(const L &lhs, const R &rhs) { + bool compare_any(const L& lhs, const R& rhs) { return typed_comparator()(lhs, rhs); } } @@ -4210,41 +6135,127 @@ namespace sqlite_orm { #include // std::string #include // std::declval #include // std::tuple, std::get, std::tuple_size +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED // #include "is_base_of_template.h" -// #include "tuple_helper.h" +// #include "tuple_helper/tuple_helper.h" // #include "optional_container.h" +// #include "ast/where.h" + +// #include "../serialize_result_type.h" + +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // string_view +#else +#include // std::string +#endif + namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + using serialize_result_type = std::string_view; +#else + using serialize_result_type = std::string; +#endif + } +} +namespace sqlite_orm { namespace internal { + struct where_string { + serialize_result_type serialize() const { + return "WHERE"; + } + }; + /** - * DISCTINCT generic container. + * WHERE argument holder. + * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc + * Don't construct it manually. Call `where(...)` function instead. */ + template + struct where_t : where_string { + using expression_type = C; + + expression_type expression; + + where_t(expression_type expression_) : expression(std::move(expression_)) {} + }; + template - struct distinct_t { - T t; + struct is_where : std::false_type {}; + + template + struct is_where> : std::true_type {}; + } + + /** + * WHERE clause. Use it to add WHERE conditions wherever you like. + * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc + * @example + * // SELECT name + * // FROM letters + * // WHERE id > 3 + * auto rows = storage.select(&Letter::name, where(greater_than(&Letter::id, 3))); + */ + template + internal::where_t where(C expression) { + return {std::move(expression)}; + } +} + +namespace sqlite_orm { + + namespace internal { +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct as_optional_t { + using value_type = T; + value_type value; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + struct distinct_string { operator std::string() const { return "DISTINCT"; } }; /** - * ALL generic container. + * DISCTINCT generic container. */ template - struct all_t { - T t; + struct distinct_t : distinct_string { + using value_type = T; + value_type value; + + distinct_t(value_type value_) : value(std::move(value_)) {} + }; + + struct all_string { operator std::string() const { return "ALL"; } }; + /** + * ALL generic container. + */ + template + struct all_t : all_string { + T value; + + all_t(T value_) : value(std::move(value_)) {} + }; + template struct columns_t { using columns_type = std::tuple; @@ -4255,6 +6266,12 @@ namespace sqlite_orm { static constexpr const int count = std::tuple_size::value; }; + template + struct is_columns : std::false_type {}; + + template + struct is_columns> : std::true_type {}; + struct set_string { operator std::string() const { return "SET"; @@ -4276,10 +6293,41 @@ namespace sqlite_orm { */ template struct column_pointer { + using self = column_pointer; using type = T; using field_type = F; field_type field; + + template + internal::is_equal_t operator==(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + internal::is_not_equal_t operator!=(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + internal::lesser_than_t operator<(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + internal::lesser_or_equal_t operator<=(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + internal::greater_than_t operator>(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + internal::greater_or_equal_t operator>=(R rhs) const { + return {*this, std::move(rhs)}; + } }; /** @@ -4295,6 +6343,12 @@ namespace sqlite_orm { bool highest_level = false; }; + template + struct is_select : std::false_type {}; + + template + struct is_select> : std::true_type {}; + /** * Base for UNION, UNION ALL, EXCEPT and INTERSECT */ @@ -4338,48 +6392,51 @@ namespace sqlite_orm { union_t(left_type l, right_type r) : union_t(std::move(l), std::move(r), false) {} }; + struct except_string { + operator std::string() const { + return "EXCEPT"; + } + }; + /** * EXCEPT object type. */ template - struct except_t : public compound_operator { + struct except_t : compound_operator, except_string { using super = compound_operator; using left_type = typename super::left_type; using right_type = typename super::right_type; using super::super; + }; + struct intersect_string { operator std::string() const { - return "EXCEPT"; + return "INTERSECT"; } }; - /** * INTERSECT object type. */ template - struct intersect_t : public compound_operator { + struct intersect_t : compound_operator, intersect_string { using super = compound_operator; using left_type = typename super::left_type; using right_type = typename super::right_type; using super::super; - - operator std::string() const { - return "INTERSECT"; - } }; /** * Generic way to get DISTINCT value from any type. */ template - bool get_distinct(const T &) { + bool get_distinct(const T&) { return false; } template - bool get_distinct(const columns_t &cols) { + bool get_distinct(const columns_t& cols) { return cols.distinct; } @@ -4454,9 +6511,16 @@ namespace sqlite_orm { static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 GROUP BY blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 ORDER BY blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 LIMIT blocks"); + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 FROM blocks"); } } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + internal::as_optional_t as_optional(T value) { + return {std::move(value)}; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED template internal::then_t then(T t) { return {std::move(t)}; @@ -4587,48 +6651,6 @@ namespace sqlite_orm { } #pragma once -#include // std::enable_if, std::is_member_pointer - -// #include "select_constraints.h" - -// #include "column.h" - -namespace sqlite_orm { - - namespace internal { - - /** - * Trait class used to define table mapped type by setter/getter/member - * T - member pointer - */ - template - struct table_type; - - template - struct table_type::value && - !std::is_member_function_pointer::value>::type> { - using type = O; - }; - - template - struct table_type::value>::type> { - using type = typename getter_traits::object_type; - }; - - template - struct table_type::value>::type> { - using type = typename setter_traits::object_type; - }; - - template - struct table_type, void> { - using type = T; - }; - } -} -#pragma once - #include // std::string namespace sqlite_orm { @@ -4640,27 +6662,32 @@ namespace sqlite_orm { bool notnull = false; std::string dflt_value; int pk = 0; + + table_info(decltype(cid) cid_, + decltype(name) name_, + decltype(type) type_, + decltype(notnull) notnull_, + decltype(dflt_value) dflt_value_, + decltype(pk) pk_) : + cid(cid_), + name(move(name_)), type(move(type_)), notnull(notnull_), dflt_value(move(dflt_value_)), pk(pk_) {} }; } #pragma once +#include // std::unique_ptr #include +#include // std::integral_constant namespace sqlite_orm { /** * Guard class which finalizes `sqlite3_stmt` in dtor */ - struct statement_finalizer { - sqlite3_stmt *stmt = nullptr; + using statement_finalizer = + std::unique_ptr>; - statement_finalizer(decltype(stmt) stmt_) : stmt(stmt_) {} - - inline ~statement_finalizer() { - sqlite3_finalize(this->stmt); - } - }; } #pragma once @@ -4697,6 +6724,7 @@ namespace sqlite_orm { #include // std::nullptr_t #include // std::declval #include // std::wstring_convert +#include // ::strncpy, ::strlen // #include "is_std_ptr.h" @@ -4712,7 +6740,7 @@ namespace sqlite_orm { struct is_std_ptr> : std::true_type { using element_type = T; - static std::shared_ptr make(const T &v) { + static std::shared_ptr make(const T& v) { return std::make_shared(v); } }; @@ -4721,7 +6749,7 @@ namespace sqlite_orm { struct is_std_ptr> : std::true_type { using element_type = T; - static std::unique_ptr make(const T &v) { + static std::unique_ptr make(const T& v) { return std::make_unique(v); } }; @@ -4740,24 +6768,41 @@ namespace sqlite_orm { */ template struct statement_binder::value>> { - int bind(sqlite3_stmt *stmt, int index, const V &value) { - return bind(stmt, index, value, tag()); + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + return this->bind(stmt, index, value, tag()); + } + + void result(sqlite3_context* context, const V& value) const { + this->result(context, value, tag()); } private: using tag = arithmetic_tag_t; - int bind(sqlite3_stmt *stmt, int index, const V &value, const int_or_smaller_tag &) { + int bind(sqlite3_stmt* stmt, int index, const V& value, const int_or_smaller_tag&) const { return sqlite3_bind_int(stmt, index, static_cast(value)); } - int bind(sqlite3_stmt *stmt, int index, const V &value, const bigint_tag &) { + void result(sqlite3_context* context, const V& value, const int_or_smaller_tag&) const { + sqlite3_result_int(context, static_cast(value)); + } + + int bind(sqlite3_stmt* stmt, int index, const V& value, const bigint_tag&) const { return sqlite3_bind_int64(stmt, index, static_cast(value)); } - int bind(sqlite3_stmt *stmt, int index, const V &value, const real_tag &) { + void result(sqlite3_context* context, const V& value, const bigint_tag&) const { + sqlite3_result_int64(context, static_cast(value)); + } + + int bind(sqlite3_stmt* stmt, int index, const V& value, const real_tag&) const { return sqlite3_bind_double(stmt, index, static_cast(value)); } + + void result(sqlite3_context* context, const V& value, const real_tag&) const { + sqlite3_result_double(context, static_cast(value)); + } }; /** @@ -4766,18 +6811,33 @@ namespace sqlite_orm { template struct statement_binder< V, - std::enable_if_t::value || std::is_same::value>> { - int bind(sqlite3_stmt *stmt, int index, const V &value) { - return sqlite3_bind_text(stmt, index, string_data(value), -1, SQLITE_TRANSIENT); + std::enable_if_t::value || std::is_same::value>> { + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + auto stringData = this->string_data(value); + return sqlite3_bind_text(stmt, index, std::get<0>(stringData), std::get<1>(stringData), SQLITE_TRANSIENT); + } + + void result(sqlite3_context* context, const V& value) const { + auto stringData = this->string_data(value); + auto stringDataLength = std::get<1>(stringData); + auto dataCopy = new char[stringDataLength + 1]; + auto stringChars = std::get<0>(stringData); + ::strncpy(dataCopy, stringChars, stringDataLength + 1); + sqlite3_result_text(context, dataCopy, stringDataLength, [](void* pointer) { + auto charPointer = (char*)pointer; + delete[] charPointer; + }); } private: - const char *string_data(const std::string &s) const { - return s.c_str(); + std::tuple string_data(const std::string& s) const { + return {s.c_str(), int(s.size())}; } - const char *string_data(const char *s) const { - return s; + std::tuple string_data(const char* s) const { + auto length = int(::strlen(s)); + return {s, length}; } }; @@ -4788,12 +6848,17 @@ namespace sqlite_orm { template struct statement_binder< V, - std::enable_if_t::value || std::is_same::value>> { - int bind(sqlite3_stmt *stmt, int index, const V &value) { + std::enable_if_t::value || std::is_same::value>> { + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { std::wstring_convert> converter; std::string utf8Str = converter.to_bytes(value); return statement_binder().bind(stmt, index, utf8Str); } + + void result(sqlite3_context* context, const V& value) const { + sqlite3_result_text16(context, (const void*)value.data(), int(value.length()), nullptr); + } }; #endif // SQLITE_ORM_OMITS_CODECVT @@ -4802,17 +6867,28 @@ namespace sqlite_orm { */ template<> struct statement_binder { - int bind(sqlite3_stmt *stmt, int index, const std::nullptr_t &) { + int bind(sqlite3_stmt* stmt, int index, const std::nullptr_t&) const { return sqlite3_bind_null(stmt, index); } + + void result(sqlite3_context* context, const std::nullptr_t&) const { + sqlite3_result_null(context); + } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * Specialization for std::nullopt_t. + */ template<> struct statement_binder { - int bind(sqlite3_stmt *stmt, int index, const std::nullopt_t &) { + int bind(sqlite3_stmt* stmt, int index, const std::nullopt_t&) const { return sqlite3_bind_null(stmt, index); } + + void result(sqlite3_context* context, const std::nullopt_t&) const { + sqlite3_result_null(context); + } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -4820,13 +6896,21 @@ namespace sqlite_orm { struct statement_binder::value>> { using value_type = typename is_std_ptr::element_type; - int bind(sqlite3_stmt *stmt, int index, const V &value) { + int bind(sqlite3_stmt* stmt, int index, const V& value) const { if(value) { return statement_binder().bind(stmt, index, *value); } else { return statement_binder().bind(stmt, index, nullptr); } } + + void result(sqlite3_context* context, const V& value) const { + if(value) { + statement_binder().result(context, value); + } else { + statement_binder().result(context, nullptr); + } + } }; /** @@ -4834,17 +6918,21 @@ namespace sqlite_orm { */ template<> struct statement_binder, void> { - int bind(sqlite3_stmt *stmt, int index, const std::vector &value) { + int bind(sqlite3_stmt* stmt, int index, const std::vector& value) const { if(value.size()) { - return sqlite3_bind_blob(stmt, - index, - (const void *)&value.front(), - int(value.size()), - SQLITE_TRANSIENT); + return sqlite3_bind_blob(stmt, index, (const void*)&value.front(), int(value.size()), SQLITE_TRANSIENT); } else { return sqlite3_bind_blob(stmt, index, "", 0, SQLITE_TRANSIENT); } } + + void result(sqlite3_context* context, const std::vector& value) const { + if(value.size()) { + sqlite3_result_blob(context, (const void*)&value.front(), int(value.size()), nullptr); + } else { + sqlite3_result_blob(context, "", 0, nullptr); + } + } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -4852,13 +6940,21 @@ namespace sqlite_orm { struct statement_binder, void> { using value_type = T; - int bind(sqlite3_stmt *stmt, int index, const std::optional &value) { + int bind(sqlite3_stmt* stmt, int index, const std::optional& value) const { if(value) { return statement_binder().bind(stmt, index, *value); } else { return statement_binder().bind(stmt, index, std::nullopt); } } + + void result(sqlite3_context* context, const std::optional& value) const { + if(value) { + statement_binder().result(context, value); + } else { + statement_binder().result(context, std::nullopt); + } + } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -4868,8 +6964,8 @@ namespace sqlite_orm { using is_bindable = std::integral_constant>::value>; struct conditional_binder_base { - sqlite3_stmt *stmt = nullptr; - int &index; + sqlite3_stmt* stmt = nullptr; + int& index; conditional_binder_base(decltype(stmt) stmt_, decltype(index) index_) : stmt(stmt_), index(index_) {} }; @@ -4882,7 +6978,7 @@ namespace sqlite_orm { using conditional_binder_base::conditional_binder_base; - int operator()(const T &t) const { + int operator()(const T& t) const { return statement_binder().bind(this->stmt, this->index++, t); } }; @@ -4891,23 +6987,34 @@ namespace sqlite_orm { struct conditional_binder : conditional_binder_base { using conditional_binder_base::conditional_binder_base; - int operator()(const T &) const { + int operator()(const T&) const { return SQLITE_OK; } }; - template - struct bindable_filter_single; + template class F, class SFINAE = void> + struct tuple_filter_single; - template - struct bindable_filter_single::value>::type> { + template class F> + struct tuple_filter_single::value>::type> { using type = std::tuple; }; + template class F> + struct tuple_filter_single::value>::type> { + using type = std::tuple<>; + }; + + template class F> + struct tuple_filter; + + template class F> + struct tuple_filter, F> { + using type = typename conc_tuple::type...>::type; + }; + template - struct bindable_filter_single::value>::type> { - using type = std::tuple<>; - }; + struct bindable_filter_single : tuple_filter_single {}; template struct bindable_filter; @@ -4922,7 +7029,7 @@ namespace sqlite_orm { #include #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if -#include // atof, atoi, atoll +#include // atof, atoi, atoll #include // std::string, std::wstring #ifndef SQLITE_ORM_OMITS_CODECVT #include // std::wstring_convert, std::codecvt_utf8_utf16 @@ -4962,7 +7069,7 @@ namespace sqlite_orm { namespace internal { - inline const std::string &to_string(journal_mode j) { + inline const std::string& to_string(journal_mode j) { static std::string res[] = { "DELETE", "TRUNCATE", @@ -4974,7 +7081,7 @@ namespace sqlite_orm { return res[static_cast(j)]; } - inline std::unique_ptr journal_mode_from_string(const std::string &str) { + inline std::unique_ptr journal_mode_from_string(const std::string& str) { std::string upper_str; std::transform(str.begin(), str.end(), std::back_inserter(upper_str), [](char c) { return static_cast(std::toupper(static_cast(c))); @@ -5009,10 +7116,13 @@ namespace sqlite_orm { template struct row_extractor { // used in sqlite3_exec (select) - V extract(const char *row_value); + V extract(const char* row_value) const; // used in sqlite_column (iteration, get_all) - V extract(sqlite3_stmt *stmt, int columnIndex); + V extract(sqlite3_stmt* stmt, int columnIndex) const; + + // used in user defined functions + V extract(sqlite3_value* value) const; }; /** @@ -5020,40 +7130,56 @@ namespace sqlite_orm { */ template struct row_extractor::value>> { - V extract(const char *row_value) { - return extract(row_value, tag()); + V extract(const char* row_value) const { + return this->extract(row_value, tag()); + } + + V extract(sqlite3_stmt* stmt, int columnIndex) const { + return this->extract(stmt, columnIndex, tag()); } - V extract(sqlite3_stmt *stmt, int columnIndex) { - return extract(stmt, columnIndex, tag()); + V extract(sqlite3_value* value) const { + return this->extract(value, tag()); } private: using tag = arithmetic_tag_t; - V extract(const char *row_value, const int_or_smaller_tag &) { + V extract(const char* row_value, const int_or_smaller_tag&) const { return static_cast(atoi(row_value)); } - V extract(sqlite3_stmt *stmt, int columnIndex, const int_or_smaller_tag &) { + V extract(sqlite3_stmt* stmt, int columnIndex, const int_or_smaller_tag&) const { return static_cast(sqlite3_column_int(stmt, columnIndex)); } - V extract(const char *row_value, const bigint_tag &) { + V extract(sqlite3_value* value, const int_or_smaller_tag&) const { + return static_cast(sqlite3_value_int(value)); + } + + V extract(const char* row_value, const bigint_tag&) const { return static_cast(atoll(row_value)); } - V extract(sqlite3_stmt *stmt, int columnIndex, const bigint_tag &) { + V extract(sqlite3_stmt* stmt, int columnIndex, const bigint_tag&) const { return static_cast(sqlite3_column_int64(stmt, columnIndex)); } - V extract(const char *row_value, const real_tag &) { + V extract(sqlite3_value* value, const bigint_tag&) const { + return static_cast(sqlite3_value_int64(value)); + } + + V extract(const char* row_value, const real_tag&) const { return static_cast(atof(row_value)); } - V extract(sqlite3_stmt *stmt, int columnIndex, const real_tag &) { + V extract(sqlite3_stmt* stmt, int columnIndex, const real_tag&) const { return static_cast(sqlite3_column_double(stmt, columnIndex)); } + + V extract(sqlite3_value* value, const real_tag&) const { + return static_cast(sqlite3_value_double(value)); + } }; /** @@ -5061,7 +7187,7 @@ namespace sqlite_orm { */ template<> struct row_extractor { - std::string extract(const char *row_value) { + std::string extract(const char* row_value) const { if(row_value) { return row_value; } else { @@ -5069,9 +7195,16 @@ namespace sqlite_orm { } } - std::string extract(sqlite3_stmt *stmt, int columnIndex) { - auto cStr = (const char *)sqlite3_column_text(stmt, columnIndex); - if(cStr) { + std::string extract(sqlite3_stmt* stmt, int columnIndex) const { + if(auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex)) { + return cStr; + } else { + return {}; + } + } + + std::string extract(sqlite3_value* value) const { + if(auto cStr = (const char*)sqlite3_value_text(value)) { return cStr; } else { return {}; @@ -5084,7 +7217,7 @@ namespace sqlite_orm { */ template<> struct row_extractor { - std::wstring extract(const char *row_value) { + std::wstring extract(const char* row_value) const { if(row_value) { std::wstring_convert> converter; return converter.from_bytes(row_value); @@ -5093,8 +7226,8 @@ namespace sqlite_orm { } } - std::wstring extract(sqlite3_stmt *stmt, int columnIndex) { - auto cStr = (const char *)sqlite3_column_text(stmt, columnIndex); + std::wstring extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); if(cStr) { std::wstring_convert> converter; return converter.from_bytes(cStr); @@ -5102,6 +7235,14 @@ namespace sqlite_orm { return {}; } } + + std::wstring extract(sqlite3_value* value) const { + if(auto cStr = (const wchar_t*)sqlite3_value_text16(value)) { + return cStr; + } else { + return {}; + } + } }; #endif // SQLITE_ORM_OMITS_CODECVT @@ -5109,7 +7250,7 @@ namespace sqlite_orm { struct row_extractor::value>> { using value_type = typename is_std_ptr::element_type; - V extract(const char *row_value) { + V extract(const char* row_value) const { if(row_value) { return is_std_ptr::make(row_extractor().extract(row_value)); } else { @@ -5117,7 +7258,7 @@ namespace sqlite_orm { } } - V extract(sqlite3_stmt *stmt, int columnIndex) { + V extract(sqlite3_stmt* stmt, int columnIndex) const { auto type = sqlite3_column_type(stmt, columnIndex); if(type != SQLITE_NULL) { return is_std_ptr::make(row_extractor().extract(stmt, columnIndex)); @@ -5125,6 +7266,15 @@ namespace sqlite_orm { return {}; } } + + V extract(sqlite3_value* value) const { + auto type = sqlite3_value_type(value); + if(type != SQLITE_NULL) { + return is_std_ptr::make(row_extractor().extract(value)); + } else { + return {}; + } + } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -5132,7 +7282,7 @@ namespace sqlite_orm { struct row_extractor, void> { using value_type = T; - std::optional extract(const char *row_value) { + std::optional extract(const char* row_value) const { if(row_value) { return std::make_optional(row_extractor().extract(row_value)); } else { @@ -5140,7 +7290,7 @@ namespace sqlite_orm { } } - std::optional extract(sqlite3_stmt *stmt, int columnIndex) { + std::optional extract(sqlite3_stmt* stmt, int columnIndex) const { auto type = sqlite3_column_type(stmt, columnIndex); if(type != SQLITE_NULL) { return std::make_optional(row_extractor().extract(stmt, columnIndex)); @@ -5148,6 +7298,15 @@ namespace sqlite_orm { return std::nullopt; } } + + std::optional extract(sqlite3_value* value) const { + auto type = sqlite3_value_type(value); + if(type != SQLITE_NULL) { + return std::make_optional(row_extractor().extract(value)); + } else { + return std::nullopt; + } + } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED /** @@ -5155,7 +7314,7 @@ namespace sqlite_orm { */ template<> struct row_extractor> { - std::vector extract(const char *row_value) { + std::vector extract(const char* row_value) const { if(row_value) { auto len = ::strlen(row_value); return this->go(row_value, len); @@ -5164,14 +7323,20 @@ namespace sqlite_orm { } } - std::vector extract(sqlite3_stmt *stmt, int columnIndex) { - auto bytes = static_cast(sqlite3_column_blob(stmt, columnIndex)); + std::vector extract(sqlite3_stmt* stmt, int columnIndex) const { + auto bytes = static_cast(sqlite3_column_blob(stmt, columnIndex)); auto len = static_cast(sqlite3_column_bytes(stmt, columnIndex)); return this->go(bytes, len); } + std::vector extract(sqlite3_value* value) const { + auto bytes = static_cast(sqlite3_value_blob(value)); + auto len = static_cast(sqlite3_value_bytes(value)); + return this->go(bytes, len); + } + protected: - std::vector go(const char *bytes, size_t len) { + std::vector go(const char* bytes, size_t len) const { if(len) { std::vector res; res.reserve(len); @@ -5186,40 +7351,40 @@ namespace sqlite_orm { template struct row_extractor> { - std::tuple extract(char **argv) { + std::tuple extract(char** argv) const { std::tuple res; this->extract::value>(res, argv); return res; } - std::tuple extract(sqlite3_stmt *stmt, int /*columnIndex*/) { + std::tuple extract(sqlite3_stmt* stmt, int /*columnIndex*/) const { std::tuple res; this->extract::value>(res, stmt); return res; } protected: - template::type * = nullptr> - void extract(std::tuple &t, sqlite3_stmt *stmt) { + template::type* = nullptr> + void extract(std::tuple& t, sqlite3_stmt* stmt) const { using tuple_type = typename std::tuple_element>::type; std::get(t) = row_extractor().extract(stmt, I - 1); this->extract(t, stmt); } - template::type * = nullptr> - void extract(std::tuple &, sqlite3_stmt *) { + template::type* = nullptr> + void extract(std::tuple&, sqlite3_stmt*) const { //.. } - template::type * = nullptr> - void extract(std::tuple &t, char **argv) { + template::type* = nullptr> + void extract(std::tuple& t, char** argv) const { using tuple_type = typename std::tuple_element>::type; std::get(t) = row_extractor().extract(argv[I - 1]); this->extract(t, argv); } - template::type * = nullptr> - void extract(std::tuple &, char **) { + template::type* = nullptr> + void extract(std::tuple&, char**) const { //.. } }; @@ -5229,7 +7394,7 @@ namespace sqlite_orm { */ template<> struct row_extractor { - journal_mode extract(const char *row_value) { + journal_mode extract(const char* row_value) const { if(row_value) { if(auto res = internal::journal_mode_from_string(row_value)) { return std::move(*res); @@ -5241,14 +7406,58 @@ namespace sqlite_orm { } } - journal_mode extract(sqlite3_stmt *stmt, int columnIndex) { - auto cStr = (const char *)sqlite3_column_text(stmt, columnIndex); + journal_mode extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); return this->extract(cStr); } }; } #pragma once +#include +#include // std::string +#include // std::system_error, std::error_code + +namespace sqlite_orm { + + namespace internal { + inline void perform_step(sqlite3* db, sqlite3_stmt* stmt) { + auto rc = sqlite3_step(stmt); + if(rc == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + static void perform_void_exec(sqlite3* db, const std::string& query) { + int rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); + if(rc != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + template + inline auto call_insert_impl_and_catch_constraint_failed(const T& insert_impl) { + try { + return insert_impl(); + } catch(const std::system_error& e) { + if(e.code() == std::error_code(SQLITE_CONSTRAINT, get_sqlite_error_category())) { + std::stringstream ss; + ss << "Attempting to execute 'insert' request resulted in an error like \"" << e.what() + << "\". Perhaps ordinary 'insert' is not acceptable for this table and you should try " + "'replace' or 'insert' with explicit column listing?"; + throw std::system_error(e.code(), ss.str()); + } + throw; + } + } + } +} +#pragma once + #include namespace sqlite_orm { @@ -5290,7 +7499,7 @@ namespace sqlite_orm { dropped_and_recreated, }; - inline std::ostream &operator<<(std::ostream &os, sync_schema_result value) { + inline std::ostream& operator<<(std::ostream& os, sync_schema_result value) { switch(value) { case sync_schema_result::new_table_created: return os << "new table created"; @@ -5326,6 +7535,9 @@ namespace sqlite_orm { struct indexed_column_t { using column_type = C; + indexed_column_t(column_type _column_or_expression) : + column_or_expression(std::move(_column_or_expression)) {} + column_type column_or_expression; std::string _collation_name; int _order = 0; // -1 = desc, 1 = asc, 0 = not specified @@ -5408,13 +7620,13 @@ namespace sqlite_orm { } template - internal::index_t::type...> make_index(const std::string &name, + internal::index_t::type...> make_index(const std::string& name, Cols... cols) { return {name, false, std::make_tuple(internal::make_indexed_column(cols)...)}; } template - internal::index_t::type...> make_unique_index(const std::string &name, + internal::index_t::type...> make_unique_index(const std::string& name, Cols... cols) { return {name, true, std::make_tuple(internal::make_indexed_column(cols)...)}; } @@ -5543,6 +7755,12 @@ namespace sqlite_orm { template struct table_t; + template + struct foreign_key_t; + + template + struct table_without_rowid_t; + namespace storage_traits { /** @@ -5624,7 +7842,21 @@ namespace sqlite_orm { */ template struct table_types> { - using type = std::tuple; + using args_tuple = std::tuple; + using columns_tuple = typename tuple_filter::type; + + using type = typename tuple_transformer::type; + }; + + /** + * type is std::tuple of field types of mapped colums. + */ + template + struct table_types> { + using args_tuple = std::tuple; + using columns_tuple = typename tuple_filter::type; + + using type = typename tuple_transformer::type; }; /** @@ -5665,14 +7897,360 @@ namespace sqlite_orm { typename std::enable_if::value>::type> : storage_mapped_columns_impl {}; + /** + * C is any column type: column_t or constraint type + * O - object type references in FOREIGN KEY + */ + template + struct column_foreign_keys_count : std::integral_constant {}; + + template + struct column_foreign_keys_count, O> { + using target_type = typename foreign_key_t::target_type; + + static constexpr const int value = std::is_same::value ? 1 : 0; + }; + + /** + * O - object type references in FOREIGN KEY + * Cs - column types which are stored in table_t::columns_type + */ + template + struct table_foreign_keys_count_impl; + + template + struct table_foreign_keys_count_impl { + static constexpr const int value = 0; + }; + + template + struct table_foreign_keys_count_impl { + static constexpr const int value = + column_foreign_keys_count::value + table_foreign_keys_count_impl::value; + }; + + /** + * T is table_t type + * O is object type which is the reference target (e.g. foreign_key(&Visit::userId).references(&User::id) has O = User) + */ + template + struct table_foreign_keys_count; + + template + struct table_foreign_keys_count, O> { + using table_type = table_t; + + static constexpr const int value = table_foreign_keys_count_impl::value; + }; + + /** + * S - storage class + * O - type mapped to S + */ + template + struct storage_foreign_keys_count_impl; + + template + struct storage_foreign_keys_count_impl, O> : std::integral_constant {}; + + template + struct storage_foreign_keys_count_impl, O> { + static constexpr const int value = table_foreign_keys_count::value + + storage_foreign_keys_count_impl, O>::value; + }; + + /** + * S - storage class + * O - type mapped to S + * This class tells how many types mapped to S have foreign keys to O + */ + template + struct storage_foreign_keys_count { + using impl_type = typename S::impl_type; + + static constexpr const int value = storage_foreign_keys_count_impl::value; + }; + + /** + * C is any column type: column_t or constraint type + * O - object type references in FOREIGN KEY + */ + template + struct column_fk_references { + using type = std::tuple<>; + }; + + template + struct column_fk_references< + foreign_key_t, + O, + typename std::enable_if::target_type>::value>::type> { + using target_type = typename foreign_key_t::source_type; + + using type = std::tuple; + }; + + template + struct column_fk_references< + foreign_key_t, + O, + typename std::enable_if::target_type>::value>::type> { + using type = std::tuple<>; + }; + + /** + * O - object type references in FOREIGN KEY + * Cs - column types which are stored in table_t::columns_type + */ + template + struct table_fk_references_impl; + + template + struct table_fk_references_impl { + using type = std::tuple<>; + }; + + template + struct table_fk_references_impl { + using head_tuple = typename column_fk_references::type; + using tail_tuple = typename table_fk_references_impl::type; + using type = typename conc_tuple::type; + }; + + /** + * T is table_t type + * O is object type which is the reference target (e.g. foreign_key(&Visit::userId).references(&User::id) has O = User) + */ + template + struct table_fk_references; + + template + struct table_fk_references, O> { + using table_type = table_t; + + using type = typename table_fk_references_impl::type; + }; + + /** + * S - storage class + * O - type mapped to S + */ + template + struct storage_fk_references_impl; + + template + struct storage_fk_references_impl, O> { + using type = std::tuple<>; + }; + + template + struct storage_fk_references_impl, O> { + using head_tuple = typename table_fk_references::type; + using tail_tuple = typename storage_fk_references_impl, O>::type; + using type = typename conc_tuple::type; + }; + + /** + * S - storage class + * O - type mapped to S + * type holds `std::tuple` with types that has references to O as foreign keys + */ + template + struct storage_fk_references { + using impl_type = typename S::impl_type; + + using type = typename storage_fk_references_impl::type; + }; + } } } +// #include "function.h" + +#include // std::string +#include +#include // std::tuple +#include // std::function + namespace sqlite_orm { - using int64 = sqlite_int64; - using uint64 = sqlite_uint64; + struct arg_values; + + namespace internal { + + struct function_base { + using func_call = std::function< + void(sqlite3_context* context, void* functionPointer, int argsCount, sqlite3_value** values)>; + using final_call = std::function; + + std::string name; + int argumentsCount = 0; + std::function create; + void (*destroy)(int*) = nullptr; + + function_base(decltype(name) name_, + decltype(argumentsCount) argumentsCount_, + decltype(create) create_, + decltype(destroy) destroy_) : + name(move(name_)), + argumentsCount(argumentsCount_), create(move(create_)), destroy(destroy_) {} + }; + + struct scalar_function_t : function_base { + func_call run; + + scalar_function_t(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(run) run_, + decltype(destroy) destroy_) : + function_base{move(name_), argumentsCount_, move(create_), destroy_}, + run(move(run_)) {} + }; + + struct aggregate_function_t : function_base { + func_call step; + final_call finalCall; + + aggregate_function_t(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(step) step_, + decltype(finalCall) finalCall_, + decltype(destroy) destroy_) : + function_base{move(name_), argumentsCount_, move(create_), destroy_}, + step(move(step_)), finalCall(move(finalCall_)) {} + }; + + // got it from here https://stackoverflow.com/questions/87372/check-if-a-class-has-a-member-function-of-a-given-signature + template + struct is_scalar_function_impl { + + template + struct SFINAE; + + template + struct SFINAE {}; + + template + static char test(SFINAE*); + + template + static int test(...); + + static constexpr bool has = sizeof(test(0)) == sizeof(char); + }; + + template + struct is_aggregate_function_impl { + + template + struct SFINAE; + + template + struct SFINAE {}; + + template + static char test(SFINAE*); + + template + static int test(...); + + template + static char test2(SFINAE*); + + template + static int test2(...); + + static constexpr bool has = sizeof(test(0)) == sizeof(char); + static constexpr bool has2 = sizeof(test2(0)) == sizeof(char); + static constexpr bool has_both = has && has2; + }; + + template + struct is_scalar_function : std::integral_constant::has> {}; + + template + struct is_aggregate_function : std::integral_constant::has_both> {}; + + template + struct scalar_run_member_pointer { + using type = decltype(&F::operator()); + }; + + template + struct aggregate_run_member_pointer { + using step_type = decltype(&F::step); + using fin_type = decltype(&F::fin); + }; + + template + struct member_function_arguments; + + template + struct member_function_arguments { + using member_function_type = R (O::*)(Args...) const; + using tuple_type = std::tuple::type...>; + using return_type = R; + }; + + template + struct member_function_arguments { + using member_function_type = R (O::*)(Args...); + using tuple_type = std::tuple::type...>; + using return_type = R; + }; + + template + struct callable_arguments_impl; + + template + struct callable_arguments_impl { + using function_member_pointer = typename scalar_run_member_pointer::type; + using args_tuple = typename member_function_arguments::tuple_type; + using return_type = typename member_function_arguments::return_type; + }; + + template + struct callable_arguments_impl { + using step_function_member_pointer = typename aggregate_run_member_pointer::step_type; + using fin_function_member_pointer = typename aggregate_run_member_pointer::fin_type; + using args_tuple = typename member_function_arguments::tuple_type; + using return_type = typename member_function_arguments::return_type; + }; + + template + struct callable_arguments : callable_arguments_impl::value> {}; + + template + struct function_call { + using function_type = F; + using args_tuple = std::tuple; + + args_tuple args; + }; + } + + /** + * Used to call user defined function: `func(...);` + */ + template + internal::function_call func(Args... args) { + using args_tuple = std::tuple; + using function_args_tuple = typename internal::callable_arguments::args_tuple; + constexpr auto argsCount = std::tuple_size::value; + constexpr auto functionArgsCount = std::tuple_size::value; + static_assert( + (argsCount == functionArgsCount && !std::is_same>::value) || + std::is_same>::value, + "Arguments amount does not match"); + return {std::make_tuple(std::forward(args)...)}; + } + +} + +namespace sqlite_orm { namespace internal { @@ -5688,6 +8266,14 @@ namespace sqlite_orm { template struct column_result_t; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct column_result_t, void> { + using type = std::optional::type>; + }; + +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template struct column_result_t + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + /** * Common case for all getter types. Getter types are defined in column.h file */ @@ -5713,12 +8309,17 @@ namespace sqlite_orm { }; template - struct column_result_t, void> { + struct column_result_t, void> { using type = R; }; + template + struct column_result_t, void> { + using type = typename callable_arguments::return_type; + }; + template - struct column_result_t, S, X>, void> { + struct column_result_t, S, X>, void> { using type = std::unique_ptr::type>; }; @@ -5854,6 +8455,11 @@ namespace sqlite_orm { using type = left_result; }; + template + struct column_result_t::value>::type> { + using type = typename T::result_type; + }; + /** * Result for the most simple queries like `SELECT 1` */ @@ -5866,7 +8472,7 @@ namespace sqlite_orm { * Result for the most simple queries like `SELECT 'ototo'` */ template - struct column_result_t { + struct column_result_t { using type = std::string; }; @@ -5933,7 +8539,7 @@ namespace sqlite_orm { // #include "constraints.h" -// #include "tuple_helper.h" +// #include "tuple_helper/tuple_helper.h" // #include "table_info.h" @@ -5945,6 +8551,7 @@ namespace sqlite_orm { namespace internal { + template struct table_base { /** @@ -5952,37 +8559,40 @@ namespace sqlite_orm { */ std::string name; - bool _without_rowid = false; + static constexpr const bool is_without_rowid = _without_rowid; }; + template + struct table_without_rowid_t; + /** - * Table interface class. Implementation is hidden in `table_impl` class. + * Template for table interface class. */ - template - struct table_t : table_base { + template + struct table_template : table_base<_without_rowid> { using object_type = T; using columns_type = std::tuple; + using super = table_base<_without_rowid>; static constexpr const int columns_count = static_cast(std::tuple_size::value); + using super::name; columns_type columns; - table_t(decltype(name) name_, columns_type columns_) : - table_base{std::move(name_)}, columns(std::move(columns_)) {} + table_template(std::string name_, columns_type columns_) : + super{std::move(name_)}, columns{std::move(columns_)} {} - table_t without_rowid() const { - auto res = *this; - res._without_rowid = true; - return res; + table_without_rowid_t without_rowid() const { + return {name, columns}; } /** * Function used to get field value from object by mapped member pointer/setter/getter */ template - const F *get_object_field_pointer(const object_type &obj, C c) const { - const F *res = nullptr; - this->for_each_column_with_field_type([&res, &c, &obj](auto &col) { + const F* get_object_field_pointer(const object_type& obj, C c) const { + const F* res = nullptr; + this->for_each_column_with_field_type([&res, &c, &obj](auto& col) { using column_type = typename std::remove_reference::type; using member_pointer_t = typename column_type::member_pointer_t; using getter_type = typename column_type::getter_type; @@ -5990,21 +8600,21 @@ namespace sqlite_orm { // Make static_if have at least one input as a workaround for GCC bug: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095 if(!res) { - static_if{}>([&res, &obj, &col](const C &c_) { + static_if{}>([&res, &obj, &col](const C& c_) { if(compare_any(col.member_pointer, c_)) { res = &(obj.*col.member_pointer); } })(c); } if(!res) { - static_if{}>([&res, &obj, &col](const C &c_) { + static_if{}>([&res, &obj, &col](const C& c_) { if(compare_any(col.getter, c_)) { res = &((obj).*(col.getter))(); } })(c); } if(!res) { - static_if{}>([&res, &obj, &col](const C &c_) { + static_if{}>([&res, &obj, &col](const C& c_) { if(compare_any(col.setter, c_)) { res = &((obj).*(col.getter))(); } @@ -6019,7 +8629,7 @@ namespace sqlite_orm { */ std::vector column_names() const { std::vector res; - this->for_each_column([&res](auto &c) { + this->for_each_column([&res](auto& c) { res.push_back(c.name); }); return res; @@ -6029,8 +8639,8 @@ namespace sqlite_orm { * Calls **l** with every primary key dedicated constraint */ template - void for_each_primary_key(const L &l) const { - iterate_tuple(this->columns, [&l](auto &column) { + void for_each_primary_key(const L& l) const { + iterate_tuple(this->columns, [&l](auto& column) { using column_type = typename std::decay::type; static_if{}>(l)(column); }); @@ -6038,7 +8648,7 @@ namespace sqlite_orm { std::vector composite_key_columns_names() const { std::vector res; - this->for_each_primary_key([this, &res](auto &c) { + this->for_each_primary_key([this, &res](auto& c) { res = this->composite_key_columns_names(c); }); return res; @@ -6046,7 +8656,7 @@ namespace sqlite_orm { std::vector primary_key_column_names() const { std::vector res; - this->for_each_column_with>([&res](auto &c) { + this->for_each_column_with>([&res](auto& c) { res.push_back(c.name); }); if(!res.size()) { @@ -6056,12 +8666,16 @@ namespace sqlite_orm { } template - std::vector composite_key_columns_names(const constraints::primary_key_t &pk) const { + std::vector composite_key_columns_names(const primary_key_t& pk) const { std::vector res; using pk_columns_tuple = decltype(pk.columns); res.reserve(std::tuple_size::value); - iterate_tuple(pk.columns, [this, &res](auto &v) { - res.push_back(this->find_column_name(v)); + iterate_tuple(pk.columns, [this, &res](auto& v) { + if(auto columnName = this->find_column_name(v)) { + res.push_back(*columnName); + } else { + res.push_back({}); + } }); return res; } @@ -6074,11 +8688,11 @@ namespace sqlite_orm { class O, typename = typename std::enable_if::value && !std::is_member_function_pointer::value>::type> - std::string find_column_name(F O::*m) const { - std::string res; - this->template for_each_column_with_field_type([&res, m](auto &c) { + const std::string* find_column_name(F O::*m) const { + const std::string* res = nullptr; + this->template for_each_column_with_field_type([&res, m](auto& c) { if(c.member_pointer == m) { - res = c.name; + res = &c.name; } }); return res; @@ -6089,13 +8703,13 @@ namespace sqlite_orm { * @return column name or empty string if nothing found. */ template - std::string find_column_name(G getter, - typename std::enable_if::value>::type * = nullptr) const { - std::string res; + const std::string* find_column_name(G getter, + typename std::enable_if::value>::type* = nullptr) const { + const std::string* res = nullptr; using field_type = typename getter_traits::field_type; - this->template for_each_column_with_field_type([&res, getter](auto &c) { + this->template for_each_column_with_field_type([&res, getter](auto& c) { if(compare_any(c.getter, getter)) { - res = c.name; + res = &c.name; } }); return res; @@ -6106,13 +8720,13 @@ namespace sqlite_orm { * @return column name or empty string if nothing found. */ template - std::string find_column_name(S setter, - typename std::enable_if::value>::type * = nullptr) const { - std::string res; + const std::string* find_column_name(S setter, + typename std::enable_if::value>::type* = nullptr) const { + const std::string* res = nullptr; using field_type = typename setter_traits::field_type; - this->template for_each_column_with_field_type([&res, setter](auto &c) { + this->template for_each_column_with_field_type([&res, setter](auto& c) { if(compare_any(c.setter, setter)) { - res = c.name; + res = &c.name; } }); return res; @@ -6126,16 +8740,16 @@ namespace sqlite_orm { * @param l Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} */ template - void for_each_column(const L &l) const { - iterate_tuple(this->columns, [&l](auto &column) { + void for_each_column(const L& l) const { + iterate_tuple(this->columns, [&l](auto& column) { using column_type = typename std::decay::type; static_if{}>(l)(column); }); } template - void for_each_column_with_field_type(const L &l) const { - iterate_tuple(this->columns, [&l](auto &column) { + void for_each_column_with_field_type(const L& l) const { + iterate_tuple(this->columns, [&l](auto& column) { using column_type = typename std::decay::type; using field_type = typename column_field_type::type; static_if{}>(l)(column); @@ -6149,9 +8763,9 @@ namespace sqlite_orm { * @param l Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} */ template - void for_each_column_with(const L &l) const { + void for_each_column_with(const L& l) const { using tuple_helper::tuple_contains_type; - iterate_tuple(this->columns, [&l](auto &column) { + iterate_tuple(this->columns, [&l](auto& column) { using column_type = typename std::decay::type; using constraints_type = typename column_constraints_type::type; static_if{}>(l)(column); @@ -6161,7 +8775,7 @@ namespace sqlite_orm { std::vector get_table_info() const { std::vector res; res.reserve(size_t(this->columns_count)); - this->for_each_column([&res](auto &col) { + this->for_each_column([&res](auto& col) { std::string dft; using field_type = typename std::decay::type::field_type; if(auto d = col.default_value()) { @@ -6173,14 +8787,14 @@ namespace sqlite_orm { type_printer().print(), col.not_null(), dft, - col.template has>(), + col.template has>(), }; res.emplace_back(i); }); auto compositeKeyColumnNames = this->composite_key_columns_names(); for(size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { - auto &columnName = compositeKeyColumnNames[i]; - auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_info &ti) { + auto& columnName = compositeKeyColumnNames[i]; + auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_info& ti) { return ti.name == columnName; }); if(it != res.end()) { @@ -6190,6 +8804,22 @@ namespace sqlite_orm { return res; } }; + + /** + * Table interface class. + */ + template + struct table_t : table_template { + using table_template::table_template; + }; + + /** + * Table interface class with 'without_rowid' tag. + */ + template + struct table_without_rowid_t : table_template { + using table_template::table_template; + }; } /** @@ -6197,12 +8827,12 @@ namespace sqlite_orm { * cause table class is templated and its constructing too (just like std::make_unique or std::make_pair). */ template>::type::object_type> - internal::table_t make_table(const std::string &name, Cs... args) { + internal::table_t make_table(const std::string& name, Cs... args) { return {name, std::make_tuple(std::forward(args)...)}; } template - internal::table_t make_table(const std::string &name, Cs... args) { + internal::table_t make_table(const std::string& name, Cs... args) { return {name, std::make_tuple(std::forward(args)...)}; } } @@ -6213,7 +8843,7 @@ namespace sqlite_orm { #include // std::nullptr_t #include // std::system_error, std::error_code #include // std::stringstream -#include // std::atoi +#include // std::atoi #include // std::forward, std::enable_if, std::is_same, std::remove_reference, std::false_type, std::true_type #include // std::pair, std::make_pair #include // std::vector @@ -6226,6 +8856,8 @@ namespace sqlite_orm { // #include "row_extractor.h" +// #include "util.h" + // #include "constraints.h" // #include "select_constraints.h" @@ -6252,7 +8884,7 @@ namespace sqlite_orm { struct field_value_holder::returns_lvalue>::type> { using type = typename getter_traits::field_type; - const type &value; + const type& value; }; template @@ -6270,7 +8902,7 @@ namespace sqlite_orm { struct storage_impl_base { - bool table_exists(const std::string &tableName, sqlite3 *db) const { + bool table_exists(const std::string& tableName, sqlite3* db) const { auto result = false; std::stringstream ss; ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = '" @@ -6280,8 +8912,8 @@ namespace sqlite_orm { auto rc = sqlite3_exec( db, query.c_str(), - [](void *data, int argc, char **argv, char * * /*azColName*/) -> int { - auto &res = *(bool *)data; + [](void* data, int argc, char** argv, char** /*azColName*/) -> int { + auto& res = *(bool*)data; if(argc) { res = !!std::atoi(argv[0]); } @@ -6296,28 +8928,15 @@ namespace sqlite_orm { return result; } - void rename_table(sqlite3 *db, const std::string &oldName, const std::string &newName) const { + void rename_table(sqlite3* db, const std::string& oldName, const std::string& newName) const { std::stringstream ss; ss << "ALTER TABLE " << oldName << " RENAME TO " << newName; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_void_exec(db, ss.str()); } - static bool get_remove_add_columns(std::vector &columnsToAdd, - std::vector &storageTableInfo, - std::vector &dbTableInfo) { + static bool calculate_remove_add_columns(std::vector& columnsToAdd, + std::vector& storageTableInfo, + std::vector& dbTableInfo) { bool notEqual = false; // iterate through storage columns @@ -6325,15 +8944,15 @@ namespace sqlite_orm { ++storageColumnInfoIndex) { // get storage's column info - auto &storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; - auto &columnName = storageColumnInfo.name; + auto& storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; + auto& columnName = storageColumnInfo.name; // search for a column in db eith the same name - auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto &ti) { + auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { return ti.name == columnName; }); if(dbColumnInfoIt != dbTableInfo.end()) { - auto &dbColumnInfo = *dbColumnInfoIt; + auto& dbColumnInfo = *dbColumnInfoIt; auto columnsAreEqual = dbColumnInfo.name == storageColumnInfo.name && dbColumnInfo.notnull == storageColumnInfo.notnull && @@ -6354,14 +8973,14 @@ namespace sqlite_orm { return notEqual; } - std::vector get_table_info(const std::string &tableName, sqlite3 *db) const { + std::vector get_table_info(const std::string& tableName, sqlite3* db) const { std::vector result; auto query = "PRAGMA table_info('" + tableName + "')"; auto rc = sqlite3_exec( db, query.c_str(), - [](void *data, int argc, char **argv, char **) -> int { - auto &res = *(std::vector *)data; + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(std::vector*)data; if(argc) { auto index = 0; auto cid = std::atoi(argv[index++]); @@ -6371,7 +8990,7 @@ namespace sqlite_orm { std::string dflt_value = argv[index] ? argv[index] : ""; index++; auto pk = std::atoi(argv[index++]); - res.push_back(table_info{cid, name, type, notnull, dflt_value, pk}); + res.push_back(table_info(cid, name, type, notnull, dflt_value, pk)); } return 0; }, @@ -6401,7 +9020,7 @@ namespace sqlite_orm { table_type table; template - void for_each(const L &l) { + void for_each(const L& l) { this->super::for_each(l); l(*this); } @@ -6413,7 +9032,7 @@ namespace sqlite_orm { */ int foreign_keys_count() { auto res = 0; - iterate_tuple(this->table.columns, [&res](auto &c) { + iterate_tuple(this->table.columns, [&res](auto& c) { if(internal::is_foreign_key::type>::value) { ++res; } @@ -6429,7 +9048,7 @@ namespace sqlite_orm { * `column_name` has SFINAE check for type equality but `column_name_simple` has not. */ template - std::string column_name_simple(F O::*m) const { + const std::string* column_name_simple(F O::*m) const { return this->table.find_column_name(m); } @@ -6438,8 +9057,8 @@ namespace sqlite_orm { * skip inequal type O. */ template - std::string column_name(F O::*m, - typename std::enable_if::value>::type * = nullptr) const { + const std::string* column_name(F O::*m, + typename std::enable_if::value>::type* = nullptr) const { return this->table.find_column_name(m); } @@ -6447,50 +9066,51 @@ namespace sqlite_orm { * Opposite version of function defined above. Just calls same function in superclass. */ template - std::string column_name(F O::*m, - typename std::enable_if::value>::type * = nullptr) const { + const std::string* + column_name(F O::*m, typename std::enable_if::value>::type* = nullptr) const { return this->super::column_name(m); } template - std::string column_name(const column_pointer &c, - typename std::enable_if::value>::type * = nullptr) const { + const std::string* column_name(const column_pointer& c, + typename std::enable_if::value>::type* = nullptr) const { return this->column_name_simple(c.field); } template - std::string column_name(const column_pointer &c, - typename std::enable_if::value>::type * = nullptr) const { + const std::string* + column_name(const column_pointer& c, + typename std::enable_if::value>::type* = nullptr) const { return this->super::column_name(c); } template - const auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { + const auto& get_impl(typename std::enable_if::value>::type* = nullptr) const { return *this; } template - const auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { + const auto& get_impl(typename std::enable_if::value>::type* = nullptr) const { return this->super::template get_impl(); } template - auto &get_impl(typename std::enable_if::value>::type * = nullptr) { + auto& get_impl(typename std::enable_if::value>::type* = nullptr) { return *this; } template - auto &get_impl(typename std::enable_if::value>::type * = nullptr) { + auto& get_impl(typename std::enable_if::value>::type* = nullptr) { return this->super::template get_impl(); } template - const auto *find_table(typename std::enable_if::value>::type * = nullptr) const { + const auto* find_table(typename std::enable_if::value>::type* = nullptr) const { return &this->table; } template - const auto *find_table(typename std::enable_if::value>::type * = nullptr) const { + const auto* find_table(typename std::enable_if::value>::type* = nullptr) const { return this->super::template find_table(); } @@ -6503,34 +9123,20 @@ namespace sqlite_orm { } } - void add_column(const table_info &ti, sqlite3 *db) const { + void add_column(const table_info& ti, sqlite3* db) const { std::stringstream ss; - ss << "ALTER TABLE " << this->table.name << " ADD COLUMN " << ti.name << " "; - ss << ti.type << " "; + ss << "ALTER TABLE " << this->table.name << " ADD COLUMN " << ti.name; + ss << " " << ti.type; if(ti.pk) { - ss << "PRIMARY KEY "; + ss << " PRIMARY KEY"; } if(ti.notnull) { - ss << "NOT NULL "; + ss << " NOT NULL"; } if(ti.dflt_value.length()) { - ss << "DEFAULT " << ti.dflt_value << " "; - } - auto query = ss.str(); - sqlite3_stmt *stmt; - auto prepareResult = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); - if(prepareResult == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - //.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + ss << " DEFAULT " << ti.dflt_value; } + perform_void_exec(db, ss.str()); } /** @@ -6538,13 +9144,11 @@ namespace sqlite_orm { * Performs CREATE TABLE %name% AS SELECT %this->table.columns_names()% FROM &this->table.name%; */ void - copy_table(sqlite3 *db, const std::string &name, const std::vector &columnsToIgnore) const { - std::ignore = columnsToIgnore; - + copy_table(sqlite3* db, const std::string& name, const std::vector& columnsToIgnore) const { std::stringstream ss; std::vector columnNames; - this->table.for_each_column([&columnNames, &columnsToIgnore](auto &c) { - auto &columnName = c.name; + this->table.for_each_column([&columnNames, &columnsToIgnore](auto& c) { + auto& columnName = c.name; auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), columnsToIgnore.end(), [&columnName](auto tableInfoPointer) { @@ -6568,28 +9172,14 @@ namespace sqlite_orm { for(size_t i = 0; i < columnNamesCount; ++i) { ss << columnNames[i]; if(i < columnNamesCount - 1) { - ss << ","; - } - ss << " "; - } - ss << "FROM '" << this->table.name << "' "; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - //.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + ss << ", "; } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); } + ss << " FROM '" << this->table.name << "' "; + perform_void_exec(db, ss.str()); } - sync_schema_result schema_status(sqlite3 *db, bool preserve) const { + sync_schema_result schema_status(sqlite3* db, bool preserve) const { auto res = sync_schema_result::already_in_sync; @@ -6604,9 +9194,9 @@ namespace sqlite_orm { auto dbTableInfo = this->get_table_info(this->table.name, db); // this vector will contain pointers to columns that gotta be added.. - std::vector columnsToAdd; + std::vector columnsToAdd; - if(this->get_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { + if(this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { gottaCreateTable = true; } @@ -6665,14 +9255,14 @@ namespace sqlite_orm { } template - void for_each(const L &) {} + void for_each(const L&) {} int foreign_keys_count() { return 0; } template - const void *find_table() const { + const void* find_table() const { return nullptr; } }; @@ -6725,7 +9315,7 @@ namespace sqlite_orm { namespace internal { struct object_from_column_builder_base { - sqlite3_stmt *stmt = nullptr; + sqlite3_stmt* stmt = nullptr; mutable int index = 0; }; @@ -6736,13 +9326,13 @@ namespace sqlite_orm { struct object_from_column_builder : object_from_column_builder_base { using object_type = O; - object_type &object; + object_type& object; - object_from_column_builder(object_type &object_, sqlite3_stmt *stmt_) : + object_from_column_builder(object_type& object_, sqlite3_stmt* stmt_) : object_from_column_builder_base{stmt_}, object(object_) {} template - void operator()(const C &c) const { + void operator()(const C& c) const { using field_type = typename C::field_type; auto value = row_extractor().extract(this->stmt, this->index++); if(c.member_pointer) { @@ -6761,27 +9351,27 @@ namespace sqlite_orm { namespace internal { /** - * This is a private row extractor class. It is used for extracting rows as objects instead of tuple. - * Main difference from regular `row_extractor` is that this class takes table info which is required - * for constructing objects by member pointers. To construct please use `row_extractor_builder` class - * Type arguments: - * V is value type just like regular `row_extractor` has - * T is table info class `table_t` - */ + * This is a private row extractor class. It is used for extracting rows as objects instead of tuple. + * Main difference from regular `row_extractor` is that this class takes table info which is required + * for constructing objects by member pointers. To construct please use `row_extractor_builder` class + * Type arguments: + * V is value type just like regular `row_extractor` has + * T is table info class `table_t` + */ template struct mapped_row_extractor { using table_info_t = T; - mapped_row_extractor(const table_info_t &tableInfo_) : tableInfo(tableInfo_) {} + mapped_row_extractor(const table_info_t& tableInfo_) : tableInfo(tableInfo_) {} - V extract(sqlite3_stmt *stmt, int /*columnIndex*/) { + V extract(sqlite3_stmt* stmt, int /*columnIndex*/) { V res; object_from_column_builder builder{res, stmt}; this->tableInfo.for_each_column(builder); return res; } - const table_info_t &tableInfo; + const table_info_t& tableInfo; }; } @@ -6793,18 +9383,18 @@ namespace sqlite_orm { namespace internal { /** - * This builder is used to construct different row extractors depending on type. - * It has two specializations: for mapped to storage types (e.g. User, Visit etc) and - * for non-mapped (e.g. std::string, QString, int etc). For non mapped its operator() returns - * generic `row_extractor`, for mapped it returns `mapped_row_extractor` instance. - */ + * This builder is used to construct different row extractors depending on type. + * It has two specializations: for mapped to storage types (e.g. User, Visit etc) and + * for non-mapped (e.g. std::string, QString, int etc). For non mapped its operator() returns + * generic `row_extractor`, for mapped it returns `mapped_row_extractor` instance. + */ template struct row_extractor_builder; template struct row_extractor_builder { - row_extractor operator()(const I * /*tableInfo*/) const { + row_extractor operator()(const I* /*tableInfo*/) const { return {}; } }; @@ -6812,13 +9402,13 @@ namespace sqlite_orm { template struct row_extractor_builder { - mapped_row_extractor operator()(const I *tableInfo) const { + mapped_row_extractor operator()(const I* tableInfo) const { return {*tableInfo}; } }; template - auto make_row_extractor(const I *tableInfo) { + auto make_row_extractor(const I* tableInfo) { using builder_t = row_extractor_builder; return builder_t{}(tableInfo); } @@ -6831,7 +9421,7 @@ namespace sqlite_orm { // #include "type_printer.h" -// #include "tuple_helper.h" +// #include "tuple_helper/tuple_helper.h" // #include "constraints.h" @@ -6915,8 +9505,10 @@ namespace sqlite_orm { * call. When one finishes iterating it the pointer * inside the shared_ptr is nulled out in all copies. */ - std::shared_ptr stmt; - view_type &view; + std::shared_ptr stmt; + + // only null for the default constructed iterator + view_type* view; /** * shared_ptr is used over unique_ptr here @@ -6924,68 +9516,53 @@ namespace sqlite_orm { */ std::shared_ptr current; - void extract_value(std::unique_ptr &temp) { - temp = std::make_unique(); - auto &storage = this->view.storage; - auto &impl = storage.template get_impl(); - object_from_column_builder builder{*temp, *this->stmt}; + void extract_value() { + auto& storage = this->view->storage; + auto& impl = storage.template get_impl(); + this->current = std::make_shared(); + object_from_column_builder builder{*this->current, this->stmt->get()}; impl.table.for_each_column(builder); } public: using difference_type = std::ptrdiff_t; - using pointer = value_type *; - using reference = value_type &; + using pointer = value_type*; + using reference = value_type&; using iterator_category = std::input_iterator_tag; - iterator_t(sqlite3_stmt *stmt_, view_type &view_) : - stmt(std::make_shared(stmt_)), view(view_) { - this->operator++(); - } - - iterator_t(const iterator_t &) = default; - - iterator_t(iterator_t &&) = default; - - iterator_t &operator=(iterator_t &&) = default; - - iterator_t &operator=(const iterator_t &) = default; + iterator_t() : view(nullptr){}; - ~iterator_t() { - if(this->stmt) { - statement_finalizer f{*this->stmt}; - } + iterator_t(sqlite3_stmt* stmt_, view_type& view_) : + stmt(std::make_shared(stmt_)), view(&view_) { + next(); } - value_type &operator*() { - if(!this->stmt) { + const value_type& operator*() const { + if(!this->stmt || !this->current) { throw std::system_error(std::make_error_code(orm_error_code::trying_to_dereference_null_iterator)); } - if(!this->current) { - std::unique_ptr value; - this->extract_value(value); - this->current = move(value); - } return *this->current; } - value_type *operator->() { + const value_type* operator->() const { return &(this->operator*()); } - void operator++() { - if(this->stmt && *this->stmt) { - auto ret = sqlite3_step(*this->stmt); + private: + void next() { + this->current.reset(); + if(this->stmt) { + auto statementPointer = this->stmt->get(); + auto ret = sqlite3_step(statementPointer); switch(ret) { case SQLITE_ROW: - this->current = nullptr; + this->extract_value(); + break; + case SQLITE_DONE: + this->stmt.reset(); break; - case SQLITE_DONE: { - statement_finalizer f{*this->stmt}; - *this->stmt = nullptr; - } break; default: { - auto db = this->view.connection.get(); + auto db = this->view->connection.get(); throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } @@ -6993,23 +9570,21 @@ namespace sqlite_orm { } } + public: + iterator_t& operator++() { + next(); + return *this; + } + void operator++(int) { this->operator++(); } - bool operator==(const iterator_t &other) const { - if(this->stmt && other.stmt) { - return *this->stmt == *other.stmt; - } else { - if(!this->stmt && !other.stmt) { - return true; - } else { - return false; - } - } + bool operator==(const iterator_t& other) const { + return this->current == other.current; } - bool operator!=(const iterator_t &other) const { + bool operator!=(const iterator_t& other) const { return !(*this == other); } }; @@ -7027,7 +9602,7 @@ namespace sqlite_orm { // #include "operators.h" -// #include "tuple_helper.h" +// #include "tuple_helper/tuple_helper.h" // #include "core_functions.h" @@ -7077,7 +9652,7 @@ namespace sqlite_orm { } } - sqlite3 *get() const { + sqlite3* get() const { return this->db; } @@ -7088,20 +9663,20 @@ namespace sqlite_orm { const std::string filename; protected: - sqlite3 *db = nullptr; + sqlite3* db = nullptr; int _retain_count = 0; }; struct connection_ref { - connection_ref(connection_holder &holder_) : holder(holder_) { + connection_ref(connection_holder& holder_) : holder(holder_) { this->holder.retain(); } - connection_ref(const connection_ref &other) : holder(other.holder) { + connection_ref(const connection_ref& other) : holder(other.holder) { this->holder.retain(); } - connection_ref(connection_ref &&other) : holder(other.holder) { + connection_ref(connection_ref&& other) : holder(other.holder) { this->holder.retain(); } @@ -7109,24 +9684,67 @@ namespace sqlite_orm { this->holder.release(); } - sqlite3 *get() const { + sqlite3* get() const { return this->holder.get(); } protected: - connection_holder &holder; + connection_holder& holder; }; } } // #include "select_constraints.h" +// #include "values.h" + +#include // std::vector +#include +#include // std::tuple +#include // std::false_type, std::true_type + +namespace sqlite_orm { + + namespace internal { + + template + struct values_t { + using args_tuple = std::tuple; + + args_tuple tuple; + }; + + template + struct is_values : std::false_type {}; + + template + struct is_values> : std::true_type {}; + + template + struct dynamic_values_t { + std::vector vector; + }; + + } + + template + internal::values_t values(Args... args) { + return {{std::forward(args)...}}; + } + + template + internal::dynamic_values_t values(std::vector vector) { + return {{move(vector)}}; + } + +} + namespace sqlite_orm { namespace internal { struct prepared_statement_base { - sqlite3_stmt *stmt = nullptr; + sqlite3_stmt* stmt = nullptr; connection_ref con; ~prepared_statement_base() { @@ -7184,7 +9802,7 @@ namespace sqlite_orm { expression_type t; - prepared_statement_t(T t_, sqlite3_stmt *stmt_, connection_ref con_) : + prepared_statement_t(T t_, sqlite3_stmt* stmt_, connection_ref con_) : prepared_statement_base{stmt_, std::move(con_)}, t(std::move(t_)) {} }; @@ -7297,6 +9915,12 @@ namespace sqlite_orm { type obj; }; + template + struct is_insert : std::false_type {}; + + template + struct is_insert> : std::true_type {}; + template struct insert_explicit { using type = T; @@ -7313,39 +9937,354 @@ namespace sqlite_orm { type obj; }; - template + template + struct is_replace : std::false_type {}; + + template + struct is_replace> : std::true_type {}; + + template struct insert_range_t { using iterator_type = It; - using object_type = typename std::iterator_traits::value_type; + using container_object_type = typename std::iterator_traits::value_type; + using transformer_type = L; + using object_type = O; std::pair range; + transformer_type transformer; }; - template + template + struct is_insert_range : std::false_type {}; + + template + struct is_insert_range> : std::true_type {}; + + template struct replace_range_t { using iterator_type = It; - using object_type = typename std::iterator_traits::value_type; + using container_object_type = typename std::iterator_traits::value_type; + using transformer_type = L; + using object_type = O; std::pair range; + transformer_type transformer; + }; + + template + struct is_replace_range : std::false_type {}; + + template + struct is_replace_range> : std::true_type {}; + + template + struct insert_raw_t { + using args_tuple = std::tuple; + + args_tuple args; + }; + + template + struct is_insert_raw : std::false_type {}; + + template + struct is_insert_raw> : std::true_type {}; + + template + struct replace_raw_t { + using args_tuple = std::tuple; + + args_tuple args; + }; + + template + struct is_replace_raw : std::false_type {}; + + template + struct is_replace_raw> : std::true_type {}; + + template + struct into_t { + using type = T; + }; + + template + struct is_into : std::false_type {}; + + template + struct is_into> : std::true_type {}; + + struct default_transformer { + + template + const T& operator()(const T& object) const { + return object; + } + }; + + struct default_values_t {}; + + template + using is_default_values = std::is_same; + + enum class insert_constraint { + abort, + fail, + ignore, + replace, + rollback, }; + + template + using is_insert_constraint = std::is_same; + } + + inline internal::insert_constraint or_rollback() { + return internal::insert_constraint::rollback; + } + + inline internal::insert_constraint or_replace() { + return internal::insert_constraint::replace; + } + + inline internal::insert_constraint or_ignore() { + return internal::insert_constraint::ignore; + } + + inline internal::insert_constraint or_fail() { + return internal::insert_constraint::fail; + } + + inline internal::insert_constraint or_abort() { + return internal::insert_constraint::abort; + } + + /** + * Use this function to add `DEFAULT VALUES` modifier to raw `INSERT`. + * + * @example + * ``` + * storage.insert(into(), default_values()); + * ``` + */ + inline internal::default_values_t default_values() { + return {}; + } + + template + internal::into_t into() { + return {}; + } + + /** + * Raw insert statement creation routine. Use this if `insert` with object does not fit you. This insert is designed to be able + * to call any type of `INSERT` query with no limitations. + * @example + * ```sql + * INSERT INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(insert(into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * storage.execute(statement)); + * ``` + * One more example: + * ```sql + * INSERT INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(insert(into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * storage.execute(statement)); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * INSERT INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(insert(into(), default_values())); + * storage.execute(statement)); + * ``` + * Also one can use `INSERT OR ABORT`/`INSERT OR FAIL`/`INSERT OR IGNORE`/`INSERT OR REPLACE`/`INSERT ROLLBACK`: + * ```c++ + * auto statement = storage.prepare(insert(or_ignore(), into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * auto statement2 = storage.prepare(insert(or_rollback(), into(), default_values())); + * auto statement3 = storage.prepare(insert(or_abort(), into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * ``` + */ + template + internal::insert_raw_t insert(Args... args) { + using args_tuple = std::tuple; + using internal::count_tuple; + using internal::is_columns; + using internal::is_insert_constraint; + using internal::is_into; + using internal::is_select; + using internal::is_upsert_clause; + using internal::is_values; + + constexpr int orArgsCount = count_tuple::value; + static_assert(orArgsCount < 2, "Raw insert must have only one OR... argument"); + + constexpr int intoArgsCount = count_tuple::value; + static_assert(intoArgsCount != 0, "Raw insert must have into argument"); + static_assert(intoArgsCount < 2, "Raw insert must have only one into argument"); + + constexpr int columnsArgsCount = count_tuple::value; + static_assert(columnsArgsCount < 2, "Raw insert must have only one columns(...) argument"); + + constexpr int valuesArgsCount = count_tuple::value; + static_assert(valuesArgsCount < 2, "Raw insert must have only one values(...) argument"); + + constexpr int defaultValuesCount = count_tuple::value; + static_assert(defaultValuesCount < 2, "Raw insert must have only one default_values() argument"); + + constexpr int selectsArgsCount = count_tuple::value; + static_assert(selectsArgsCount < 2, "Raw insert must have only one select(...) argument"); + + constexpr int upsertClausesCount = count_tuple::value; + static_assert(upsertClausesCount <= 2, "Raw insert can contain 2 instances of upsert clause maximum"); + + constexpr int argsCount = int(std::tuple_size::value); + static_assert(argsCount == intoArgsCount + columnsArgsCount + valuesArgsCount + defaultValuesCount + + selectsArgsCount + orArgsCount + upsertClausesCount, + "Raw insert has invalid arguments"); + + return {{std::forward(args)...}}; + } + + /** + * Raw replace statement creation routine. Use this if `replace` with object does not fit you. This replace is designed to be able + * to call any type of `REPLACE` query with no limitations. Actually this is the same query as raw insert except `OR...` option existance. + * @example + * ```sql + * REPLACE INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(replace(into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * storage.execute(statement)); + * ``` + * One more example: + * ```sql + * REPLACE INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(replace(into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * storage.execute(statement)); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * REPLACE INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * auto statement = storage.prepare(replace(into(), default_values())); + * storage.execute(statement)); + * ``` + */ + template + internal::replace_raw_t replace(Args... args) { + using args_tuple = std::tuple; + using internal::count_tuple; + using internal::is_columns; + using internal::is_into; + using internal::is_values; + + constexpr int intoArgsCount = count_tuple::value; + static_assert(intoArgsCount != 0, "Raw replace must have into argument"); + static_assert(intoArgsCount < 2, "Raw replace must have only one into argument"); + + constexpr int columnsArgsCount = count_tuple::value; + static_assert(columnsArgsCount < 2, "Raw replace must have only one columns(...) argument"); + + constexpr int valuesArgsCount = count_tuple::value; + static_assert(valuesArgsCount < 2, "Raw replace must have only one values(...) argument"); + + constexpr int defaultValuesCount = count_tuple::value; + static_assert(defaultValuesCount < 2, "Raw replace must have only one default_values() argument"); + + constexpr int selectsArgsCount = count_tuple::value; + static_assert(selectsArgsCount < 2, "Raw replace must have only one select(...) argument"); + + constexpr int argsCount = int(std::tuple_size::value); + static_assert(argsCount == + intoArgsCount + columnsArgsCount + valuesArgsCount + defaultValuesCount + selectsArgsCount, + "Raw replace has invalid arguments"); + + return {{std::forward(args)...}}; } /** * Create a replace range statement + * + * @example + * ``` + * std::vector users; + * users.push_back(User{1, "Leony"}); + * auto statement = storage.prepare(replace_range(users.begin(), users.end())); + * storage.execute(statement); + * ``` */ template - internal::replace_range_t replace_range(It from, It to) { + internal::replace_range_t::value_type> + replace_range(It from, It to) { return {{std::move(from), std::move(to)}}; } + /** + * Create an replace range statement with explicit transformer. Transformer is used to apply containers with no strict objects with other kind of objects like pointers, + * optionals or whatever. + * @example + * ``` + * std::vector> userPointers; + * userPointers.push_back(std::make_unique(1, "Eneli")); + * auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), [](const std::unique_ptr &userPointer) -> const User & { + * return *userPointer; + * })); + * storage.execute(statement); + * ``` + */ + template + internal::replace_range_t replace_range(It from, It to, L transformer) { + return {{std::move(from), std::move(to)}, std::move(transformer)}; + } + /** * Create an insert range statement + * @example + * ``` + * std::vector users; + * users.push_back(User{1, "Leony"}); + * auto statement = storage.prepare(insert_range(users.begin(), users.end())); + * storage.execute(statement); + * ``` */ template - internal::insert_range_t insert_range(It from, It to) { - return {{std::move(from), std::move(to)}}; + internal::insert_range_t::value_type> + insert_range(It from, It to) { + return {{std::move(from), std::move(to)}, internal::default_transformer{}}; } + /** + * Create an insert range statement with explicit transformer. Transformer is used to apply containers with no strict objects with other kind of objects like pointers, + * optionals or whatever. + * @example + * ``` + * std::vector> userPointers; + * userPointers.push_back(std::make_unique(1, "Eneli")); + * auto statement = storage.prepare(insert_range(userPointers.begin(), userPointers.end(), [](const std::unique_ptr &userPointer) -> const User & { + * return *userPointer; + * })); + * storage.execute(statement); + * ``` + */ + template + internal::insert_range_t insert_range(It from, It to, L transformer) { + return {{std::move(from), std::move(to)}, std::move(transformer)}; + } /** * Create a replace statement. * T is an object type mapped to a storage. @@ -7551,38 +10490,91 @@ namespace sqlite_orm { // #include "values.h" -#include // std::vector -#include -#include // std::tuple +// #include "function.h" + +// #include "ast/excluded.h" namespace sqlite_orm { + namespace internal { + + template + struct excluded_t { + using expression_type = T; + + expression_type expression; + }; + } + + template + internal::excluded_t excluded(T expression) { + return {std::move(expression)}; + } +} + +// #include "ast/upsert_clause.h" +#include // std::tuple +#include // std::false_type, std::true_type + +namespace sqlite_orm { namespace internal { + template + struct upsert_clause; + template - struct values_t { - std::tuple tuple; + struct conflict_target { + using args_tuple = std::tuple; + + args_tuple args; + + upsert_clause> do_nothing() { + return {std::move(this->args), {}}; + } + + template + upsert_clause> do_update(ActionsArgs... actions) { + return {std::move(this->args), {std::make_tuple(std::forward(actions)...)}}; + } }; - template - struct dynamic_values_t { - std::vector vector; + template + struct upsert_clause, std::tuple> { + using target_args_tuple = std::tuple; + using actions_tuple = std::tuple; + + target_args_tuple target_args; + + actions_tuple actions; }; - } + template + struct is_upsert_clause : std::false_type {}; - template - internal::values_t values(Args... args) { - return {{std::forward(args)...}}; + template + struct is_upsert_clause> : std::true_type {}; } - template - internal::dynamic_values_t values(std::vector vector) { - return {{move(vector)}}; + /** + * ON CONFLICT upsert clause builder function. + * @example + * storage.insert(into(), + * columns(&Employee::id, &Employee::name, &Employee::age, &Employee::address, &Employee::salary), + * values(std::make_tuple(3, "Sofia", 26, "Madrid", 15000.0), + * std::make_tuple(4, "Doja", 26, "LA", 25000.0)), + * on_conflict(&Employee::id).do_update(set(c(&Employee::name) = excluded(&Employee::name), + * c(&Employee::age) = excluded(&Employee::age), + * c(&Employee::address) = excluded(&Employee::address), + * c(&Employee::salary) = excluded(&Employee::salary)))); + */ + template + internal::conflict_target on_conflict(Args... args) { + return {std::tuple(std::forward(args)...)}; } - } +// #include "ast/where.h" + namespace sqlite_orm { namespace internal { @@ -7604,7 +10596,7 @@ namespace sqlite_orm { * L is a callable type. Mostly is a templated lambda */ template - void operator()(const T &t, const L &l) const { + void operator()(const T& t, const L& l) const { l(t); } }; @@ -7613,18 +10605,50 @@ namespace sqlite_orm { * Simplified API */ template - void iterate_ast(const T &t, const L &l) { + void iterate_ast(const T& t, const L& l) { ast_iterator iterator; iterator(t, l); } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct ast_iterator, void> { + using node_type = as_optional_t; + + template + void operator()(const node_type& node, const L& lambda) const { + iterate_ast(node.value, lambda); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template struct ast_iterator, void> { using node_type = std::reference_wrapper; template - void operator()(const node_type &r, const L &l) const { - iterate_ast(r.get(), l); + void operator()(const node_type& r, const L& lambda) const { + iterate_ast(r.get(), lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = excluded_t; + + template + void operator()(const node_type& expression, const L& lambda) const { + iterate_ast(expression.expression, lambda); + } + }; + + template + struct ast_iterator, std::tuple>, void> { + using node_type = upsert_clause, std::tuple>; + + template + void operator()(const node_type& expression, const L& lambda) const { + iterate_ast(expression.actions, lambda); } }; @@ -7633,8 +10657,8 @@ namespace sqlite_orm { using node_type = where_t; template - void operator()(const node_type &where, const L &l) const { - iterate_ast(where.c, l); + void operator()(const node_type& expression, const L& lambda) const { + iterate_ast(expression.expression, lambda); } }; @@ -7643,7 +10667,7 @@ namespace sqlite_orm { using node_type = T; template - void operator()(const node_type &binaryCondition, const L &l) const { + void operator()(const node_type& binaryCondition, const L& l) const { iterate_ast(binaryCondition.l, l); iterate_ast(binaryCondition.r, l); } @@ -7654,7 +10678,7 @@ namespace sqlite_orm { using node_type = binary_operator; template - void operator()(const node_type &binaryOperator, const C &l) const { + void operator()(const node_type& binaryOperator, const C& l) const { iterate_ast(binaryOperator.lhs, l); iterate_ast(binaryOperator.rhs, l); } @@ -7665,19 +10689,30 @@ namespace sqlite_orm { using node_type = columns_t; template - void operator()(const node_type &cols, const L &l) const { + void operator()(const node_type& cols, const L& l) const { iterate_ast(cols.columns, l); } }; template - struct ast_iterator, void> { - using node_type = in_t; + struct ast_iterator, void> { + using node_type = dynamic_in_t; template - void operator()(const node_type &in, const C &l) const { - iterate_ast(in.l, l); - iterate_ast(in.arg, l); + void operator()(const node_type& in, const C& l) const { + iterate_ast(in.left, l); + iterate_ast(in.argument, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = in_t; + + template + void operator()(const node_type& in, const C& l) const { + iterate_ast(in.left, l); + iterate_ast(in.argument, l); } }; @@ -7686,8 +10721,8 @@ namespace sqlite_orm { using node_type = std::vector; template - void operator()(const node_type &vec, const L &l) const { - for(auto &i: vec) { + void operator()(const node_type& vec, const L& l) const { + for(auto& i: vec) { iterate_ast(i, l); } } @@ -7698,7 +10733,7 @@ namespace sqlite_orm { using node_type = std::vector; template - void operator()(const node_type &vec, const L &l) const { + void operator()(const node_type& vec, const L& l) const { l(vec); } }; @@ -7708,18 +10743,48 @@ namespace sqlite_orm { using node_type = T; template - void operator()(const node_type &c, const L &l) const { + void operator()(const node_type& c, const L& l) const { iterate_ast(c.left, l); iterate_ast(c.right, l); } }; + template + struct ast_iterator, void> { + using node_type = into_t; + + template + void operator()(const node_type& node, const L& l) const { + //.. + } + }; + + template + struct ast_iterator, void> { + using node_type = insert_raw_t; + + template + void operator()(const node_type& node, const L& l) const { + iterate_ast(node.args, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = replace_raw_t; + + template + void operator()(const node_type& node, const L& l) const { + iterate_ast(node.args, l); + } + }; + template struct ast_iterator, void> { using node_type = select_t; template - void operator()(const node_type &sel, const L &l) const { + void operator()(const node_type& sel, const L& l) const { iterate_ast(sel.col, l); iterate_ast(sel.conditions, l); } @@ -7730,7 +10795,7 @@ namespace sqlite_orm { using node_type = get_all_t; template - void operator()(const node_type &get, const L &l) const { + void operator()(const node_type& get, const L& l) const { iterate_ast(get.conditions, l); } }; @@ -7740,7 +10805,7 @@ namespace sqlite_orm { using node_type = get_all_pointer_t; template - void operator()(const node_type &get, const L &l) const { + void operator()(const node_type& get, const L& l) const { iterate_ast(get.conditions, l); } }; @@ -7751,7 +10816,7 @@ namespace sqlite_orm { using node_type = get_all_optional_t; template - void operator()(const node_type &get, const L &l) const { + void operator()(const node_type& get, const L& l) const { iterate_ast(get.conditions, l); } }; @@ -7762,7 +10827,7 @@ namespace sqlite_orm { using node_type = update_all_t, Wargs...>; template - void operator()(const node_type &u, const L &l) const { + void operator()(const node_type& u, const L& l) const { iterate_ast(u.set, l); iterate_ast(u.conditions, l); } @@ -7773,7 +10838,7 @@ namespace sqlite_orm { using node_type = remove_all_t; template - void operator()(const node_type &r, const L &l) const { + void operator()(const node_type& r, const L& l) const { iterate_ast(r.conditions, l); } }; @@ -7783,7 +10848,7 @@ namespace sqlite_orm { using node_type = set_t; template - void operator()(const node_type &s, const L &l) const { + void operator()(const node_type& s, const L& l) const { iterate_ast(s.assigns, l); } }; @@ -7793,8 +10858,8 @@ namespace sqlite_orm { using node_type = std::tuple; template - void operator()(const node_type &tuple, const L &l) const { - iterate_tuple(tuple, [&l](auto &v) { + void operator()(const node_type& tuple, const L& l) const { + iterate_tuple(tuple, [&l](auto& v) { iterate_ast(v, l); }); } @@ -7805,7 +10870,7 @@ namespace sqlite_orm { using node_type = having_t; template - void operator()(const node_type &hav, const L &l) const { + void operator()(const node_type& hav, const L& l) const { iterate_ast(hav.t, l); } }; @@ -7815,7 +10880,7 @@ namespace sqlite_orm { using node_type = cast_t; template - void operator()(const node_type &c, const L &l) const { + void operator()(const node_type& c, const L& l) const { iterate_ast(c.expression, l); } }; @@ -7825,7 +10890,7 @@ namespace sqlite_orm { using node_type = exists_t; template - void operator()(const node_type &e, const L &l) const { + void operator()(const node_type& e, const L& l) const { iterate_ast(e.t, l); } }; @@ -7835,10 +10900,10 @@ namespace sqlite_orm { using node_type = like_t; template - void operator()(const node_type &lk, const L &l) const { + void operator()(const node_type& lk, const L& l) const { iterate_ast(lk.arg, l); iterate_ast(lk.pattern, l); - lk.arg3.apply([&l](auto &value) { + lk.arg3.apply([&l](auto& value) { iterate_ast(value, l); }); } @@ -7849,7 +10914,7 @@ namespace sqlite_orm { using node_type = glob_t; template - void operator()(const node_type &lk, const L &l) const { + void operator()(const node_type& lk, const L& l) const { iterate_ast(lk.arg, l); iterate_ast(lk.pattern, l); } @@ -7860,7 +10925,7 @@ namespace sqlite_orm { using node_type = between_t; template - void operator()(const node_type &b, const L &l) const { + void operator()(const node_type& b, const L& l) const { iterate_ast(b.expr, l); iterate_ast(b.b1, l); iterate_ast(b.b2, l); @@ -7872,7 +10937,7 @@ namespace sqlite_orm { using node_type = named_collate; template - void operator()(const node_type &col, const L &l) const { + void operator()(const node_type& col, const L& l) const { iterate_ast(col.expr, l); } }; @@ -7882,7 +10947,7 @@ namespace sqlite_orm { using node_type = negated_condition_t; template - void operator()(const node_type &neg, const L &l) const { + void operator()(const node_type& neg, const L& l) const { iterate_ast(neg.c, l); } }; @@ -7892,7 +10957,7 @@ namespace sqlite_orm { using node_type = is_null_t; template - void operator()(const node_type &i, const L &l) const { + void operator()(const node_type& i, const L& l) const { iterate_ast(i.t, l); } }; @@ -7902,17 +10967,27 @@ namespace sqlite_orm { using node_type = is_not_null_t; template - void operator()(const node_type &i, const L &l) const { + void operator()(const node_type& i, const L& l) const { iterate_ast(i.t, l); } }; + template + struct ast_iterator, void> { + using node_type = function_call; + + template + void operator()(const node_type& f, const L& l) const { + iterate_ast(f.args, l); + } + }; + template - struct ast_iterator, void> { - using node_type = core_function_t; + struct ast_iterator, void> { + using node_type = built_in_function_t; template - void operator()(const node_type &f, const L &l) const { + void operator()(const node_type& f, const L& l) const { iterate_ast(f.args, l); } }; @@ -7922,7 +10997,7 @@ namespace sqlite_orm { using node_type = left_join_t; template - void operator()(const node_type &j, const L &l) const { + void operator()(const node_type& j, const L& l) const { iterate_ast(j.constraint, l); } }; @@ -7932,7 +11007,7 @@ namespace sqlite_orm { using node_type = on_t; template - void operator()(const node_type &o, const L &l) const { + void operator()(const node_type& o, const L& l) const { iterate_ast(o.arg, l); } }; @@ -7942,7 +11017,7 @@ namespace sqlite_orm { using node_type = join_t; template - void operator()(const node_type &j, const L &l) const { + void operator()(const node_type& j, const L& l) const { iterate_ast(j.constraint, l); } }; @@ -7952,7 +11027,7 @@ namespace sqlite_orm { using node_type = left_outer_join_t; template - void operator()(const node_type &j, const L &l) const { + void operator()(const node_type& j, const L& l) const { iterate_ast(j.constraint, l); } }; @@ -7962,7 +11037,7 @@ namespace sqlite_orm { using node_type = inner_join_t; template - void operator()(const node_type &j, const L &l) const { + void operator()(const node_type& j, const L& l) const { iterate_ast(j.constraint, l); } }; @@ -7972,15 +11047,15 @@ namespace sqlite_orm { using node_type = simple_case_t; template - void operator()(const node_type &c, const L &l) const { - c.case_expression.apply([&l](auto &c_) { + void operator()(const node_type& c, const L& l) const { + c.case_expression.apply([&l](auto& c_) { iterate_ast(c_, l); }); - iterate_tuple(c.args, [&l](auto &pair) { + iterate_tuple(c.args, [&l](auto& pair) { iterate_ast(pair.first, l); iterate_ast(pair.second, l); }); - c.else_expression.apply([&l](auto &el) { + c.else_expression.apply([&l](auto& el) { iterate_ast(el, l); }); } @@ -7991,7 +11066,7 @@ namespace sqlite_orm { using node_type = as_t; template - void operator()(const node_type &a, const L &l) const { + void operator()(const node_type& a, const L& l) const { iterate_ast(a.expression, l); } }; @@ -8001,7 +11076,7 @@ namespace sqlite_orm { using node_type = limit_t; template - void operator()(const node_type &a, const L &l) const { + void operator()(const node_type& a, const L& l) const { iterate_ast(a.lim, l); } }; @@ -8011,9 +11086,9 @@ namespace sqlite_orm { using node_type = limit_t; template - void operator()(const node_type &a, const L &l) const { + void operator()(const node_type& a, const L& l) const { iterate_ast(a.lim, l); - a.off.apply([&l](auto &value) { + a.off.apply([&l](auto& value) { iterate_ast(value, l); }); } @@ -8024,8 +11099,8 @@ namespace sqlite_orm { using node_type = limit_t; template - void operator()(const node_type &a, const L &l) const { - a.off.apply([&l](auto &value) { + void operator()(const node_type& a, const L& l) const { + a.off.apply([&l](auto& value) { iterate_ast(value, l); }); iterate_ast(a.lim, l); @@ -8037,8 +11112,8 @@ namespace sqlite_orm { using node_type = distinct_t; template - void operator()(const node_type &a, const L &l) const { - iterate_ast(a.t, l); + void operator()(const node_type& a, const L& l) const { + iterate_ast(a.value, l); } }; @@ -8047,8 +11122,8 @@ namespace sqlite_orm { using node_type = all_t; template - void operator()(const node_type &a, const L &l) const { - iterate_ast(a.t, l); + void operator()(const node_type& a, const L& l) const { + iterate_ast(a.value, l); } }; @@ -8057,7 +11132,7 @@ namespace sqlite_orm { using node_type = bitwise_not_t; template - void operator()(const node_type &a, const L &l) const { + void operator()(const node_type& a, const L& l) const { iterate_ast(a.argument, l); } }; @@ -8067,7 +11142,7 @@ namespace sqlite_orm { using node_type = values_t; template - void operator()(const node_type &node, const L &l) const { + void operator()(const node_type& node, const L& l) const { iterate_ast(node.tuple, l); } }; @@ -8077,7 +11152,7 @@ namespace sqlite_orm { using node_type = dynamic_values_t; template - void operator()(const node_type &node, const L &l) const { + void operator()(const node_type& node, const L& l) const { iterate_ast(node.vector, l); } }; @@ -8087,7 +11162,7 @@ namespace sqlite_orm { using node_type = collate_t; template - void operator()(const node_type &node, const L &l) const { + void operator()(const node_type& node, const L& l) const { iterate_ast(node.expr, l); } }; @@ -8103,17 +11178,26 @@ namespace sqlite_orm { namespace internal { + /** + * This class does not related to SQL view. This is a container like class which is returned by + * by storage_t::iterate function. This class contains STL functions: + * - size_t size() + * - bool empty() + * - iterator end() + * - iterator begin() + * All these functions are not right const cause all of them may open SQLite connections. + */ template struct view_t { using mapped_type = T; using storage_type = S; using self = view_t; - storage_type &storage; + storage_type& storage; connection_ref connection; get_all_t, Args...> args; - view_t(storage_type &stor, decltype(connection) conn, Args &&... args_) : + view_t(storage_type& stor, decltype(connection) conn, Args&&... args_) : storage(stor), connection(std::move(conn)), args{std::make_tuple(std::forward(args_)...)} {} size_t size() { @@ -8124,12 +11208,8 @@ namespace sqlite_orm { return !this->size(); } - iterator_t end() { - return {nullptr, *this}; - } - iterator_t begin() { - sqlite3_stmt *stmt = nullptr; + sqlite3_stmt* stmt = nullptr; auto db = this->connection.get(); using context_t = serializator_context; context_t context{this->storage.impl}; @@ -8139,7 +11219,7 @@ namespace sqlite_orm { auto ret = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); if(ret == SQLITE_OK) { auto index = 1; - iterate_ast(this->args.conditions, [&index, stmt, db](auto &node) { + iterate_ast(this->args.conditions, [&index, stmt, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -8153,6 +11233,10 @@ namespace sqlite_orm { sqlite3_errmsg(db)); } } + + iterator_t end() { + return {}; + } }; } } @@ -8179,9 +11263,12 @@ namespace sqlite_orm { #include #include // std::function #include // std::shared_ptr +#include // std::vector // #include "error_code.h" +// #include "util.h" + // #include "row_extractor.h" // #include "journal_mode.h" @@ -8192,126 +11279,141 @@ namespace sqlite_orm { namespace internal { struct storage_base; - } - - struct pragma_t { - using get_connection_t = std::function; - pragma_t(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} - - void busy_timeout(int value) { - this->set_pragma("busy_timeout", value); + template + int getPragmaCallback(void* data, int argc, char** argv, char**) { + auto& res = *(T*)data; + if(argc) { + res = row_extractor().extract(argv[0]); + } + return 0; } - int busy_timeout() { - return this->get_pragma("busy_timeout"); + template<> + inline int getPragmaCallback>(void* data, int argc, char** argv, char**) { + auto& res = *(std::vector*)data; + res.reserve(argc); + for(decltype(argc) i = 0; i < argc; ++i) { + auto rowString = row_extractor().extract(argv[i]); + res.push_back(move(rowString)); + } + return 0; } - sqlite_orm::journal_mode journal_mode() { - return this->get_pragma("journal_mode"); - } + struct pragma_t { + using get_connection_t = std::function; - void journal_mode(sqlite_orm::journal_mode value) { - this->_journal_mode = -1; - this->set_pragma("journal_mode", value); - this->_journal_mode = static_cast_journal_mode)>(value); - } + pragma_t(get_connection_t get_connection_) : get_connection(move(get_connection_)) {} - int synchronous() { - return this->get_pragma("synchronous"); - } + void busy_timeout(int value) { + this->set_pragma("busy_timeout", value); + } - void synchronous(int value) { - this->_synchronous = -1; - this->set_pragma("synchronous", value); - this->_synchronous = value; - } + int busy_timeout() { + return this->get_pragma("busy_timeout"); + } - int user_version() { - return this->get_pragma("user_version"); - } + sqlite_orm::journal_mode journal_mode() { + return this->get_pragma("journal_mode"); + } - void user_version(int value) { - this->set_pragma("user_version", value); - } + void journal_mode(sqlite_orm::journal_mode value) { + this->_journal_mode = -1; + this->set_pragma("journal_mode", value); + this->_journal_mode = static_cast_journal_mode)>(value); + } - int auto_vacuum() { - return this->get_pragma("auto_vacuum"); - } + int synchronous() { + return this->get_pragma("synchronous"); + } - void auto_vacuum(int value) { - this->set_pragma("auto_vacuum", value); - } + void synchronous(int value) { + this->_synchronous = -1; + this->set_pragma("synchronous", value); + this->_synchronous = value; + } - protected: - friend struct storage_base; + int user_version() { + return this->get_pragma("user_version"); + } - public: - int _synchronous = -1; - signed char _journal_mode = -1; // if != -1 stores static_cast(journal_mode) - get_connection_t get_connection; - - template - T get_pragma(const std::string &name) { - auto connection = this->get_connection(); - auto query = "PRAGMA " + name; - T result; - auto db = connection.get(); - auto rc = sqlite3_exec( - db, - query.c_str(), - [](void *data, int argc, char **argv, char **) -> int { - auto &res = *(T *)data; - if(argc) { - res = row_extractor().extract(argv[0]); - } - return 0; - }, - &result, - nullptr); - if(rc == SQLITE_OK) { - return result; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + void user_version(int value) { + this->set_pragma("user_version", value); } - } - /** - * Yevgeniy Zakharov: I wanted to refactore this function with statements and value bindings - * but it turns out that bindings in pragma statements are not supported. - */ - template - void set_pragma(const std::string &name, const T &value, sqlite3 *db = nullptr) { - auto con = this->get_connection(); - if(!db) { - db = con.get(); + int auto_vacuum() { + return this->get_pragma("auto_vacuum"); } - std::stringstream ss; - ss << "PRAGMA " << name << " = " << value; - auto query = ss.str(); - auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + + void auto_vacuum(int value) { + this->set_pragma("auto_vacuum", value); } - } - void set_pragma(const std::string &name, const sqlite_orm::journal_mode &value, sqlite3 *db = nullptr) { - auto con = this->get_connection(); - if(!db) { - db = con.get(); + std::vector integrity_check() { + return this->get_pragma>("integrity_check"); } - std::stringstream ss; - ss << "PRAGMA " << name << " = " << internal::to_string(value); - auto query = ss.str(); - auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + + template + std::vector integrity_check(T table_name) { + std::ostringstream oss; + oss << "integrity_check(" << table_name << ")"; + return this->get_pragma>(oss.str()); } - } - }; + + std::vector integrity_check(int n) { + std::ostringstream oss; + oss << "integrity_check(" << n << ")"; + return this->get_pragma>(oss.str()); + } + + private: + friend struct storage_base; + + int _synchronous = -1; + signed char _journal_mode = -1; // if != -1 stores static_cast(journal_mode) + get_connection_t get_connection; + + template + T get_pragma(const std::string& name) { + auto connection = this->get_connection(); + auto query = "PRAGMA " + name; + T result; + auto db = connection.get(); + auto rc = sqlite3_exec(db, query.c_str(), getPragmaCallback, &result, nullptr); + if(rc == SQLITE_OK) { + return result; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + + /** + * Yevgeniy Zakharov: I wanted to refactor this function with statements and value bindings + * but it turns out that bindings in pragma statements are not supported. + */ + template + void set_pragma(const std::string& name, const T& value, sqlite3* db = nullptr) { + auto con = this->get_connection(); + if(!db) { + db = con.get(); + } + std::stringstream ss; + ss << "PRAGMA " << name << " = " << value; + internal::perform_void_exec(db, ss.str()); + } + + void set_pragma(const std::string& name, const sqlite_orm::journal_mode& value, sqlite3* db = nullptr) { + auto con = this->get_connection(); + if(!db) { + db = con.get(); + } + std::stringstream ss; + ss << "PRAGMA " << name << " = " << internal::to_string(value); + internal::perform_void_exec(db, ss.str()); + } + }; + } } // #include "limit_accesor.h" @@ -8526,10 +11628,12 @@ namespace sqlite_orm { // #include "type_printer.h" -// #include "tuple_helper.h" +// #include "tuple_helper/tuple_helper.h" // #include "row_extractor.h" +// #include "util.h" + // #include "connection_holder.h" // #include "backup.h" @@ -8554,9 +11658,9 @@ namespace sqlite_orm { */ struct backup_t { backup_t(connection_ref to_, - const std::string &zDestName, + const std::string& zDestName, connection_ref from_, - const std::string &zSourceName, + const std::string& zSourceName, std::unique_ptr holder_) : handle(sqlite3_backup_init(to_.get(), zDestName.c_str(), from_.get(), zSourceName.c_str())), to(to_), from(from_), holder(move(holder_)) { @@ -8565,7 +11669,7 @@ namespace sqlite_orm { } } - backup_t(backup_t &&other) : + backup_t(backup_t&& other) : handle(other.handle), to(other.to), from(other.from), holder(move(other.holder)) { other.handle = nullptr; } @@ -8598,23 +11702,215 @@ namespace sqlite_orm { return sqlite3_backup_pagecount(this->handle); } - protected: - sqlite3_backup *handle = nullptr; - connection_ref to; - connection_ref from; - std::unique_ptr holder; + protected: + sqlite3_backup* handle = nullptr; + connection_ref to; + connection_ref from; + std::unique_ptr holder; + }; + } +} + +// #include "function.h" + +// #include "values_to_tuple.h" + +#include +#include // std::get, std::tuple_element + +// #include "row_extractor.h" + +// #include "arg_values.h" + +#include + +// #include "row_extractor.h" + +namespace sqlite_orm { + + struct arg_value { + + arg_value() : arg_value(nullptr) {} + + arg_value(sqlite3_value* value_) : value(value_) {} + + template + T get() const { + return row_extractor().extract(this->value); + } + + bool is_null() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_NULL; + } + + bool is_text() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_TEXT; + } + + bool is_integer() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_INTEGER; + } + + bool is_float() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_FLOAT; + } + + bool is_blob() const { + auto type = sqlite3_value_type(this->value); + return type == SQLITE_BLOB; + } + + bool empty() const { + return this->value == nullptr; + } + + private: + sqlite3_value* value = nullptr; + }; + + struct arg_values { + + struct iterator { + + iterator(const arg_values& container_, int index_) : + container(container_), index(index_), + currentValue(index_ < int(container_.size()) ? container_[index_] : arg_value()) {} + + iterator& operator++() { + ++this->index; + if(this->index < int(this->container.size())) { + this->currentValue = this->container[this->index]; + } else { + this->currentValue = {}; + } + return *this; + } + + iterator operator++(int) { + auto res = *this; + ++this->index; + if(this->index < int(this->container.size())) { + this->currentValue = this->container[this->index]; + } else { + this->currentValue = {}; + } + return res; + } + + arg_value operator*() const { + if(this->index < int(this->container.size()) && this->index >= 0) { + return this->currentValue; + } else { + throw std::system_error(std::make_error_code(orm_error_code::index_is_out_of_bounds)); + } + } + + arg_value* operator->() const { + return &this->currentValue; + } + + bool operator==(const iterator& other) const { + return &other.container == &this->container && other.index == this->index; + } + + bool operator!=(const iterator& other) const { + return !(*this == other); + } + + private: + const arg_values& container; + int index = 0; + mutable arg_value currentValue; + }; + + arg_values() : arg_values(0, nullptr) {} + + arg_values(int argsCount_, sqlite3_value** values_) : argsCount(argsCount_), values(values_) {} + + size_t size() const { + return this->argsCount; + } + + bool empty() const { + return 0 == this->argsCount; + } + + arg_value operator[](int index) const { + if(index < this->argsCount && index >= 0) { + auto valuePointer = this->values[index]; + return {valuePointer}; + } else { + throw std::system_error(std::make_error_code(orm_error_code::index_is_out_of_bounds)); + } + } + + arg_value at(int index) const { + return this->operator[](index); + } + + iterator begin() const { + return {*this, 0}; + } + + iterator end() const { + return {*this, this->argsCount}; + } + + private: + int argsCount = 0; + sqlite3_value** values = nullptr; + }; +} + +namespace sqlite_orm { + + namespace internal { + + /** + * T is a std::tuple type + * I is index to extract value from values C array to tuple. I must me < std::tuple_size::value + */ + template + struct values_to_tuple { + + void extract(sqlite3_value** values, T& tuple, int argsCount) const { + using element_type = typename std::tuple_element::type; + std::get(tuple) = row_extractor().extract(values[I]); + + values_to_tuple().extract(values, tuple, argsCount); + } + }; + + template + struct values_to_tuple { + void extract(sqlite3_value** values, T& tuple, int argsCount) const { + //.. + } + }; + + template<> + struct values_to_tuple, 0> { + void extract(sqlite3_value** values, std::tuple& tuple, int argsCount) const { + std::get<0>(tuple) = arg_values(argsCount, values); + } }; } } +// #include "arg_values.h" + namespace sqlite_orm { namespace internal { struct storage_base { - using collating_function = std::function; + using collating_function = std::function; - std::function on_open; + std::function on_open; pragma_t pragma; limit_accesor limit; @@ -8625,50 +11921,20 @@ namespace sqlite_orm { return {this->get_connection(), move(commitFunc), move(rollbackFunc)}; } - void drop_index(const std::string &indexName) { - auto con = this->get_connection(); - auto db = con.get(); + void drop_index(const std::string& indexName) { std::stringstream ss; ss << "DROP INDEX '" << indexName + "'"; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_void_exec(get_connection().get(), ss.str()); } void vacuum() { - auto con = this->get_connection(); - auto db = con.get(); - std::string query = "VACUUM"; - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_void_exec(get_connection().get(), "VACUUM"); } /** * Drops table with given name. */ - void drop_table(const std::string &tableName) { + void drop_table(const std::string& tableName) { auto con = this->get_connection(); this->drop_table_internal(tableName, con.get()); } @@ -8676,11 +11942,10 @@ namespace sqlite_orm { /** * Rename table named `from` to `to`. */ - void rename_table(const std::string &from, const std::string &to) { - auto con = this->get_connection(); + void rename_table(const std::string& from, const std::string& to) { std::stringstream ss; ss << "ALTER TABLE '" << from << "' RENAME TO '" << to << "'"; - this->perform_query_without_result(ss.str(), con.get()); + perform_void_exec(get_connection().get(), ss.str()); } /** @@ -8716,13 +11981,13 @@ namespace sqlite_orm { return sqlite3_libversion(); } - bool transaction(const std::function &f) { - this->begin_transaction(); + bool transaction(const std::function& f) { + auto guard = transaction_guard(); auto shouldCommit = f(); if(shouldCommit) { - this->commit(); + guard.commit(); } else { - this->rollback(); + guard.rollback(); } return shouldCommit; } @@ -8759,8 +12024,8 @@ namespace sqlite_orm { int res = sqlite3_exec( db, sql.c_str(), - [](void *data, int argc, char **argv, char * * /*columnName*/) -> int { - auto &tableNames_ = *(data_t *)data; + [](void* data, int argc, char** argv, char** /*columnName*/) -> int { + auto& tableNames_ = *(data_t*)data; for(int i = 0; i < argc; i++) { if(argv[i]) { tableNames_.push_back(argv[i]); @@ -8786,9 +12051,166 @@ namespace sqlite_orm { } } - void create_collation(const std::string &name, collating_function f) { - collating_function *functionPointer = nullptr; - if(f) { + /** + * Call this to create user defined scalar function. Can be called at any time no matter connection is opened or no. + * T - function class. T must have operator() overload and static name function like this: + * ``` + * struct SqrtFunction { + * + * double operator()(double arg) const { + * return std::sqrt(arg); + * } + * + * static const char *name() { + * return "SQRT"; + * } + * }; + * ``` + */ + template + void create_scalar_function() { + static_assert(is_scalar_function::value, "F cannot be a scalar function"); + + std::stringstream ss; + ss << F::name(); + auto name = ss.str(); + using args_tuple = typename callable_arguments::args_tuple; + using return_type = typename callable_arguments::return_type; + auto argsCount = int(std::tuple_size::value); + if(std::is_same>::value) { + argsCount = -1; + } + this->scalarFunctions.emplace_back(new scalar_function_t{ + move(name), + argsCount, + []() -> int* { + return (int*)(new F()); + }, + /* call = */ + [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { + auto& functionPointer = *static_cast(functionVoidPointer); + args_tuple argsTuple; + using tuple_size = std::tuple_size; + values_to_tuple().extract(values, argsTuple, argsCount); + auto result = call(functionPointer, std::move(argsTuple)); + statement_binder().result(context, result); + }, + delete_function_callback, + }); + + if(this->connection->retain_count() > 0) { + auto db = this->connection->get(); + try_to_create_function(db, static_cast(*this->scalarFunctions.back())); + } + } + + /** + * Call this to create user defined aggregate function. Can be called at any time no matter connection is opened or no. + * T - function class. T must have step member function, fin member function and static name function like this: + * ``` + * struct MeanFunction { + * double total = 0; + * int count = 0; + * + * void step(double value) { + * total += value; + * ++count; + * } + * + * int fin() const { + * return total / count; + * } + * + * static std::string name() { + * return "MEAN"; + * } + * }; + * ``` + */ + template + void create_aggregate_function() { + static_assert(is_aggregate_function::value, "F cannot be an aggregate function"); + + std::stringstream ss; + ss << F::name(); + auto name = ss.str(); + using args_tuple = typename callable_arguments::args_tuple; + using return_type = typename callable_arguments::return_type; + auto argsCount = int(std::tuple_size::value); + if(std::is_same>::value) { + argsCount = -1; + } + this->aggregateFunctions.emplace_back(new aggregate_function_t{ + move(name), + argsCount, + /* create = */ + []() -> int* { + return (int*)(new F()); + }, + /* step = */ + [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { + auto& functionPointer = *static_cast(functionVoidPointer); + args_tuple argsTuple; + using tuple_size = std::tuple_size; + values_to_tuple().extract(values, argsTuple, argsCount); + call(functionPointer, &F::step, move(argsTuple)); + }, + /* finalCall = */ + [](sqlite3_context* context, void* functionVoidPointer) { + auto& functionPointer = *static_cast(functionVoidPointer); + auto result = functionPointer.fin(); + statement_binder().result(context, result); + }, + delete_function_callback, + }); + + if(this->connection->retain_count() > 0) { + auto db = this->connection->get(); + try_to_create_function(db, static_cast(*this->aggregateFunctions.back())); + } + } + + /** + * Use it to delete scalar function you created before. Can be called at any time no matter connection is open or no. + */ + template + void delete_scalar_function() { + static_assert(is_scalar_function::value, "F cannot be a scalar function"); + std::stringstream ss; + ss << F::name(); + auto name = ss.str(); + this->delete_function_impl(name, this->scalarFunctions); + } + + /** + * Use it to delete aggregate function you created before. Can be called at any time no matter connection is open or no. + */ + template + void delete_aggregate_function() { + static_assert(is_aggregate_function::value, "F cannot be an aggregate function"); + std::stringstream ss; + ss << F::name(); + auto name = ss.str(); + this->delete_function_impl(name, this->aggregateFunctions); + } + + template + void create_collation() { + collating_function func = [](int leftLength, const void* lhs, int rightLength, const void* rhs) { + C collatingObject; + return collatingObject(leftLength, lhs, rightLength, rhs); + }; + std::stringstream ss; + ss << C::name(); + auto name = ss.str(); + ss.flush(); + this->create_collation(name, move(func)); + } + + void create_collation(const std::string& name, collating_function f) { + collating_function* functionPointer = nullptr; + const auto functionExists = bool(f); + if(functionExists) { functionPointer = &(collatingFunctions[name] = std::move(f)); } else { collatingFunctions.erase(name); @@ -8797,29 +12219,39 @@ namespace sqlite_orm { // create collations if db is open if(this->connection->retain_count() > 0) { auto db = this->connection->get(); - if(sqlite3_create_collation(db, - name.c_str(), - SQLITE_UTF8, - functionPointer, - f ? collate_callback : nullptr) != SQLITE_OK) { + auto resultCode = sqlite3_create_collation(db, + name.c_str(), + SQLITE_UTF8, + functionPointer, + functionExists ? collate_callback : nullptr); + if(resultCode != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } } + template + void delete_collation() { + std::stringstream ss; + ss << C::name(); + auto name = ss.str(); + ss.flush(); + this->create_collation(name, {}); + } + void begin_transaction() { this->connection->retain(); if(1 == this->connection->retain_count()) { this->on_open_internal(this->connection->get()); } auto db = this->connection->get(); - this->begin_transaction(db); + perform_void_exec(db, "BEGIN TRANSACTION"); } void commit() { auto db = this->connection->get(); - this->commit(db); + perform_void_exec(db, "COMMIT"); this->connection->release(); if(this->connection->retain_count() < 0) { throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction)); @@ -8828,54 +12260,54 @@ namespace sqlite_orm { void rollback() { auto db = this->connection->get(); - this->rollback(db); + perform_void_exec(db, "ROLLBACK"); this->connection->release(); if(this->connection->retain_count() < 0) { throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction)); } } - void backup_to(const std::string &filename) { + void backup_to(const std::string& filename) { auto backup = this->make_backup_to(filename); backup.step(-1); } - void backup_to(storage_base &other) { + void backup_to(storage_base& other) { auto backup = this->make_backup_to(other); backup.step(-1); } - void backup_from(const std::string &filename) { + void backup_from(const std::string& filename) { auto backup = this->make_backup_from(filename); backup.step(-1); } - void backup_from(storage_base &other) { + void backup_from(storage_base& other) { auto backup = this->make_backup_from(other); backup.step(-1); } - backup_t make_backup_to(const std::string &filename) { + backup_t make_backup_to(const std::string& filename) { auto holder = std::make_unique(filename); connection_ref conRef{*holder}; return {conRef, "main", this->get_connection(), "main", move(holder)}; } - backup_t make_backup_to(storage_base &other) { + backup_t make_backup_to(storage_base& other) { return {other.get_connection(), "main", this->get_connection(), "main", {}}; } - backup_t make_backup_from(const std::string &filename) { + backup_t make_backup_from(const std::string& filename) { auto holder = std::make_unique(filename); connection_ref conRef{*holder}; return {this->get_connection(), "main", conRef, "main", move(holder)}; } - backup_t make_backup_from(storage_base &other) { + backup_t make_backup_from(storage_base& other) { return {this->get_connection(), "main", other.get_connection(), "main", {}}; } - const std::string &filename() const { + const std::string& filename() const { return this->connection->filename; } @@ -8901,7 +12333,7 @@ namespace sqlite_orm { } protected: - storage_base(const std::string &filename_, int foreignKeysCount) : + storage_base(const std::string& filename_, int foreignKeysCount) : pragma(std::bind(&storage_base::get_connection, this)), limit(std::bind(&storage_base::get_connection, this)), inMemory(filename_.empty() || filename_ == ":memory:"), @@ -8912,7 +12344,7 @@ namespace sqlite_orm { } } - storage_base(const storage_base &other) : + storage_base(const storage_base& other) : on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this)), limit(std::bind(&storage_base::get_connection, this)), inMemory(other.inMemory), connection(std::make_unique(other.connection->filename)), @@ -8932,13 +12364,6 @@ namespace sqlite_orm { } } - const bool inMemory; - bool isOpenedForever = false; - std::unique_ptr connection; - std::map collatingFunctions; - const int cachedForeignKeysCount; - std::function _busy_handler; - connection_ref get_connection() { connection_ref res{*this->connection}; if(1 == this->connection->retain_count()) { @@ -8949,25 +12374,20 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3006019 - void foreign_keys(sqlite3 *db, bool value) { + void foreign_keys(sqlite3* db, bool value) { std::stringstream ss; ss << "PRAGMA foreign_keys = " << value; - auto query = ss.str(); - auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_void_exec(db, ss.str()); } - bool foreign_keys(sqlite3 *db) { + bool foreign_keys(sqlite3* db) { std::string query = "PRAGMA foreign_keys"; auto result = false; auto rc = sqlite3_exec( db, query.c_str(), - [](void *data, int argc, char **argv, char **) -> int { - auto &res = *(bool *)data; + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(bool*)data; if(argc) { res = row_extractor().extract(argv[0]); } @@ -8983,7 +12403,7 @@ namespace sqlite_orm { } #endif - void on_open_internal(sqlite3 *db) { + void on_open_internal(sqlite3* db) { #if SQLITE_VERSION_NUMBER >= 3006019 if(this->cachedForeignKeysCount) { @@ -8998,15 +12418,16 @@ namespace sqlite_orm { this->pragma.set_pragma("journal_mode", static_cast(this->pragma._journal_mode), db); } - for(auto &p: this->collatingFunctions) { - if(sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback) != - SQLITE_OK) { + for(auto& p: this->collatingFunctions) { + auto resultCode = + sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback); + if(resultCode != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } - for(auto &p: this->limit.limits) { + for(auto& p: this->limit.limits) { sqlite3_limit(db, p.first, p.second); } @@ -9014,69 +12435,120 @@ namespace sqlite_orm { sqlite3_busy_handler(this->connection->get(), busy_handler_callback, this); } + for(auto& functionPointer: this->scalarFunctions) { + try_to_create_function(db, static_cast(*functionPointer)); + } + + for(auto& functionPointer: this->aggregateFunctions) { + try_to_create_function(db, static_cast(*functionPointer)); + } + if(this->on_open) { this->on_open(db); } } - void begin_transaction(sqlite3 *db) { - std::stringstream ss; - ss << "BEGIN TRANSACTION"; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + void delete_function_impl(const std::string& name, + std::vector>& functionsVector) const { + auto it = find_if(functionsVector.begin(), functionsVector.end(), [&name](auto& functionPointer) { + return functionPointer->name == name; + }); + if(it != functionsVector.end()) { + functionsVector.erase(it); + it = functionsVector.end(); + + if(this->connection->retain_count() > 0) { + auto db = this->connection->get(); + auto resultCode = sqlite3_create_function_v2(db, + name.c_str(), + 0, + SQLITE_UTF8, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr); + if(resultCode != SQLITE_OK) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } } } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + throw std::system_error(std::make_error_code(orm_error_code::function_not_found)); } } - void commit(sqlite3 *db) { - std::stringstream ss; - ss << "COMMIT"; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { + void try_to_create_function(sqlite3* db, scalar_function_t& function) { + auto resultCode = sqlite3_create_function_v2(db, + function.name.c_str(), + function.argumentsCount, + SQLITE_UTF8, + &function, + scalar_function_callback, + nullptr, + nullptr, + nullptr); + if(resultCode != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } } - void rollback(sqlite3 *db) { - std::stringstream ss; - ss << "ROLLBACK"; - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + void try_to_create_function(sqlite3* db, aggregate_function_t& function) { + auto resultCode = sqlite3_create_function(db, + function.name.c_str(), + function.argumentsCount, + SQLITE_UTF8, + &function, + nullptr, + aggregate_function_step_callback, + aggregate_function_final_callback); + if(resultCode != SQLITE_OK) { + throw std::system_error(std::error_code(resultCode, get_sqlite_error_category()), + sqlite3_errstr(resultCode)); + } + } + + static void + aggregate_function_step_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { + auto functionVoidPointer = sqlite3_user_data(context); + auto functionPointer = static_cast(functionVoidPointer); + auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); + auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); + if(*aggregateContextIntPointer == nullptr) { + *aggregateContextIntPointer = functionPointer->create(); + } + functionPointer->step(context, *aggregateContextIntPointer, argsCount, values); + } + + static void aggregate_function_final_callback(sqlite3_context* context) { + auto functionVoidPointer = sqlite3_user_data(context); + auto functionPointer = static_cast(functionVoidPointer); + auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); + auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); + functionPointer->finalCall(context, *aggregateContextIntPointer); + functionPointer->destroy(*aggregateContextIntPointer); + } + + static void scalar_function_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { + auto functionVoidPointer = sqlite3_user_data(context); + auto functionPointer = static_cast(functionVoidPointer); + std::unique_ptr callablePointer(functionPointer->create(), + functionPointer->destroy); + if(functionPointer->argumentsCount != -1 && functionPointer->argumentsCount != argsCount) { + throw std::system_error(std::make_error_code(orm_error_code::arguments_count_does_not_match)); } + functionPointer->run(context, functionPointer, argsCount, values); + } + + template + static void delete_function_callback(int* pointer) { + auto voidPointer = static_cast(pointer); + auto fPointer = static_cast(voidPointer); + delete fPointer; } - std::string current_timestamp(sqlite3 *db) { + std::string current_timestamp(sqlite3* db) { std::string result; std::stringstream ss; ss << "SELECT CURRENT_TIMESTAMP"; @@ -9084,8 +12556,8 @@ namespace sqlite_orm { auto rc = sqlite3_exec( db, query.c_str(), - [](void *data, int argc, char **argv, char **) -> int { - auto &res = *(std::string *)data; + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(std::string*)data; if(argc) { if(argv[0]) { res = row_extractor().extract(argv[0]); @@ -9102,35 +12574,19 @@ namespace sqlite_orm { return result; } - void drop_table_internal(const std::string &tableName, sqlite3 *db) { + void drop_table_internal(const std::string& tableName, sqlite3* db) { std::stringstream ss; ss << "DROP TABLE '" << tableName + "'"; - this->perform_query_without_result(ss.str(), db); + perform_void_exec(db, ss.str()); } - void perform_query_without_result(const std::string &query, sqlite3 *db) { - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - - static int collate_callback(void *arg, int leftLen, const void *lhs, int rightLen, const void *rhs) { - auto &f = *(collating_function *)arg; + static int collate_callback(void* arg, int leftLen, const void* lhs, int rightLen, const void* rhs) { + auto& f = *(collating_function*)arg; return f(leftLen, lhs, rightLen, rhs); } - static int busy_handler_callback(void *selfPointer, int triesCount) { - auto &storage = *static_cast(selfPointer); + static int busy_handler_callback(void* selfPointer, int triesCount) { + auto& storage = *static_cast(selfPointer); if(storage._busy_handler) { return storage._busy_handler(triesCount); } else { @@ -9140,13 +12596,22 @@ namespace sqlite_orm { // returns foreign keys count in storage definition template - static int foreign_keys_count(T &storageImpl) { + static int foreign_keys_count(T& storageImpl) { auto res = 0; - storageImpl.for_each([&res](auto &impl) { + storageImpl.for_each([&res](auto& impl) { res += impl.foreign_keys_count(); }); return res; } + + const bool inMemory; + bool isOpenedForever = false; + std::unique_ptr connection; + std::map collatingFunctions; + const int cachedForeignKeysCount; + std::function _busy_handler; + std::vector> scalarFunctions; + std::vector> aggregateFunctions; }; } } @@ -9187,6 +12652,16 @@ namespace sqlite_orm { using type = typename std::decay::type; }; + template + struct expression_object_type> { + using type = typename replace_range_t::object_type; + }; + + template + struct expression_object_type, L, O>> { + using type = typename replace_range_t, L, O>::object_type; + }; + template struct expression_object_type> { using type = typename std::decay::type; @@ -9197,6 +12672,17 @@ namespace sqlite_orm { using type = typename std::decay::type; }; + template + struct expression_object_type> { + using transformer_type = L; + using type = typename insert_range_t::object_type; + }; + + template + struct expression_object_type, L, O>> { + using type = typename insert_range_t, L, O>::object_type; + }; + template struct expression_object_type> { using type = typename std::decay::type; @@ -9211,7 +12697,7 @@ namespace sqlite_orm { struct get_ref_t { template - auto &operator()(O &t) const { + auto& operator()(O& t) const { return t; } }; @@ -9220,13 +12706,13 @@ namespace sqlite_orm { struct get_ref_t> { template - auto &operator()(O &t) const { + auto& operator()(O& t) const { return t.get(); } }; template - auto &get_ref(T &t) { + auto& get_ref(T& t) { using arg_type = typename std::decay::type; get_ref_t g; return g(t); @@ -9239,7 +12725,7 @@ namespace sqlite_orm { struct get_object_t : get_object_t {}; template - auto &get_object(T &t) { + auto& get_object(T& t) { using expression_type = typename std::decay::type; get_object_t obj; return obj(t); @@ -9250,7 +12736,7 @@ namespace sqlite_orm { using expression_type = replace_t; template - auto &operator()(O &e) const { + auto& operator()(O& e) const { return get_ref(e.obj); } }; @@ -9260,7 +12746,7 @@ namespace sqlite_orm { using expression_type = insert_t; template - auto &operator()(O &e) const { + auto& operator()(O& e) const { return get_ref(e.obj); } }; @@ -9270,7 +12756,7 @@ namespace sqlite_orm { using expression_type = update_t; template - auto &operator()(O &e) const { + auto& operator()(O& e) const { return get_ref(e.obj); } }; @@ -9281,9 +12767,12 @@ namespace sqlite_orm { #include // std::stringstream #include // std::string -#include // std::enable_if +#include // std::enable_if, std::remove_pointer #include // std::vector #include // std::iter_swap +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED // #include "core_functions.h" @@ -9321,8 +12810,12 @@ namespace sqlite_orm { find_table_name_t find_table_name; mutable table_name_set table_names; + table_name_collector() = default; + + table_name_collector(find_table_name_t _find_table_name) : find_table_name(std::move(_find_table_name)) {} + template - table_name_set operator()(const T &) const { + table_name_set operator()(const T&) const { return {}; } @@ -9334,19 +12827,19 @@ namespace sqlite_orm { } template - void operator()(const column_pointer &) const { + void operator()(const column_pointer&) const { if(this->find_table_name) { table_names.insert({this->find_table_name(typeid(T)), ""}); } } template - void operator()(const alias_column_t &a) const { + void operator()(const alias_column_t& a) const { (*this)(a.column, alias_extractor::get()); } template - void operator()(const count_asterisk_t &) const { + void operator()(const count_asterisk_t&) const { if(this->find_table_name) { auto tableName = this->find_table_name(typeid(T)); if(!tableName.empty()) { @@ -9356,7 +12849,31 @@ namespace sqlite_orm { } template - void operator()(const asterisk_t &) const { + void operator()(const asterisk_t&) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + table_names.insert(std::make_pair(move(tableName), "")); + } + } + + template + void operator()(const object_t&) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + table_names.insert(std::make_pair(move(tableName), "")); + } + } + + template + void operator()(const table_rowid_t&) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + table_names.insert(std::make_pair(move(tableName), "")); + } + } + + template + void operator()(const table_oid_t&) const { if(this->find_table_name) { auto tableName = this->find_table_name(typeid(T)); table_names.insert(std::make_pair(move(tableName), "")); @@ -9364,7 +12881,7 @@ namespace sqlite_orm { } template - void operator()(const object_t &) const { + void operator()(const table__rowid_t&) const { if(this->find_table_name) { auto tableName = this->find_table_name(typeid(T)); table_names.insert(std::make_pair(move(tableName), "")); @@ -9391,14 +12908,14 @@ namespace sqlite_orm { namespace internal { template - std::string serialize(const T &t, const C &context); + std::string serialize(const T& t, const C& context); template struct column_names_getter { using expression_type = T; template - std::vector operator()(const expression_type &t, const C &context) { + std::vector operator()(const expression_type& t, const C& context) { auto newContext = context; newContext.skip_table_name = false; auto columnName = serialize(t, newContext); @@ -9411,7 +12928,7 @@ namespace sqlite_orm { }; template - std::vector get_column_names(const T &t, const C &context) { + std::vector get_column_names(const T& t, const C& context) { column_names_getter serializator; return serializator(t, context); } @@ -9421,7 +12938,7 @@ namespace sqlite_orm { using expression_type = std::reference_wrapper; template - std::vector operator()(const expression_type &expression, const C &context) { + std::vector operator()(const expression_type& expression, const C& context) { return get_column_names(expression.get(), context); } }; @@ -9431,7 +12948,7 @@ namespace sqlite_orm { using expression_type = asterisk_t; template - std::vector operator()(const expression_type &, const C &) { + std::vector operator()(const expression_type&, const C&) { std::vector res; res.push_back("*"); return res; @@ -9443,7 +12960,7 @@ namespace sqlite_orm { using expression_type = object_t; template - std::vector operator()(const expression_type &, const C &) { + std::vector operator()(const expression_type&, const C&) { std::vector res; res.push_back("*"); return res; @@ -9455,12 +12972,12 @@ namespace sqlite_orm { using expression_type = columns_t; template - std::vector operator()(const expression_type &cols, const C &context) { + std::vector operator()(const expression_type& cols, const C& context) { std::vector columnNames; columnNames.reserve(static_cast(cols.count)); auto newContext = context; newContext.skip_table_name = false; - iterate_tuple(cols.columns, [&columnNames, &newContext](auto &m) { + iterate_tuple(cols.columns, [&columnNames, &newContext](auto& m) { auto columnName = serialize(m, newContext); if(columnName.length()) { columnNames.push_back(columnName); @@ -9489,7 +13006,7 @@ namespace sqlite_orm { struct order_by_serializator; template - std::string serialize_order_by(const T &t, const C &context) { + std::string serialize_order_by(const T& t, const C& context) { order_by_serializator serializator; return serializator(t, context); } @@ -9499,11 +13016,11 @@ namespace sqlite_orm { using statement_type = order_by_t; template - std::string operator()(const statement_type &orderBy, const C &context) const { + std::string operator()(const statement_type& orderBy, const C& context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; - auto columnName = serialize(orderBy.o, newContext); + auto columnName = serialize(orderBy.expression, newContext); ss << columnName << " "; if(orderBy._collate_argument.length()) { ss << "COLLATE " << orderBy._collate_argument << " "; @@ -9525,9 +13042,9 @@ namespace sqlite_orm { using statement_type = dynamic_order_by_t; template - std::string operator()(const statement_type &orderBy, const C &) const { + std::string operator()(const statement_type& orderBy, const C&) const { std::vector expressions; - for(auto &entry: orderBy) { + for(auto& entry: orderBy) { std::string entryString; { std::stringstream ss; @@ -9569,6 +13086,12 @@ namespace sqlite_orm { // #include "indexed_column.h" +// #include "function.h" + +// #include "ast/upsert_clause.h" + +// #include "ast/excluded.h" + namespace sqlite_orm { namespace internal { @@ -9577,7 +13100,7 @@ namespace sqlite_orm { struct statement_serializator; template - std::string serialize(const T &t, const C &context) { + std::string serialize(const T& t, const C& context) { statement_serializator serializator; return serializator(t, context); } @@ -9587,7 +13110,7 @@ namespace sqlite_orm { using statement_type = T; template - std::string operator()(const statement_type &statement, const C &context) { + std::string operator()(const statement_type& statement, const C& context) { if(context.replace_bindable_with_question) { return "?"; } else { @@ -9596,12 +13119,39 @@ namespace sqlite_orm { } }; + template + struct statement_serializator, void> { + using statement_type = excluded_t; + + template + std::string operator()(const statement_type& statement, const C& context) { + std::stringstream ss; + ss << "excluded."; + if(auto columnNamePointer = context.impl.column_name(statement.expression)) { + ss << "\"" << *columnNamePointer << "\""; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } + return ss.str(); + } + }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializator, void> { + using statement_type = as_optional_t; + + template + std::string operator()(const statement_type& statement, const C& context) { + return serialize(statement.value, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct statement_serializator, void> { using statement_type = std::reference_wrapper; template - std::string operator()(const statement_type &s, const C &context) { + std::string operator()(const statement_type& s, const C& context) { return serialize(s.get(), context); } }; @@ -9611,33 +13161,78 @@ namespace sqlite_orm { using statement_type = std::nullptr_t; template - std::string operator()(const statement_type &, const C &) { + std::string operator()(const statement_type&, const C&) { return "?"; } }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<> + struct statement_serializator { + using statement_type = std::nullopt_t; + template + std::string operator()(const statement_type&, const C&) { + return "?"; + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct statement_serializator, void> { using statement_type = alias_holder; template - std::string operator()(const statement_type &, const C &) { + std::string operator()(const statement_type&, const C&) { return T::get(); } }; + template + struct statement_serializator, std::tuple>, void> { + using statement_type = upsert_clause, std::tuple>; + + template + std::string operator()(const statement_type& statement, const C& context) const { + std::stringstream ss; + ss << "ON CONFLICT"; + iterate_tuple(statement.target_args, [&ss, &context](auto& value) { + using value_type = typename std::decay::type; + auto needParenthesis = std::is_member_pointer::value; + ss << ' '; + if(needParenthesis) { + ss << '('; + } + ss << serialize(value, context); + if(needParenthesis) { + ss << ')'; + } + }); + ss << ' ' << "DO"; + if(std::tuple_size::value == 0) { + ss << " NOTHING"; + } else { + ss << " UPDATE"; + auto updateContext = context; + updateContext.use_parentheses = false; + iterate_tuple(statement.actions, [&ss, &updateContext](auto& value) { + ss << ' ' << serialize(value, updateContext); + }); + } + return ss.str(); + } + }; + template - struct statement_serializator, void> { - using statement_type = core_function_t; + struct statement_serializator, void> { + using statement_type = built_in_function_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; - ss << static_cast(c) << "("; + ss << statement.serialize() << "("; std::vector args; - using args_type = typename std::decay::type::args_type; + using args_type = typename std::decay::type::args_type; args.reserve(std::tuple_size::value); - iterate_tuple(c.args, [&args, &context](auto &v) { + iterate_tuple(statement.args, [&args, &context](auto& v) { args.push_back(serialize(v, context)); }); for(size_t i = 0; i < args.size(); ++i) { @@ -9651,12 +13246,36 @@ namespace sqlite_orm { } }; + template + struct statement_serializator, void> { + using statement_type = function_call; + + template + std::string operator()(const statement_type& statement, const C& context) const { + using args_tuple = std::tuple; + + std::stringstream ss; + ss << F::name() << "("; + auto index = 0; + iterate_tuple(statement.args, [&context, &ss, &index](auto& v) { + auto value = serialize(v, context); + ss << value; + if(index < std::tuple_size::value - 1) { + ss << ", "; + } + ++index; + }); + ss << ")"; + return ss.str(); + } + }; + template struct statement_serializator, void> { using statement_type = as_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto tableAliasString = alias_extractor::get(); return serialize(c.expression, context) + " AS " + tableAliasString; } @@ -9667,7 +13286,7 @@ namespace sqlite_orm { using statement_type = alias_column_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << T::get() << "'."; @@ -9684,21 +13303,21 @@ namespace sqlite_orm { using statement_type = std::string; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { if(context.replace_bindable_with_question) { return "?"; } else { - return "\"" + c + "\""; + return "\'" + c + "\'"; } } }; template<> - struct statement_serializator { - using statement_type = const char *; + struct statement_serializator { + using statement_type = const char*; template - std::string operator()(const char *c, const C &context) const { + std::string operator()(const char* c, const C& context) const { if(context.replace_bindable_with_question) { return "?"; } else { @@ -9712,12 +13331,16 @@ namespace sqlite_orm { using statement_type = F O::*; template - std::string operator()(const statement_type &m, const C &context) const { + std::string operator()(const statement_type& m, const C& context) const { std::stringstream ss; if(!context.skip_table_name) { ss << "\"" << context.impl.find_table_name(typeid(O)) << "\"."; } - ss << "\"" << context.column_name(m) << "\""; + if(auto columnnamePointer = context.column_name(m)) { + ss << "\"" << *columnnamePointer << "\""; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } return ss.str(); } }; @@ -9727,7 +13350,7 @@ namespace sqlite_orm { using statement_type = rowid_t; template - std::string operator()(const statement_type &s, const C &) { + std::string operator()(const statement_type& s, const C&) { return static_cast(s); } }; @@ -9737,7 +13360,7 @@ namespace sqlite_orm { using statement_type = oid_t; template - std::string operator()(const statement_type &s, const C &) { + std::string operator()(const statement_type& s, const C&) { return static_cast(s); } }; @@ -9747,7 +13370,7 @@ namespace sqlite_orm { using statement_type = _rowid_t; template - std::string operator()(const statement_type &s, const C &) { + std::string operator()(const statement_type& s, const C&) { return static_cast(s); } }; @@ -9757,7 +13380,7 @@ namespace sqlite_orm { using statement_type = table_rowid_t; template - std::string operator()(const statement_type &s, const C &context) { + std::string operator()(const statement_type& s, const C& context) { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; @@ -9772,7 +13395,7 @@ namespace sqlite_orm { using statement_type = table_oid_t; template - std::string operator()(const statement_type &s, const C &context) { + std::string operator()(const statement_type& s, const C& context) { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; @@ -9787,7 +13410,7 @@ namespace sqlite_orm { using statement_type = table__rowid_t; template - std::string operator()(const statement_type &s, const C &context) { + std::string operator()(const statement_type& s, const C& context) { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; @@ -9802,11 +13425,17 @@ namespace sqlite_orm { using statement_type = binary_operator; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto lhs = serialize(c.lhs, context); auto rhs = serialize(c.rhs, context); std::stringstream ss; - ss << "(" << lhs << " " << static_cast(c) << " " << rhs << ")"; + if(context.use_parentheses) { + ss << '('; + } + ss << lhs << " " << static_cast(c) << " " << rhs; + if(context.use_parentheses) { + ss << ')'; + } return ss.str(); } }; @@ -9816,7 +13445,7 @@ namespace sqlite_orm { using statement_type = count_asterisk_t; template - std::string operator()(const statement_type &, const C &context) const { + std::string operator()(const statement_type&, const C& context) const { return serialize(count_asterisk_without_type{}, context); } }; @@ -9826,9 +13455,10 @@ namespace sqlite_orm { using statement_type = count_asterisk_without_type; template - std::string operator()(const statement_type &c, const C &) const { + std::string operator()(const statement_type& c, const C&) const { std::stringstream ss; - ss << static_cast(c) << "(*)"; + auto functionName = c.serialize(); + ss << functionName << "(*)"; return ss.str(); } }; @@ -9838,9 +13468,9 @@ namespace sqlite_orm { using statement_type = distinct_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; - auto expr = serialize(c.t, context); + auto expr = serialize(c.value, context); ss << static_cast(c) << "(" << expr << ")"; return ss.str(); } @@ -9851,9 +13481,9 @@ namespace sqlite_orm { using statement_type = all_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; - auto expr = serialize(c.t, context); + auto expr = serialize(c.value, context); ss << static_cast(c) << "(" << expr << ")"; return ss.str(); } @@ -9864,12 +13494,16 @@ namespace sqlite_orm { using statement_type = column_pointer; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; if(!context.skip_table_name) { ss << "'" << context.impl.find_table_name(typeid(T)) << "'."; } - ss << "\"" << context.impl.column_name_simple(c.field) << "\""; + if(auto columnNamePointer = context.impl.column_name_simple(c.field)) { + ss << "\"" << *columnNamePointer << "\""; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } return ss.str(); } }; @@ -9879,7 +13513,7 @@ namespace sqlite_orm { using statement_type = cast_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast(c) << " ("; ss << serialize(c.expression, context) << " AS " << type_printer().print() << ")"; @@ -9893,7 +13527,7 @@ namespace sqlite_orm { using statement_type = T; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << serialize(c.left, context) << " "; ss << static_cast(c) << " "; @@ -9907,17 +13541,17 @@ namespace sqlite_orm { using statement_type = simple_case_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << "CASE "; - c.case_expression.apply([&ss, context](auto &c_) { + c.case_expression.apply([&ss, context](auto& c_) { ss << serialize(c_, context) << " "; }); - iterate_tuple(c.args, [&ss, context](auto &pair) { + iterate_tuple(c.args, [&ss, context](auto& pair) { ss << "WHEN " << serialize(pair.first, context) << " "; ss << "THEN " << serialize(pair.second, context) << " "; }); - c.else_expression.apply([&ss, context](auto &el) { + c.else_expression.apply([&ss, context](auto& el) { ss << "ELSE " << serialize(el, context) << " "; }); ss << "END"; @@ -9930,7 +13564,7 @@ namespace sqlite_orm { using statement_type = is_null_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << serialize(c.t, context) << " " << static_cast(c); return ss.str(); @@ -9942,7 +13576,7 @@ namespace sqlite_orm { using statement_type = is_not_null_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << serialize(c.t, context) << " " << static_cast(c); return ss.str(); @@ -9954,7 +13588,7 @@ namespace sqlite_orm { using statement_type = bitwise_not_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast(c) << " "; auto cString = serialize(c.argument, context); @@ -9968,7 +13602,7 @@ namespace sqlite_orm { using statement_type = negated_condition_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast(c) << " "; auto cString = serialize(c.c, context); @@ -9983,7 +13617,7 @@ namespace sqlite_orm { using statement_type = T; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto leftString = serialize(c.l, context); auto rightString = serialize(c.r, context); std::stringstream ss; @@ -10003,7 +13637,7 @@ namespace sqlite_orm { using statement_type = named_collate; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto newContext = context; newContext.use_parentheses = false; auto res = serialize(c.expr, newContext); @@ -10016,7 +13650,7 @@ namespace sqlite_orm { using statement_type = collate_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto newContext = context; newContext.use_parentheses = false; auto res = serialize(c.expr, newContext); @@ -10025,44 +13659,64 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { - using statement_type = in_t; + struct statement_serializator, void> { + using statement_type = dynamic_in_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; - auto leftString = serialize(c.l, context); + auto leftString = serialize(c.left, context); ss << leftString << " " << static_cast(c) << " "; auto newContext = context; newContext.use_parentheses = true; - ss << serialize(c.arg, newContext); + ss << serialize(c.argument, newContext); return ss.str(); } }; template - struct statement_serializator>, void> { - using statement_type = in_t>; + struct statement_serializator>, void> { + using statement_type = dynamic_in_t>; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; - auto leftString = serialize(c.l, context); - if(context.use_parentheses) { - ss << '('; - } - ss << leftString << " " << static_cast(c) << " ( "; - for(size_t index = 0; index < c.arg.size(); ++index) { - auto &value = c.arg[index]; - ss << " " << serialize(value, context); - if(index < c.arg.size() - 1) { + auto leftString = serialize(c.left, context); + ss << leftString << " " << static_cast(c) << " ("; + for(size_t index = 0; index < c.argument.size(); ++index) { + auto& value = c.argument[index]; + ss << serialize(value, context); + if(index < c.argument.size() - 1) { ss << ", "; } } - ss << " )"; - if(context.use_parentheses) { - ss << ')'; + ss << ")"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = in_t; + + template + std::string operator()(const statement_type& c, const C& context) const { + std::stringstream ss; + auto leftString = serialize(c.left, context); + ss << leftString << " " << static_cast(c) << " ("; + std::vector args; + using args_type = std::tuple; + args.reserve(std::tuple_size::value); + iterate_tuple(c.argument, [&args, &context](auto& v) { + args.push_back(serialize(v, context)); + }); + for(size_t i = 0; i < args.size(); ++i) { + ss << args[i]; + if(i < args.size() - 1) { + ss << ", "; + } } + ss << ")"; return ss.str(); } }; @@ -10072,12 +13726,12 @@ namespace sqlite_orm { using statement_type = like_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << serialize(c.arg, context) << " "; ss << static_cast(c) << " "; ss << serialize(c.pattern, context); - c.arg3.apply([&ss, &context](auto &value) { + c.arg3.apply([&ss, &context](auto& value) { ss << " ESCAPE " << serialize(value, context); }); return ss.str(); @@ -10089,7 +13743,7 @@ namespace sqlite_orm { using statement_type = glob_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << serialize(c.arg, context) << " "; ss << static_cast(c) << " "; @@ -10103,7 +13757,7 @@ namespace sqlite_orm { using statement_type = between_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; auto expr = serialize(c.expr, context); ss << expr << " " << static_cast(c) << " "; @@ -10119,7 +13773,7 @@ namespace sqlite_orm { using statement_type = exists_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast(c) << " "; ss << serialize(c.t, context); @@ -10128,33 +13782,37 @@ namespace sqlite_orm { }; template<> - struct statement_serializator { - using statement_type = constraints::autoincrement_t; + struct statement_serializator { + using statement_type = autoincrement_t; template - std::string operator()(const statement_type &c, const C &) const { + std::string operator()(const statement_type& c, const C&) const { return static_cast(c); } }; template - struct statement_serializator, void> { - using statement_type = constraints::primary_key_t; + struct statement_serializator, void> { + using statement_type = primary_key_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto res = static_cast(c); using columns_tuple = typename statement_type::columns_tuple; auto columnsCount = std::tuple_size::value; if(columnsCount) { res += "("; decltype(columnsCount) columnIndex = 0; - iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto &column) { - res += context.column_name(column); - if(columnIndex < columnsCount - 1) { - res += ", "; + iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto& column) { + if(auto columnNamePointer = context.column_name(column)) { + res += *columnNamePointer; + if(columnIndex < columnsCount - 1) { + res += ", "; + } + ++columnIndex; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); } - ++columnIndex; }); res += ")"; } @@ -10163,23 +13821,27 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { - using statement_type = constraints::unique_t; + struct statement_serializator, void> { + using statement_type = unique_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { auto res = static_cast(c); using columns_tuple = typename statement_type::columns_tuple; auto columnsCount = std::tuple_size::value; if(columnsCount) { res += "("; decltype(columnsCount) columnIndex = 0; - iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto &column) { - res += context.column_name(column); - if(columnIndex < columnsCount - 1) { - res += ", "; + iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto& column) { + if(auto columnNamePointer = context.column_name(column)) { + res += *columnNamePointer; + if(columnIndex < columnsCount - 1) { + res += ", "; + } + ++columnIndex; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); } - ++columnIndex; }); res += ")"; } @@ -10188,38 +13850,42 @@ namespace sqlite_orm { }; template<> - struct statement_serializator { - using statement_type = constraints::collate_t; + struct statement_serializator { + using statement_type = collate_constraint_t; template - std::string operator()(const statement_type &c, const C &) const { + std::string operator()(const statement_type& c, const C&) const { return static_cast(c); } }; template - struct statement_serializator, void> { - using statement_type = constraints::default_t; + struct statement_serializator, void> { + using statement_type = default_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { return static_cast(c) + " (" + serialize(c.value, context) + ")"; } }; template - struct statement_serializator, std::tuple>, void> { - using statement_type = constraints::foreign_key_t, std::tuple>; + struct statement_serializator, std::tuple>, void> { + using statement_type = foreign_key_t, std::tuple>; template - std::string operator()(const statement_type &fk, const C &context) const { + std::string operator()(const statement_type& fk, const C& context) const { std::stringstream ss; std::vector columnNames; using columns_type_t = typename std::decay::type::columns_type; constexpr const size_t columnsCount = std::tuple_size::value; columnNames.reserve(columnsCount); - iterate_tuple(fk.columns, [&columnNames, &context](auto &v) { - columnNames.push_back(context.impl.column_name(v)); + iterate_tuple(fk.columns, [&columnNames, &context](auto& v) { + if(auto columnNamePointer = context.impl.column_name(v)) { + columnNames.push_back(*columnNamePointer); + } else { + columnNames.push_back({}); + } }); ss << "FOREIGN KEY("; for(size_t i = 0; i < columnNames.size(); ++i) { @@ -10239,8 +13905,12 @@ namespace sqlite_orm { auto refTableName = context.impl.find_table_name(typeid(first_reference_mapped_type)); ss << '\'' << refTableName << '\''; } - iterate_tuple(fk.references, [&referencesNames, &context](auto &v) { - referencesNames.push_back(context.impl.column_name(v)); + iterate_tuple(fk.references, [&referencesNames, &context](auto& v) { + if(auto columnNamePointer = context.impl.column_name(v)) { + referencesNames.push_back(*columnNamePointer); + } else { + referencesNames.push_back({}); + } }); ss << "("; for(size_t i = 0; i < referencesNames.size(); ++i) { @@ -10261,11 +13931,11 @@ namespace sqlite_orm { }; template - struct statement_serializator, void> { - using statement_type = constraints::check_t; + struct statement_serializator, void> { + using statement_type = check_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { return static_cast(c) + " " + serialize(c.expression, context); } }; @@ -10275,7 +13945,7 @@ namespace sqlite_orm { using statement_type = column_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << "'" << c.name << "' "; using column_type = typename std::decay::type; @@ -10291,12 +13961,12 @@ namespace sqlite_orm { int tupleIndex = 0; iterate_tuple( c.constraints, - [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context](auto &v) { + [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context](auto& v) { using constraint_type = typename std::decay::type; constraintsStrings.push_back(serialize(v, context)); if(is_primary_key::value) { primaryKeyIndex = tupleIndex; - } else if(std::is_same::value) { + } else if(std::is_same::value) { autoincrementIndex = tupleIndex; } ++tupleIndex; @@ -10305,7 +13975,7 @@ namespace sqlite_orm { iter_swap(constraintsStrings.begin() + primaryKeyIndex, constraintsStrings.begin() + autoincrementIndex); } - for(auto &str: constraintsStrings) { + for(auto& str: constraintsStrings) { ss << str << ' '; } } @@ -10321,11 +13991,11 @@ namespace sqlite_orm { using statement_type = remove_all_t; template - std::string operator()(const statement_type &rem, const C &context) const { - auto &tImpl = context.impl.template get_impl(); + std::string operator()(const statement_type& rem, const C& context) const { + auto& tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "DELETE FROM '" << tImpl.table.name << "' "; - iterate_tuple(rem.conditions, [&context, &ss](auto &v) { + iterate_tuple(rem.conditions, [&context, &ss](auto& v) { ss << serialize(v, context); }); return ss.str(); @@ -10337,33 +14007,8 @@ namespace sqlite_orm { using statement_type = replace_t; template - std::string operator()(const statement_type &rep, const C &context) const { - using expression_type = typename std::decay::type; - using object_type = typename expression_object_type::type; - auto &tImpl = context.impl.template get_impl(); - std::stringstream ss; - ss << "REPLACE INTO '" << tImpl.table.name << "' ("; - auto columnNames = tImpl.table.column_names(); - auto columnNamesCount = columnNames.size(); - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - ss << "VALUES("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "?"; - if(i < columnNamesCount - 1) { - ss << ", "; - } else { - ss << ")"; - } - } - return ss.str(); + std::string operator()(const statement_type& rep, const C& context) const { + return serialize_replace_range_impl(rep, context, 1); } }; @@ -10372,12 +14017,12 @@ namespace sqlite_orm { using statement_type = insert_explicit; template - std::string operator()(const statement_type &ins, const C &context) const { + std::string operator()(const statement_type& ins, const C& context) const { constexpr const size_t colsCount = std::tuple_size>::value; static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); using expression_type = typename std::decay::type; using object_type = typename expression_object_type::type; - auto &tImpl = context.impl.template get_impl(); + auto& tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "INSERT INTO '" << tImpl.table.name << "' "; std::vector columnNames; @@ -10385,7 +14030,7 @@ namespace sqlite_orm { { auto columnsContext = context; columnsContext.skip_table_name = true; - iterate_tuple(ins.columns.columns, [&columnNames, &columnsContext](auto &m) { + iterate_tuple(ins.columns.columns, [&columnNames, &columnsContext](auto& m) { auto columnName = serialize(m, columnsContext); if(!columnName.empty()) { columnNames.push_back(columnName); @@ -10423,16 +14068,16 @@ namespace sqlite_orm { using statement_type = update_t; template - std::string operator()(const statement_type &upd, const C &context) const { + std::string operator()(const statement_type& upd, const C& context) const { using expression_type = typename std::decay::type; using object_type = typename expression_object_type::type; - auto &tImpl = context.impl.template get_impl(); + auto& tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "UPDATE '" << tImpl.table.name << "' SET "; std::vector setColumnNames; - tImpl.table.for_each_column([&setColumnNames](auto &c) { - if(!c.template has>()) { + tImpl.table.for_each_column([&setColumnNames](auto& c) { + if(!c.template has>()) { setColumnNames.emplace_back(c.name); } }); @@ -10458,17 +14103,43 @@ namespace sqlite_orm { } }; + template + struct statement_serializator, void> { + using statement_type = set_t; + + template + std::string operator()(const statement_type& statement, const C& context) const { + std::stringstream ss; + ss << static_cast(statement); + auto assignsCount = std::tuple_size::value; + decltype(assignsCount) assignIndex = 0; + auto leftContext = context; + leftContext.skip_table_name = true; + iterate_tuple(statement.assigns, + [&ss, &context, &leftContext, &assignIndex, assignsCount](auto& value) { + ss << ' ' << serialize(value.lhs, leftContext); + ss << ' ' << static_cast(value) << ' '; + ss << serialize(value.rhs, context); + if(assignIndex < assignsCount - 1) { + ss << ","; + } + ++assignIndex; + }); + return ss.str(); + } + }; + template struct statement_serializator, Wargs...>, void> { using statement_type = update_all_t, Wargs...>; template - std::string operator()(const statement_type &upd, const C &context) const { + std::string operator()(const statement_type& upd, const C& context) const { std::stringstream ss; ss << "UPDATE "; - table_name_collector collector{[&context](std::type_index ti) { + table_name_collector collector([&context](std::type_index ti) { return context.impl.find_table_name(ti); - }}; + }); iterate_ast(upd.set.assigns, collector); if(!collector.table_names.empty()) { if(collector.table_names.size() == 1) { @@ -10477,7 +14148,7 @@ namespace sqlite_orm { std::vector setPairs; auto leftContext = context; leftContext.skip_table_name = true; - iterate_tuple(upd.set.assigns, [&context, &leftContext, &setPairs](auto &asgn) { + iterate_tuple(upd.set.assigns, [&context, &leftContext, &setPairs](auto& asgn) { std::stringstream sss; sss << serialize(asgn.lhs, leftContext); sss << " " << static_cast(asgn) << " "; @@ -10491,7 +14162,7 @@ namespace sqlite_orm { ss << ", "; } } - iterate_tuple(upd.conditions, [&context, &ss](auto &v) { + iterate_tuple(upd.conditions, [&context, &ss](auto& v) { ss << serialize(v, context); }); return ss.str(); @@ -10504,55 +14175,148 @@ namespace sqlite_orm { } }; + template + std::string serialize_insert_range_impl(const T& /*statement*/, const C& context, const int valuesCount) { + using object_type = typename expression_object_type::type; + auto& tImpl = context.impl.template get_impl(); + + std::stringstream ss; + ss << "INSERT INTO '" << tImpl.table.name << "' "; + std::vector columnNames; + auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); + + tImpl.table.for_each_column([&columnNames, &compositeKeyColumnNames](auto& c) { + using table_type = typename std::decay::type; + if(table_type::is_without_rowid || !c.template has>()) { + auto it = find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); + if(it == compositeKeyColumnNames.end()) { + columnNames.emplace_back(c.name); + } + } + }); + + const auto columnNamesCount = columnNames.size(); + if(columnNamesCount) { + ss << "("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNamesCount - 1) { + ss << ","; + } else { + ss << ")"; + } + ss << " "; + } + } else { + ss << "DEFAULT "; + } + ss << "VALUES "; + if(columnNamesCount) { + auto valuesString = [columnNamesCount] { + std::stringstream ss_; + ss_ << "("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss_ << "?"; + if(i < columnNamesCount - 1) { + ss_ << ", "; + } else { + ss_ << ")"; + } + } + return ss_.str(); + }(); + for(auto i = 0; i < valuesCount; ++i) { + ss << valuesString; + if(i < valuesCount - 1) { + ss << ","; + } + ss << " "; + } + } else if(valuesCount != 1) { + throw std::system_error(std::make_error_code(orm_error_code::cannot_use_default_value)); + } + + return ss.str(); + } + template struct statement_serializator, void> { using statement_type = insert_t; template - std::string operator()(const statement_type &, const C &context) const { - using object_type = typename expression_object_type::type; - auto &tImpl = context.impl.template get_impl(); + std::string operator()(const statement_type& statement, const C& context) const { + return serialize_insert_range_impl(statement, context, 1); + } + }; + + template + struct statement_serializator, void> { + using statement_type = into_t; + + template + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; - ss << "INSERT INTO '" << tImpl.table.name << "' "; - std::vector columnNames; - auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); + auto& tImpl = context.impl.template get_impl(); + ss << "INTO " << tImpl.table.name; + return ss.str(); + } + }; - tImpl.table.for_each_column([&tImpl, &columnNames, &compositeKeyColumnNames](auto &c) { - if(tImpl.table._without_rowid || !c.template has>()) { - auto it = find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); - if(it == compositeKeyColumnNames.end()) { - columnNames.emplace_back(c.name); - } + template + struct statement_serializator, void> { + using statement_type = columns_t; + + template + std::string operator()(const statement_type& statement, const C& context) const { + std::stringstream ss; + auto index = 0; + if(context.use_parentheses) { + ss << '('; + } + iterate_tuple(statement.columns, [&context, &ss, &index](auto& value) { + ss << serialize(value, context); + if(index < int(std::tuple_size>::value) - 1) { + ss << ", "; } + ++index; }); + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; - auto columnNamesCount = columnNames.size(); - if(columnNamesCount) { - ss << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } + template + struct statement_serializator< + T, + typename std::enable_if::value || is_replace_raw::value>::type> { + using statement_type = T; + + template + std::string operator()(const statement_type& statement, const C& context) const { + std::stringstream ss; + if(is_insert_raw::value) { + ss << "INSERT"; } else { - ss << "DEFAULT "; + ss << "REPLACE"; } - ss << "VALUES "; - if(columnNamesCount) { - ss << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "?"; - if(i < columnNamesCount - 1) { - ss << ", "; - } else { - ss << ")"; - } + iterate_tuple(statement.args, [&context, &ss](auto& value) { + using value_type = typename std::decay::type; + ss << ' '; + if(is_columns::value) { + auto newContext = context; + newContext.skip_table_name = true; + newContext.use_parentheses = true; + ss << serialize(value, newContext); + } else if(is_values::value || is_select::value) { + auto newContext = context; + newContext.use_parentheses = false; + ss << serialize(value, newContext); + } else { + ss << serialize(value, context); } - } + }); return ss.str(); } }; @@ -10562,8 +14326,8 @@ namespace sqlite_orm { using statement_type = remove_t; template - std::string operator()(const statement_type &, const C &context) const { - auto &tImpl = context.impl.template get_impl(); + std::string operator()(const statement_type&, const C& context) const { + auto& tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "DELETE FROM '" << tImpl.table.name << "' "; ss << "WHERE "; @@ -10579,110 +14343,71 @@ namespace sqlite_orm { } }; - template - struct statement_serializator, void> { - using statement_type = replace_range_t; - - template - std::string operator()(const statement_type &rep, const C &context) const { - using expression_type = typename std::decay::type; - using object_type = typename expression_type::object_type; - auto &tImpl = context.impl.template get_impl(); - std::stringstream ss; - ss << "REPLACE INTO '" << tImpl.table.name << "' ("; - auto columnNames = tImpl.table.column_names(); - auto columnNamesCount = columnNames.size(); + template + std::string serialize_replace_range_impl(const T& rep, const C& context, const int valuesCount) { + using expression_type = typename std::decay::type; + using object_type = typename expression_object_type::type; + auto& tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "REPLACE INTO '" << tImpl.table.name << "' ("; + auto columnNames = tImpl.table.column_names(); + auto columnNamesCount = columnNames.size(); + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNamesCount - 1) { + ss << ", "; + } else { + ss << ") "; + } + } + ss << "VALUES "; + auto valuesString = [columnNamesCount] { + std::stringstream ss_; + ss_ << "("; for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; + ss_ << "?"; if(i < columnNamesCount - 1) { - ss << ", "; + ss_ << ", "; } else { - ss << ") "; + ss_ << ")"; } } - ss << "VALUES "; - auto valuesString = [columnNamesCount] { - std::stringstream ss_; - ss_ << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss_ << "?"; - if(i < columnNamesCount - 1) { - ss_ << ", "; - } else { - ss_ << ")"; - } - } - return ss_.str(); - }(); - auto valuesCount = static_cast(std::distance(rep.range.first, rep.range.second)); - for(auto i = 0; i < valuesCount; ++i) { - ss << valuesString; - if(i < valuesCount - 1) { - ss << ","; - } - ss << " "; + return ss_.str(); + }(); + for(auto i = 0; i < valuesCount; ++i) { + ss << valuesString; + if(i < valuesCount - 1) { + ss << ","; } - return ss.str(); + ss << " "; } - }; + return ss.str(); + } - template - struct statement_serializator, void> { - using statement_type = insert_range_t; + template + struct statement_serializator, void> { + using statement_type = replace_range_t; template - std::string operator()(const statement_type &statement, const C &context) const { - using expression_type = typename std::decay::type; - using object_type = typename expression_type::object_type; - auto &tImpl = context.impl.template get_impl(); - - std::stringstream ss; - ss << "INSERT INTO '" << tImpl.table.name << "' ("; - std::vector columnNames; - tImpl.table.for_each_column([&columnNames](auto &c) { - if(!c.template has>()) { - columnNames.emplace_back(c.name); - } - }); + std::string operator()(const statement_type& rep, const C& context) const { + auto valuesCount = static_cast(std::distance(rep.range.first, rep.range.second)); + return serialize_replace_range_impl(rep, context, valuesCount); + } + }; - auto columnNamesCount = columnNames.size(); - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - ss << "VALUES "; - auto valuesString = [columnNamesCount] { - std::stringstream ss_; - ss_ << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss_ << "?"; - if(i < columnNamesCount - 1) { - ss_ << ", "; - } else { - ss_ << ")"; - } - } - return ss_.str(); - }(); - auto valuesCount = static_cast(std::distance(statement.range.first, statement.range.second)); - for(auto i = 0; i < valuesCount; ++i) { - ss << valuesString; - if(i < valuesCount - 1) { - ss << ","; - } - ss << " "; - } - return ss.str(); + template + struct statement_serializator, void> { + using statement_type = insert_range_t; + + template + std::string operator()(const statement_type& statement, const C& context) const { + const auto valuesCount = static_cast(std::distance(statement.range.first, statement.range.second)); + return serialize_insert_range_impl(statement, context, valuesCount); } }; template - std::string serialize_get_all_impl(const T &get, const C &context) { + std::string serialize_get_all_impl(const T& get, const C& context) { using primary_type = typename T::type; table_name_collector collector; @@ -10691,7 +14416,7 @@ namespace sqlite_orm { iterate_ast(get.conditions, collector); std::stringstream ss; ss << "SELECT "; - auto &tImpl = context.impl.template get_impl(); + auto& tImpl = context.impl.template get_impl(); auto columnNames = tImpl.table.column_names(); for(size_t i = 0; i < columnNames.size(); ++i) { ss << "\"" << tImpl.table.name << "\"." @@ -10706,7 +14431,7 @@ namespace sqlite_orm { std::vector> tableNames(collector.table_names.begin(), collector.table_names.end()); for(size_t i = 0; i < tableNames.size(); ++i) { - auto &tableNamePair = tableNames[i]; + auto& tableNamePair = tableNames[i]; ss << "'" << tableNamePair.first << "' "; if(!tableNamePair.second.empty()) { ss << tableNamePair.second << " "; @@ -10716,7 +14441,7 @@ namespace sqlite_orm { } ss << " "; } - iterate_tuple(get.conditions, [&context, &ss](auto &v) { + iterate_tuple(get.conditions, [&context, &ss](auto& v) { ss << serialize(v, context); }); return ss.str(); @@ -10728,7 +14453,7 @@ namespace sqlite_orm { using statement_type = get_all_optional_t; template - std::string operator()(const statement_type &get, const C &context) const { + std::string operator()(const statement_type& get, const C& context) const { return serialize_get_all_impl(get, context); } }; @@ -10739,7 +14464,7 @@ namespace sqlite_orm { using statement_type = get_all_pointer_t; template - std::string operator()(const statement_type &get, const C &context) const { + std::string operator()(const statement_type& get, const C& context) const { return serialize_get_all_impl(get, context); } }; @@ -10749,15 +14474,15 @@ namespace sqlite_orm { using statement_type = get_all_t; template - std::string operator()(const statement_type &get, const C &context) const { + std::string operator()(const statement_type& get, const C& context) const { return serialize_get_all_impl(get, context); } }; template - std::string serialize_get_impl(const T &, const C &context) { + std::string serialize_get_impl(const T&, const C& context) { using primary_type = typename T::type; - auto &tImpl = context.impl.template get_impl(); + auto& tImpl = context.impl.template get_impl(); std::stringstream ss; ss << "SELECT "; auto columnNames = tImpl.table.column_names(); @@ -10790,7 +14515,7 @@ namespace sqlite_orm { using statement_type = get_t; template - std::string operator()(const statement_type &get, const C &context) const { + std::string operator()(const statement_type& get, const C& context) const { return serialize_get_impl(get, context); } }; @@ -10800,8 +14525,29 @@ namespace sqlite_orm { using statement_type = get_pointer_t; template - std::string operator()(const statement_type &get, const C &context) const { - return serialize_get_impl(get, context); + std::string operator()(const statement_type& statement, const C& context) const { + return serialize_get_impl(statement, context); + } + }; + + template<> + struct statement_serializator { + using statement_type = insert_constraint; + + template + std::string operator()(const statement_type& statement, const C& context) const { + switch(statement) { + case insert_constraint::abort: + return "OR ABORT"; + case insert_constraint::fail: + return "OR FAIL"; + case insert_constraint::ignore: + return "OR IGNORE"; + case insert_constraint::replace: + return "OR REPLACE"; + case insert_constraint::rollback: + return "OR ROLLBACK"; + } } }; @@ -10811,7 +14557,7 @@ namespace sqlite_orm { using statement_type = get_optional_t; template - std::string operator()(const statement_type &get, const C &context) const { + std::string operator()(const statement_type& get, const C& context) const { return serialize_get_impl(get, context); } }; @@ -10821,11 +14567,12 @@ namespace sqlite_orm { using statement_type = select_t; template - std::string operator()(const statement_type &sel, const C &context) const { + std::string operator()(const statement_type& sel, const C& context) const { std::stringstream ss; - if(!is_base_of_template::value) { - if(!sel.highest_level) { - ss << "( "; + const auto isCompoundOperator = is_base_of_template::value; + if(!isCompoundOperator) { + if(!sel.highest_level && context.use_parentheses) { + ss << "("; } ss << "SELECT "; } @@ -10836,46 +14583,47 @@ namespace sqlite_orm { for(size_t i = 0; i < columnNames.size(); ++i) { ss << columnNames[i]; if(i < columnNames.size() - 1) { - ss << ","; + ss << ", "; } - ss << " "; } - table_name_collector collector{[&context](std::type_index ti) { + table_name_collector collector([&context](std::type_index ti) { return context.impl.find_table_name(ti); - }}; - iterate_ast(sel.col, collector); - iterate_ast(sel.conditions, collector); - internal::join_iterator()([&collector, &context](const auto &c) { - using original_join_type = typename std::decay::type::join_type::type; - using cross_join_type = typename internal::mapped_type_proxy::type; - auto crossJoinedTableName = context.impl.find_table_name(typeid(cross_join_type)); - auto tableAliasString = alias_extractor::get(); - std::pair tableNameWithAlias(std::move(crossJoinedTableName), - std::move(tableAliasString)); - collector.table_names.erase(tableNameWithAlias); }); - if(!collector.table_names.empty()) { - ss << "FROM "; - std::vector> tableNames(collector.table_names.begin(), - collector.table_names.end()); - for(size_t i = 0; i < tableNames.size(); ++i) { - auto &tableNamePair = tableNames[i]; - ss << "'" << tableNamePair.first << "' "; - if(!tableNamePair.second.empty()) { - ss << tableNamePair.second << " "; - } - if(int(i) < int(tableNames.size()) - 1) { - ss << ","; + const auto explicitFromItemsCount = count_tuple, is_from>::value; + if(!explicitFromItemsCount) { + iterate_ast(sel.col, collector); + iterate_ast(sel.conditions, collector); + join_iterator()([&collector, &context](const auto& c) { + using original_join_type = typename std::decay::type::join_type::type; + using cross_join_type = typename internal::mapped_type_proxy::type; + auto crossJoinedTableName = context.impl.find_table_name(typeid(cross_join_type)); + auto tableAliasString = alias_extractor::get(); + std::pair tableNameWithAlias(std::move(crossJoinedTableName), + std::move(tableAliasString)); + collector.table_names.erase(tableNameWithAlias); + }); + if(!collector.table_names.empty() && !isCompoundOperator) { + ss << " FROM "; + std::vector> tableNames(collector.table_names.begin(), + collector.table_names.end()); + for(size_t i = 0; i < tableNames.size(); ++i) { + auto& tableNamePair = tableNames[i]; + ss << "'" << tableNamePair.first << "'"; + if(!tableNamePair.second.empty()) { + ss << ' ' << tableNamePair.second; + } + if(int(i) < int(tableNames.size()) - 1) { + ss << ", "; + } } - ss << " "; } } - iterate_tuple(sel.conditions, [&context, &ss](auto &v) { - ss << serialize(v, context); + iterate_tuple(sel.conditions, [&context, &ss](auto& v) { + ss << ' ' << serialize(v, context); }); if(!is_base_of_template::value) { - if(!sel.highest_level) { - ss << ") "; + if(!sel.highest_level && context.use_parentheses) { + ss << ")"; } } return ss.str(); @@ -10887,7 +14635,7 @@ namespace sqlite_orm { using statement_type = indexed_column_t; template - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; ss << serialize(statement.column_or_expression, context); if(!statement._collation_name.empty()) { @@ -10914,7 +14662,7 @@ namespace sqlite_orm { using statement_type = index_t; template - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; ss << "CREATE "; if(statement.unique) { @@ -10926,11 +14674,12 @@ namespace sqlite_orm { ss << "INDEX IF NOT EXISTS '" << statement.name << "' ON '" << context.impl.find_table_name(typeid(indexed_type)) << "' ("; std::vector columnNames; - iterate_tuple(statement.columns, [&columnNames, &context](auto &v) { - columnNames.push_back(context.column_name(v.column_or_expression)); + iterate_tuple(statement.columns, [&columnNames, &context](auto& v) { + auto columnName = serialize(v, context); + columnNames.push_back(move(columnName)); }); for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "'" << columnNames[i] << "'"; + ss << columnNames[i]; if(i < columnNames.size() - 1) { ss << ", "; } @@ -10940,15 +14689,44 @@ namespace sqlite_orm { } }; + template + struct statement_serializator, void> { + using statement_type = from_t; + + template + std::string operator()(const statement_type& statement, const C& context) const { + using tuple = std::tuple; + + std::stringstream ss; + ss << "FROM "; + size_t index = 0; + iterate_tuple([&context, &ss, &index](auto* itemPointer) { + using mapped_type = typename std::remove_pointer::type; + + auto aliasString = alias_extractor::get(); + ss << "'" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) + << "'"; + if(aliasString.length()) { + ss << " '" << aliasString << "'"; + } + if(index < std::tuple_size::value - 1) { + ss << ", "; + } + ++index; + }); + return ss.str(); + } + }; + template struct statement_serializator, void> { using statement_type = where_t; template - std::string operator()(const statement_type &w, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; - ss << static_cast(w) << " "; - auto whereString = serialize(w.c, context); + ss << statement.serialize() << " "; + auto whereString = serialize(statement.expression, context); ss << "( " << whereString << ") "; return ss.str(); } @@ -10959,7 +14737,7 @@ namespace sqlite_orm { using statement_type = order_by_t; template - std::string operator()(const statement_type &orderBy, const C &context) const { + std::string operator()(const statement_type& orderBy, const C& context) const { std::stringstream ss; ss << static_cast(orderBy) << " "; auto orderByString = serialize_order_by(orderBy, context); @@ -10973,7 +14751,7 @@ namespace sqlite_orm { using statement_type = dynamic_order_by_t; template - std::string operator()(const statement_type &orderBy, const CC &context) const { + std::string operator()(const statement_type& orderBy, const CC& context) const { return serialize_order_by(orderBy, context); } }; @@ -10983,10 +14761,10 @@ namespace sqlite_orm { using statement_type = multi_order_by_t; template - std::string operator()(const statement_type &orderBy, const C &context) const { + std::string operator()(const statement_type& orderBy, const C& context) const { std::stringstream ss; std::vector expressions; - iterate_tuple(orderBy.args, [&expressions, &context](auto &v) { + iterate_tuple(orderBy.args, [&expressions, &context](auto& v) { auto expression = serialize_order_by(v, context); expressions.push_back(move(expression)); }); @@ -11007,7 +14785,7 @@ namespace sqlite_orm { using statement_type = cross_join_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast(c) << " "; ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; @@ -11020,7 +14798,7 @@ namespace sqlite_orm { using statement_type = inner_join_t; template - std::string operator()(const statement_type &l, const C &context) const { + std::string operator()(const statement_type& l, const C& context) const { std::stringstream ss; ss << static_cast(l) << " "; auto aliasString = alias_extractor::get(); @@ -11038,7 +14816,7 @@ namespace sqlite_orm { using statement_type = on_t; template - std::string operator()(const statement_type &t, const C &context) const { + std::string operator()(const statement_type& t, const C& context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; @@ -11052,10 +14830,14 @@ namespace sqlite_orm { using statement_type = join_t; template - std::string operator()(const statement_type &l, const C &context) const { + std::string operator()(const statement_type& l, const C& context) const { std::stringstream ss; ss << static_cast(l) << " "; - ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + auto aliasString = alias_extractor::get(); + ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) << "' "; + if(aliasString.length()) { + ss << "'" << aliasString << "' "; + } ss << serialize(l.constraint, context); return ss.str(); } @@ -11066,10 +14848,14 @@ namespace sqlite_orm { using statement_type = left_join_t; template - std::string operator()(const statement_type &l, const C &context) const { + std::string operator()(const statement_type& l, const C& context) const { std::stringstream ss; ss << static_cast(l) << " "; - ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + auto aliasString = alias_extractor::get(); + ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) << "' "; + if(aliasString.length()) { + ss << "'" << aliasString << "' "; + } ss << serialize(l.constraint, context); return ss.str(); } @@ -11080,10 +14866,14 @@ namespace sqlite_orm { using statement_type = left_outer_join_t; template - std::string operator()(const statement_type &l, const C &context) const { + std::string operator()(const statement_type& l, const C& context) const { std::stringstream ss; ss << static_cast(l) << " "; - ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + auto aliasString = alias_extractor::get(); + ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) << "' "; + if(aliasString.length()) { + ss << "'" << aliasString << "' "; + } ss << serialize(l.constraint, context); return ss.str(); } @@ -11094,7 +14884,7 @@ namespace sqlite_orm { using statement_type = natural_join_t; template - std::string operator()(const statement_type &c, const C &context) const { + std::string operator()(const statement_type& c, const C& context) const { std::stringstream ss; ss << static_cast(c) << " "; ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; @@ -11107,12 +14897,12 @@ namespace sqlite_orm { using statement_type = group_by_t; template - std::string operator()(const statement_type &groupBy, const C &context) const { + std::string operator()(const statement_type& groupBy, const C& context) const { std::stringstream ss; std::vector expressions; auto newContext = context; newContext.skip_table_name = false; - iterate_tuple(groupBy.args, [&expressions, &newContext](auto &v) { + iterate_tuple(groupBy.args, [&expressions, &newContext](auto& v) { auto expression = serialize(v, newContext); expressions.push_back(expression); }); @@ -11133,7 +14923,7 @@ namespace sqlite_orm { using statement_type = having_t; template - std::string operator()(const statement_type &hav, const C &context) const { + std::string operator()(const statement_type& hav, const C& context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; @@ -11152,21 +14942,21 @@ namespace sqlite_orm { using statement_type = limit_t; template - std::string operator()(const statement_type &limt, const C &context) const { + std::string operator()(const statement_type& limt, const C& context) const { auto newContext = context; newContext.skip_table_name = false; std::stringstream ss; ss << static_cast(limt) << " "; if(HO) { if(OI) { - limt.off.apply([&newContext, &ss](auto &value) { + limt.off.apply([&newContext, &ss](auto& value) { ss << serialize(value, newContext); }); ss << ", "; ss << serialize(limt.lim, newContext); } else { ss << serialize(limt.lim, newContext) << " OFFSET "; - limt.off.apply([&newContext, &ss](auto &value) { + limt.off.apply([&newContext, &ss](auto& value) { ss << serialize(value, newContext); }); } @@ -11177,12 +14967,22 @@ namespace sqlite_orm { } }; + template<> + struct statement_serializator { + using statement_type = default_values_t; + + template + std::string operator()(const statement_type& statement, const C& context) const { + return "DEFAULT VALUES"; + } + }; + template struct statement_serializator, void> { using statement_type = using_t; template - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { auto newContext = context; newContext.skip_table_name = true; return static_cast(statement) + " (" + serialize(statement.column, newContext) + " )"; @@ -11194,12 +14994,12 @@ namespace sqlite_orm { using statement_type = std::tuple; template - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; ss << '('; auto index = 0; using TupleSize = std::tuple_size; - iterate_tuple(statement, [&context, &index, &ss](auto &value) { + iterate_tuple(statement, [&context, &index, &ss](auto& value) { ss << serialize(value, context); if(index < TupleSize::value - 1) { ss << ", "; @@ -11216,7 +15016,7 @@ namespace sqlite_orm { using statement_type = values_t; template - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; if(context.use_parentheses) { ss << '('; @@ -11224,10 +15024,10 @@ namespace sqlite_orm { ss << "VALUES "; { auto index = 0; - auto &tuple = statement.tuple; + auto& tuple = statement.tuple; using tuple_type = typename std::decay::type; using TupleSize = std::tuple_size; - iterate_tuple(tuple, [&context, &index, &ss](auto &value) { + iterate_tuple(tuple, [&context, &index, &ss](auto& value) { ss << serialize(value, context); if(index < TupleSize::value - 1) { ss << ", "; @@ -11247,7 +15047,7 @@ namespace sqlite_orm { using statement_type = dynamic_values_t; template - std::string operator()(const statement_type &statement, const C &context) const { + std::string operator()(const statement_type& statement, const C& context) const { std::stringstream ss; if(context.use_parentheses) { ss << '('; @@ -11256,7 +15056,7 @@ namespace sqlite_orm { { auto vectorSize = statement.vector.size(); for(decltype(vectorSize) index = 0; index < vectorSize; ++index) { - auto &value = statement.vector[index]; + auto& value = statement.vector[index]; ss << serialize(value, context); if(index < vectorSize - 1) { ss << ", "; @@ -11277,6 +15077,10 @@ namespace sqlite_orm { // #include "object_from_column_builder.h" +// #include "table.h" + +// #include "column.h" + namespace sqlite_orm { namespace internal { @@ -11294,10 +15098,10 @@ namespace sqlite_orm { * @param filename database filename. * @param impl_ storage_impl head */ - storage_t(const std::string &filename, impl_type impl_) : + storage_t(const std::string& filename, impl_type impl_) : storage_base{filename, foreign_keys_count(impl_)}, impl(std::move(impl_)) {} - storage_t(const storage_t &other) : storage_base(other), impl(other.impl) {} + storage_t(const storage_t& other) : storage_base(other), impl(other.impl) {} protected: impl_type impl; @@ -11315,42 +15119,30 @@ namespace sqlite_orm { friend struct serializator_context_builder; template - void create_table(sqlite3 *db, const std::string &tableName, const I &tableImpl) { + void create_table(sqlite3* db, const std::string& tableName, const I& tableImpl) { + using table_type = typename std::decay::type; std::stringstream ss; ss << "CREATE TABLE '" << tableName << "' ( "; auto columnsCount = tableImpl.table.columns_count; auto index = 0; using context_t = serializator_context; context_t context{this->impl}; - iterate_tuple(tableImpl.table.columns, [columnsCount, &index, &ss, &context](auto &c) { + iterate_tuple(tableImpl.table.columns, [columnsCount, &index, &ss, &context](auto& c) { ss << serialize(c, context); if(index < columnsCount - 1) { ss << ", "; } index++; }); - ss << ") "; - if(tableImpl.table._without_rowid) { - ss << "WITHOUT ROWID "; - } - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + ss << ")"; + if(table_type::is_without_rowid) { + ss << " WITHOUT ROWID "; } + perform_void_exec(db, ss.str()); } template - void backup_table(sqlite3 *db, const I &tableImpl, const std::vector &columnsToIgnore) { + void backup_table(sqlite3* db, const I& tableImpl, const std::vector& columnsToIgnore) { // here we copy source table to another with a name with '_backup' suffix, but in case table with such // a name already exists we append suffix 1, then 2, etc until we find a free name.. @@ -11385,18 +15177,49 @@ namespace sqlite_orm { } template - auto &get_impl() const { + void assert_insertable_type() const { + auto& tImpl = this->get_impl(); + using table_type = typename std::decay::type; + using columns_type = typename std::decay::type; + + using bool_type = std::integral_constant; + + static_if( + [](auto& tImpl) { + std::ignore = tImpl; + + // all right. it's a "without_rowid" table + }, + [](auto& tImpl) { + std::ignore = tImpl; + static_assert( + count_tuple::value <= 1, + "Attempting to execute 'insert' request into an noninsertable table was detected. " + "Insertable table cannot contain > 1 primary keys. Please use 'replace' instead of " + "'insert', or you can use 'insert' with explicit column listing."); + static_assert( + count_tuple::value == 0, + "Attempting to execute 'insert' request into an noninsertable table was detected. " + "Insertable table cannot contain non-standard primary keys. Please use 'replace' instead " + "of 'insert', or you can use 'insert' with explicit column listing."); + + // unfortunately, this static_assert's can't see an composite keys(( + })(tImpl); + } + + template + auto& get_impl() const { return this->impl.template get_impl(); } template - auto &get_impl() { + auto& get_impl() { return this->impl.template get_impl(); } public: template - view_t iterate(Args &&... args) { + view_t iterate(Args&&... args) { this->assert_mapped_type(); auto con = this->get_connection(); @@ -11411,7 +15234,7 @@ namespace sqlite_orm { * @example: storage.remove_all(where(in(&User::id, {5, 6, 7}))); - DELETE FROM users WHERE id IN (5, 6, 7) */ template - void remove_all(Args &&... args) { + void remove_all(Args&&... args) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::remove_all(std::forward(args)...)); this->execute(statement); @@ -11436,7 +15259,7 @@ namespace sqlite_orm { * @param o object to be updated. */ template - void update(const O &o) { + void update(const O& o) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::update(std::ref(o))); this->execute(statement); @@ -11450,7 +15273,7 @@ namespace sqlite_orm { protected: template - std::string group_concat_internal(F O::*m, std::unique_ptr y, Args &&... args) { + std::string group_concat_internal(F O::*m, std::unique_ptr y, Args&&... args) { this->assert_mapped_type(); std::vector rows; if(y) { @@ -11475,7 +15298,7 @@ namespace sqlite_orm { * @example: storage.get_all(where(like(&User::name, "N%")), order_by(&User::id)); - SELECT * FROM users WHERE name LIKE 'N%' ORDER BY id */ template - auto get_all(Args &&... args) { + auto get_all(Args&&... args) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::get_all(std::forward(args)...)); return this->execute(statement); @@ -11490,7 +15313,7 @@ namespace sqlite_orm { * @example: storage.get_all>(where(like(&User::name, "N%")), order_by(&User::id)); - SELECT * FROM users WHERE name LIKE 'N%' ORDER BY id */ template - auto get_all(Args &&... args) { + auto get_all(Args&&... args) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::get_all(std::forward(args)...)); return this->execute(statement); @@ -11505,7 +15328,7 @@ namespace sqlite_orm { * @example: storage.get_all_pointer(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 */ template - auto get_all_pointer(Args &&... args) { + auto get_all_pointer(Args&&... args) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::get_all_pointer(std::forward(args)...)); return this->execute(statement); @@ -11520,7 +15343,7 @@ namespace sqlite_orm { * @example: storage.get_all_pointer>(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 */ template - auto get_all_pointer(Args &&... args) { + auto get_all_pointer(Args&&... args) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::get_all_pointer(std::forward(args)...)); return this->execute(statement); @@ -11588,7 +15411,7 @@ namespace sqlite_orm { * @return Number of O object in table. */ template::type> - int count(Args &&... args) { + int count(Args&&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::count(), std::forward(args)...); if(!rows.empty()) { @@ -11601,9 +15424,10 @@ namespace sqlite_orm { /** * SELECT COUNT(X) https://www.sqlite.org/lang_aggfunc.html#count * @param m member pointer to class mapped to the storage. + * @return count of `m` values from database. */ template - int count(F O::*m, Args &&... args) { + int count(F O::*m, Args&&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::count(m), std::forward(args)...); if(!rows.empty()) { @@ -11616,10 +15440,10 @@ namespace sqlite_orm { /** * AVG(X) query. https://www.sqlite.org/lang_aggfunc.html#avg * @param m is a class member pointer (the same you passed into make_column). - * @return average value from db. + * @return average value from database. */ template - double avg(F O::*m, Args &&... args) { + double avg(F O::*m, Args&&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::avg(m), std::forward(args)...); if(!rows.empty()) { @@ -11644,7 +15468,7 @@ namespace sqlite_orm { class... Args, class Tuple = std::tuple, typename sfinae = typename std::enable_if::value >= 1>::type> - std::string group_concat(F O::*m, Args &&... args) { + std::string group_concat(F O::*m, Args&&... args) { return this->group_concat_internal(m, {}, std::forward(args)...); } @@ -11654,14 +15478,14 @@ namespace sqlite_orm { * @return group_concat query result. */ template - std::string group_concat(F O::*m, std::string y, Args &&... args) { + std::string group_concat(F O::*m, std::string y, Args&&... args) { return this->group_concat_internal(m, std::make_unique(move(y)), std::forward(args)...); } template - std::string group_concat(F O::*m, const char *y, Args &&... args) { + std::string group_concat(F O::*m, const char* y, Args&&... args) { std::unique_ptr str; if(y) { str = std::make_unique(y); @@ -11677,7 +15501,7 @@ namespace sqlite_orm { * @return std::unique_ptr with max value or null if sqlite engine returned null. */ template::type> - std::unique_ptr max(F O::*m, Args &&... args) { + std::unique_ptr max(F O::*m, Args&&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::max(m), std::forward(args)...); if(!rows.empty()) { @@ -11693,7 +15517,7 @@ namespace sqlite_orm { * @return std::unique_ptr with min value or null if sqlite engine returned null. */ template::type> - std::unique_ptr min(F O::*m, Args &&... args) { + std::unique_ptr min(F O::*m, Args&&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::min(m), std::forward(args)...); if(!rows.empty()) { @@ -11709,7 +15533,7 @@ namespace sqlite_orm { * @return std::unique_ptr with sum value or null if sqlite engine returned null. */ template::type> - std::unique_ptr sum(F O::*m, Args &&... args) { + std::unique_ptr sum(F O::*m, Args&&... args) { this->assert_mapped_type(); std::vector> rows = this->select(sqlite_orm::sum(m), std::forward(args)...); @@ -11731,7 +15555,7 @@ namespace sqlite_orm { * https://www.sqlite.org/lang_aggfunc.html) */ template - double total(F O::*m, Args &&... args) { + double total(F O::*m, Args&&... args) { this->assert_mapped_type(); auto rows = this->select(sqlite_orm::total(m), std::forward(args)...); if(!rows.empty()) { @@ -11757,7 +15581,7 @@ namespace sqlite_orm { template typename std::enable_if::value, std::string>::type - dump(const T &preparedStatement) const { + dump(const T& preparedStatement) const { using context_t = serializator_context; context_t context{this->impl}; return serialize(preparedStatement.t, context); @@ -11769,13 +15593,13 @@ namespace sqlite_orm { */ template typename std::enable_if::value, std::string>::type - dump(const O &o) { - auto &tImpl = this->get_impl(); + dump(const O& o) { + auto& tImpl = this->get_impl(); std::stringstream ss; ss << "{ "; using pair = std::pair; std::vector pairs; - tImpl.table.for_each_column([&pairs, &o](auto &c) { + tImpl.table.for_each_column([&pairs, &o](auto& c) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; pair p{c.name, std::string()}; @@ -11789,7 +15613,7 @@ namespace sqlite_orm { pairs.push_back(move(p)); }); for(size_t i = 0; i < pairs.size(); ++i) { - auto &p = pairs[i]; + auto& p = pairs[i]; ss << p.first << " : '" << p.second << "'"; if(i < pairs.size() - 1) { ss << ", "; @@ -11807,7 +15631,7 @@ namespace sqlite_orm { * id and creates own one. */ template - void replace(const O &o) { + void replace(const O& o) { this->assert_mapped_type(); auto statement = this->prepare(sqlite_orm::replace(std::ref(o))); this->execute(statement); @@ -11825,8 +15649,19 @@ namespace sqlite_orm { this->execute(statement); } + template + void replace_range(It from, It to, L transformer) { + this->assert_mapped_type(); + if(from == to) { + return; + } + + auto statement = this->prepare(sqlite_orm::replace_range(from, to, std::move(transformer))); + this->execute(statement); + } + template - int insert(const O &o, columns_t cols) { + int insert(const O& o, columns_t cols) { constexpr const size_t colsCount = std::tuple_size>::value; static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); this->assert_mapped_type(); @@ -11840,22 +15675,116 @@ namespace sqlite_orm { * @return id of just created object. */ template - int insert(const O &o) { + int insert(const O& o) { this->assert_mapped_type(); - auto statement = this->prepare(sqlite_orm::insert(std::ref(o))); - return int(this->execute(statement)); + this->assert_insertable_type(); + + return call_insert_impl_and_catch_constraint_failed([this, &o]() { + auto statement = this->prepare(sqlite_orm::insert(std::ref(o))); + return int(this->execute(statement)); + }); + } + + /** + * Raw insert routine. Use this if `insert` with object does not fit you. This insert is designed to be able + * to call any type of `INSERT` query with no limitations. + * @example + * ```sql + * INSERT INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * storage.insert(into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix"))); + * ``` + * One more example: + * ```sql + * INSERT INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * storage.insert(into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs"))); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * INSERT INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * storage.insert(into(), default_values()); + * ``` + * Also one can use `INSERT OR ABORT`/`INSERT OR FAIL`/`INSERT OR IGNORE`/`INSERT OR REPLACE`/`INSERT ROLLBACK`: + * ```c++ + * storage.insert(or_ignore(), into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs"))); + * storage.insert(or_rollback(), into(), default_values()); + * storage.insert(or_abort(), into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix"))); + * ``` + */ + template + void insert(Args... args) { + auto statement = this->prepare(sqlite_orm::insert(std::forward(args)...)); + this->execute(statement); + } + + /** + * Raw replace statement creation routine. Use this if `replace` with object does not fit you. This replace is designed to be able + * to call any type of `REPLACE` query with no limitations. Actually this is the same query as raw insert except `OR...` option existance. + * @example + * ```sql + * REPLACE INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * storage.prepare(replace(into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * ``` + * One more example: + * ```sql + * REPLACE INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * storage.prepare(replace(into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * REPLACE INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * storage.prepare(replace(into(), default_values())); + * ``` + */ + template + void replace(Args... args) { + auto statement = this->prepare(sqlite_orm::replace(std::forward(args)...)); + this->execute(statement); } template void insert_range(It from, It to) { using O = typename std::iterator_traits::value_type; this->assert_mapped_type(); + this->assert_insertable_type(); if(from == to) { return; } - auto statement = this->prepare(sqlite_orm::insert_range(from, to)); - this->execute(statement); + call_insert_impl_and_catch_constraint_failed([this, from, to]() { + auto statement = this->prepare(sqlite_orm::insert_range(from, to)); + this->execute(statement); + }); + } + + template + void insert_range(It from, It to, L transformer) { + this->assert_mapped_type(); + this->assert_insertable_type(); + if(from == to) { + return; + } + call_insert_impl_and_catch_constraint_failed([this, from, to, transformer = std::move(transformer)]() { + auto statement = this->prepare(sqlite_orm::insert_range(from, to, std::move(transformer))); + this->execute(statement); + }); } /** @@ -11865,7 +15794,7 @@ namespace sqlite_orm { template void rename_table(std::string name) { this->assert_mapped_type(); - auto &tImpl = this->get_impl(); + auto& tImpl = this->get_impl(); tImpl.table.name = move(name); } @@ -11876,30 +15805,42 @@ namespace sqlite_orm { * any SQLite queries */ template - const std::string &tablename() const { + const std::string& tablename() const { this->assert_mapped_type(); - auto &tImpl = this->get_impl(); + auto& tImpl = this->get_impl(); return tImpl.table.name; } + template + const std::string* column_name(F O::*memberPointer) const { + return this->impl.column_name(memberPointer); + } + protected: template - sync_schema_result sync_table(const storage_impl, Tss...> &tableImpl, sqlite3 *db, bool) { + sync_schema_result schema_status(const storage_impl, Tss...>&, sqlite3*, bool) { + return sync_schema_result::already_in_sync; + } + + template class TTable, class... Tss, class... Cs> + sync_schema_result + schema_status(const storage_impl, Tss...>& tImpl, sqlite3* db, bool preserve) { + return tImpl.schema_status(db, preserve); + } + + template + sync_schema_result sync_table(const storage_impl, Tss...>& tableImpl, sqlite3* db, bool) { auto res = sync_schema_result::already_in_sync; using context_t = serializator_context; context_t context{this->impl}; auto query = serialize(tableImpl.table, context); - auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_void_exec(db, query); return res; } - template + template class TTable, class... Tss, class... Cs> sync_schema_result - sync_table(const storage_impl, Tss...> &tImpl, sqlite3 *db, bool preserve) { + sync_table(const storage_impl, Tss...>& tImpl, sqlite3* db, bool preserve) { auto res = sync_schema_result::already_in_sync; auto schema_stat = tImpl.schema_status(db, preserve); @@ -11919,9 +15860,9 @@ namespace sqlite_orm { auto dbTableInfo = tImpl.get_table_info(tImpl.table.name, db); // this vector will contain pointers to columns that gotta be added.. - std::vector columnsToAdd; + std::vector columnsToAdd; - tImpl.get_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + tImpl.calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); if(schema_stat == sync_schema_result::old_columns_removed) { @@ -11953,6 +15894,24 @@ namespace sqlite_orm { return res; } + template + prepared_statement_t prepare_impl(S statement) { + auto con = this->get_connection(); + sqlite3_stmt* stmt; + auto db = con.get(); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(statement, context); + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + return prepared_statement_t{std::forward(statement), stmt, con}; + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } + public: /** * This is a cute function used to replace migration up/down functionality. @@ -11985,7 +15944,7 @@ namespace sqlite_orm { auto con = this->get_connection(); std::map result; auto db = con.get(); - this->impl.for_each([&result, db, preserve, this](auto &tableImpl) { + this->impl.for_each([&result, db, preserve, this](auto& tableImpl) { auto res = this->sync_table(tableImpl, db, preserve); result.insert({tableImpl.table.name, res}); }); @@ -12001,8 +15960,9 @@ namespace sqlite_orm { auto con = this->get_connection(); std::map result; auto db = con.get(); - this->impl.for_each([&result, db, preserve](auto tableImpl) { - result.insert({tableImpl.table.name, tableImpl.schema_status(db, preserve)}); + this->impl.for_each([&result, db, preserve, this](auto& tableImpl) { + auto schemaStatus = this->schema_status(tableImpl, db, preserve); + result.insert({tableImpl.table.name, schemaStatus}); }); return result; } @@ -12012,7 +15972,7 @@ namespace sqlite_orm { * Note: table can be not mapped to a storage * @return true if table with a given name exists in db, false otherwise. */ - bool table_exists(const std::string &tableName) { + bool table_exists(const std::string& tableName) { auto con = this->get_connection(); return this->impl.table_exists(tableName, con.get()); } @@ -12020,305 +15980,147 @@ namespace sqlite_orm { template prepared_statement_t> prepare(select_t sel) { sel.highest_level = true; - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(sel, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(sel), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(sel)); } template prepared_statement_t> prepare(get_all_t get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(get_)); } template prepared_statement_t> prepare(get_all_pointer_t get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(get_)); + } + + template + prepared_statement_t> prepare(replace_raw_t ins) { + return prepare_impl>(std::move(ins)); + } + + template + prepared_statement_t> prepare(insert_raw_t ins) { + return prepare_impl>(std::move(ins)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template prepared_statement_t> prepare(get_all_optional_t get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(get_)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template prepared_statement_t, Wargs...>> prepare(update_all_t, Wargs...> upd) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(upd, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(upd), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl, Wargs...>>(std::move(upd)); } template prepared_statement_t> prepare(remove_all_t rem) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(rem, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(rem), stmt, std::move(con)}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(rem)); } template prepared_statement_t> prepare(get_t get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(get_)); } template prepared_statement_t> prepare(get_pointer_t get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(get_)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template prepared_statement_t> prepare(get_optional_t get_) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(get_, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get_), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(get_)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template prepared_statement_t> prepare(update_t upd) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(upd, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(upd), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(upd)); } template prepared_statement_t> prepare(remove_t rem) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(rem, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(rem), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(rem)); } template prepared_statement_t> prepare(insert_t ins) { using object_type = typename expression_object_type::type; this->assert_mapped_type(); - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(ins, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(ins), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + this->assert_insertable_type(); + return prepare_impl>(std::move(ins)); } template prepared_statement_t> prepare(replace_t rep) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; using object_type = typename expression_object_type::type; this->assert_mapped_type(); - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(rep, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(rep), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + return prepare_impl>(std::move(rep)); } - template - prepared_statement_t> prepare(insert_range_t statement) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(statement, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(statement), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + template + prepared_statement_t> prepare(insert_range_t statement) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + this->assert_insertable_type(); + return prepare_impl>(std::move(statement)); } - template - prepared_statement_t> prepare(replace_range_t rep) { - auto con = this->get_connection(); - sqlite3_stmt *stmt; - auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(rep, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(rep), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + template + prepared_statement_t> prepare(replace_range_t rep) { + return prepare_impl>(std::move(rep)); } template prepared_statement_t> prepare(insert_explicit ins) { using object_type = typename expression_object_type::type; this->assert_mapped_type(); + return prepare_impl>(std::move(ins)); + } + + template + void execute(const prepared_statement_t>& statement) { auto con = this->get_connection(); - sqlite3_stmt *stmt; auto db = con.get(); - using context_t = serializator_context; - context_t context{this->impl}; - context.skip_table_name = false; - context.replace_bindable_with_question = true; - auto query = serialize(ins, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(ins), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + auto stmt = statement.stmt; + sqlite3_reset(stmt); + auto index = 1; + iterate_ast(statement.t.args, [stmt, &index, db](auto& node) { + using node_type = typename std::decay::type; + conditional_binder> binder{stmt, index}; + if(SQLITE_OK != binder(node)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + perform_step(db, stmt); + } + + template + void execute(const prepared_statement_t>& statement) { + auto con = this->get_connection(); + auto db = con.get(); + auto stmt = statement.stmt; + sqlite3_reset(stmt); + auto index = 1; + iterate_ast(statement.t.args, [stmt, &index, db](auto& node) { + using node_type = typename std::decay::type; + conditional_binder> binder{stmt, index}; + if(SQLITE_OK != binder(node)) { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + }); + perform_step(db, stmt); } template - int64 execute(const prepared_statement_t> &statement) { + int64 execute(const prepared_statement_t>& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type::type; @@ -12326,40 +16128,37 @@ namespace sqlite_orm { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; - auto &tImpl = this->get_impl(); - auto &o = statement.t.obj; + auto& tImpl = this->get_impl(); + auto& o = statement.t.obj; sqlite3_reset(stmt); - iterate_tuple(statement.t.columns.columns, [&o, &index, &stmt, &tImpl, db](auto &m) { + iterate_tuple(statement.t.columns.columns, [&o, &index, &stmt, &tImpl, db](auto& m) { using column_type = typename std::decay::type; using field_type = typename column_result_t::type; - const field_type *value = tImpl.table.template get_object_field_pointer(o, m); + const field_type* value = tImpl.table.template get_object_field_pointer(o, m); if(SQLITE_OK != statement_binder().bind(stmt, index++, *value)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - return sqlite3_last_insert_rowid(db); - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_step(db, stmt); + return sqlite3_last_insert_rowid(db); } - template - void execute(const prepared_statement_t> &statement) { + template::value || is_replace::value)>::type* = nullptr> + void execute(const prepared_statement_t& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; - using object_type = typename expression_type::object_type; - auto &tImpl = this->get_impl(); + using object_type = typename expression_object_type::type; + auto& tImpl = this->get_impl(); auto index = 1; auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; sqlite3_reset(stmt); - for(auto it = statement.t.range.first; it != statement.t.range.second; ++it) { - auto &o = *it; - tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { + + auto processObject = [&index, &stmt, &tImpl, db](auto& o) { + tImpl.table.for_each_column([&](auto& c) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; if(c.member_pointer) { @@ -12378,180 +16177,120 @@ namespace sqlite_orm { } } }); - } - if(sqlite3_step(stmt) == SQLITE_DONE) { - //.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } + }; - template - void execute(const prepared_statement_t> &statement) { - using statement_type = typename std::decay::type; - using expression_type = typename statement_type::expression_type; - using object_type = typename expression_type::object_type; - auto index = 1; - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto &tImpl = this->get_impl(); - sqlite3_reset(stmt); - for(auto it = statement.t.range.first; it != statement.t.range.second; ++it) { - auto &o = *it; - tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { - if(!c.template has>()) { - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - if(c.member_pointer) { - if(SQLITE_OK != - statement_binder().bind(stmt, index++, o.*c.member_pointer)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder valueHolder{((o).*(c.getter))()}; - if(SQLITE_OK != statement_binder().bind(stmt, index++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - } - }); - } - if(sqlite3_step(stmt) == SQLITE_DONE) { - //.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + static_if{}>( + [&processObject](auto& statement) { + auto& transformer = statement.t.transformer; + std::for_each( /// + statement.t.range.first, + statement.t.range.second, + [&processObject, &transformer](auto& object) { + auto& realObject = transformer(object); + processObject(realObject); + }); + }, + [&processObject](auto& statement) { + auto& o = get_object(statement.t); + processObject(o); + })(statement); + perform_step(db, stmt); } - template - void execute(const prepared_statement_t> &statement) { + template::value || is_insert::value)>::type* = nullptr> + int64 execute(const prepared_statement_t& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type::type; - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; auto index = 1; - auto &o = get_object(statement.t); - auto &tImpl = this->get_impl(); - sqlite3_reset(stmt); - tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - if(c.member_pointer) { - if(SQLITE_OK != statement_binder().bind(stmt, index++, o.*c.member_pointer)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder valueHolder{((o).*(c.getter))()}; - if(SQLITE_OK != statement_binder().bind(stmt, index++, valueHolder.value)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - //.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - - template - int64 execute(const prepared_statement_t> &statement) { - using statement_type = typename std::decay::type; - using expression_type = typename statement_type::expression_type; - using object_type = typename expression_object_type::type; - int64 res = 0; auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; - auto index = 1; - auto &tImpl = this->get_impl(); - auto &o = get_object(statement.t); + auto& tImpl = this->get_impl(); auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); sqlite3_reset(stmt); - tImpl.table.for_each_column([&o, &index, &stmt, &tImpl, &compositeKeyColumnNames, db](auto &c) { - if(tImpl.table._without_rowid || !c.template has>()) { - auto it = std::find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); - if(it == compositeKeyColumnNames.end()) { - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - if(c.member_pointer) { - if(SQLITE_OK != - statement_binder().bind(stmt, index++, o.*c.member_pointer)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder valueHolder{((o).*(c.getter))()}; - if(SQLITE_OK != statement_binder().bind(stmt, index++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + auto processObject = [&index, &stmt, &tImpl, &compositeKeyColumnNames, db](auto& o) { + tImpl.table.for_each_column([&](auto& c) { + using table_type = typename std::decay::type; + if(table_type::is_without_rowid || !c.template has>()) { + auto it = std::find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); + if(it == compositeKeyColumnNames.end()) { + using column_type = typename std::decay::type; + using field_type = typename column_type::field_type; + if(c.member_pointer) { + if(SQLITE_OK != + statement_binder().bind(stmt, index++, o.*c.member_pointer)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } + } else { + using getter_type = typename column_type::getter_type; + field_value_holder valueHolder{((o).*(c.getter))()}; + if(SQLITE_OK != + statement_binder().bind(stmt, index++, valueHolder.value)) { + throw std::system_error( + std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } } } } - } - }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - res = sqlite3_last_insert_rowid(db); - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - return res; + }); + }; + + static_if{}>( + [&processObject](auto& statement) { + auto& transformer = statement.t.transformer; + std::for_each( /// + statement.t.range.first, + statement.t.range.second, + [&processObject, &transformer](auto& object) { + auto& realObject = transformer(object); + processObject(realObject); + }); + }, + [&processObject](auto& statement) { + auto& o = get_object(statement.t); + processObject(o); + })(statement); + + perform_step(db, stmt); + return sqlite3_last_insert_rowid(db); } template - void execute(const prepared_statement_t> &statement) { + void execute(const prepared_statement_t>& statement) { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { using field_type = typename std::decay::type; if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_step(db, stmt); } template - void execute(const prepared_statement_t> &statement) { + void execute(const prepared_statement_t>& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type::type; auto con = this->get_connection(); auto db = con.get(); - auto &tImpl = this->get_impl(); + auto& tImpl = this->get_impl(); auto stmt = statement.stmt; auto index = 1; - auto &o = get_object(statement.t); + auto& o = get_object(statement.t); sqlite3_reset(stmt); - tImpl.table.for_each_column([&o, stmt, &index, db](auto &c) { - if(!c.template has>()) { + tImpl.table.for_each_column([&o, stmt, &index, db](auto& c) { + if(!c.template has>()) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; if(c.member_pointer) { @@ -12572,8 +16311,8 @@ namespace sqlite_orm { } } }); - tImpl.table.for_each_column([&o, stmt, &index, db](auto &c) { - if(c.template has>()) { + tImpl.table.for_each_column([&o, stmt, &index, db](auto& c) { + if(c.template has>()) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; if(c.member_pointer) { @@ -12593,23 +16332,18 @@ namespace sqlite_orm { } } }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_step(db, stmt); } template - std::unique_ptr execute(const prepared_statement_t> &statement) { - auto &tImpl = this->get_impl(); + std::unique_ptr execute(const prepared_statement_t>& statement) { + auto& tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { using field_type = typename std::decay::type; if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), @@ -12636,14 +16370,14 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - std::optional execute(const prepared_statement_t> &statement) { - auto &tImpl = this->get_impl(); + std::optional execute(const prepared_statement_t>& statement) { + auto& tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { using field_type = typename std::decay::type; if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), @@ -12670,14 +16404,14 @@ namespace sqlite_orm { #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - T execute(const prepared_statement_t> &statement) { - auto &tImpl = this->get_impl(); + T execute(const prepared_statement_t>& statement) { + auto& tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto &v) { + iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { using field_type = typename std::decay::type; if(SQLITE_OK != statement_binder().bind(stmt, index++, v)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), @@ -12703,13 +16437,13 @@ namespace sqlite_orm { } template - void execute(const prepared_statement_t> &statement) { + void execute(const prepared_statement_t>& statement) { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t.conditions, [stmt, &index, db](auto &node) { + iterate_ast(statement.t.conditions, [stmt, &index, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -12717,23 +16451,18 @@ namespace sqlite_orm { sqlite3_errmsg(db)); } }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_step(db, stmt); } template - void execute(const prepared_statement_t, Wargs...>> &statement) { + void execute(const prepared_statement_t, Wargs...>>& statement) { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_tuple(statement.t.set.assigns, [&index, stmt, db](auto &setArg) { - iterate_ast(setArg, [&index, stmt, db](auto &node) { + iterate_tuple(statement.t.set.assigns, [&index, stmt, db](auto& setArg) { + iterate_ast(setArg, [&index, stmt, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -12742,7 +16471,7 @@ namespace sqlite_orm { } }); }); - iterate_ast(statement.t.conditions, [stmt, &index, db](auto &node) { + iterate_ast(statement.t.conditions, [stmt, &index, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -12750,22 +16479,17 @@ namespace sqlite_orm { sqlite3_errmsg(db)); } }); - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_step(db, stmt); } template::type> - std::vector execute(const prepared_statement_t> &statement) { + std::vector execute(const prepared_statement_t>& statement) { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto &node) { + iterate_ast(statement.t, [stmt, &index, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -12799,14 +16523,14 @@ namespace sqlite_orm { } template - R execute(const prepared_statement_t> &statement) { - auto &tImpl = this->get_impl(); + R execute(const prepared_statement_t>& statement) { + auto& tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto &node) { + iterate_ast(statement.t, [stmt, &index, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -12837,14 +16561,14 @@ namespace sqlite_orm { } template - R execute(const prepared_statement_t> &statement) { - auto &tImpl = this->get_impl(); + R execute(const prepared_statement_t>& statement) { + auto& tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto &node) { + iterate_ast(statement.t, [stmt, &index, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -12876,14 +16600,14 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - R execute(const prepared_statement_t> &statement) { - auto &tImpl = this->get_impl(); + R execute(const prepared_statement_t>& statement) { + auto& tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; auto index = 1; sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto &node) { + iterate_ast(statement.t, [stmt, &index, db](auto& node) { using node_type = typename std::decay::type; conditional_binder> binder{stmt, index}; if(SQLITE_OK != binder(node)) { @@ -12913,6 +16637,23 @@ namespace sqlite_orm { return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + /*template + bool has_dependent_rows(const O& object) { + auto res = false; + using TupleWithForeignKeyTypes = typename storage_traits::storage_fk_references::type; + iterate_tuple([&res, this](auto *itemPointer){ + using ConstItem = typename std::remove_pointer::type; + using Item = typename std::decay::type; + if(!res) { + auto rows = this->select(count()); + if (!rows.empty()) { + res = rows[0]; + } + } + }); + return res; + }*/ }; // struct storage_t template @@ -12923,8 +16664,8 @@ namespace sqlite_orm { } template - internal::storage_t make_storage(const std::string &filename, Ts... tables) { - return {filename, internal::storage_impl(tables...)}; + internal::storage_t make_storage(const std::string& filename, Ts... tables) { + return {filename, internal::storage_impl(std::forward(tables)...)}; } /** @@ -12951,6 +16692,9 @@ __pragma(pop_macro("min")) #include // std::tuple #include // std::pair #include // std::reference_wrapper +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED // #include "conditions.h" @@ -12964,6 +16708,14 @@ __pragma(pop_macro("min")) // #include "core_functions.h" + // #include "function.h" + + // #include "ast/excluded.h" + + // #include "ast/upsert_clause.h" + + // #include "ast/where.h" + namespace sqlite_orm { namespace internal { @@ -12977,12 +16729,32 @@ __pragma(pop_macro("min")) struct node_tuple { using type = std::tuple<>; }; - +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct node_tuple, void> { + using type = typename node_tuple::type; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED template struct node_tuple, void> { using type = typename node_tuple::type; }; + template + struct node_tuple, std::tuple>, void> { + using type = typename node_tuple>::type; + }; + + template + struct node_tuple, void> { + using type = typename conc_tuple::type...>::type; + }; + + template + struct node_tuple, void> { + using type = typename node_tuple::type; + }; + template struct node_tuple, void> { using node_type = where_t; @@ -13016,13 +16788,21 @@ __pragma(pop_macro("min")) }; template - struct node_tuple, void> { - using node_type = in_t; + struct node_tuple, void> { + using node_type = dynamic_in_t; using left_tuple = typename node_tuple::type; using right_tuple = typename node_tuple::type; using type = typename conc_tuple::type; }; + template + struct node_tuple, void> { + using node_type = in_t; + using left_tuple = typename node_tuple::type; + using right_tuple = typename conc_tuple::type...>::type; + using type = typename conc_tuple::type; + }; + template struct node_tuple::value>::type> { using node_type = T; @@ -13041,6 +16821,36 @@ __pragma(pop_macro("min")) using type = typename conc_tuple::type; }; + template + struct node_tuple, void> { + using node_type = insert_raw_t; + using type = typename conc_tuple::type...>::type; + }; + + template + struct node_tuple, void> { + using node_type = replace_raw_t; + using type = typename conc_tuple::type...>::type; + }; + + template + struct node_tuple, void> { + using node_type = into_t; + using type = std::tuple<>; + }; + + template + struct node_tuple, void> { + using node_type = values_t; + using type = typename conc_tuple::type...>::type; + }; + + template + struct node_tuple, void> { + using node_type = std::tuple; + using type = typename conc_tuple::type...>::type; + }; + template struct node_tuple, void> { using node_type = get_all_t; @@ -13156,8 +16966,14 @@ __pragma(pop_macro("min")) }; template - struct node_tuple, void> { - using node_type = core_function_t; + struct node_tuple, void> { + using node_type = built_in_function_t; + using type = typename conc_tuple::type...>::type; + }; + + template + struct node_tuple, void> { + using node_type = function_call; using type = typename conc_tuple::type...>::type; }; @@ -13247,132 +17063,132 @@ __pragma(pop_macro("min")) namespace sqlite_orm { - template - auto &get(internal::prepared_statement_t> &statement) { + template + auto& get(internal::prepared_statement_t>& statement) { return std::get(statement.t.range); } - template - const auto &get(const internal::prepared_statement_t> &statement) { + template + const auto& get(const internal::prepared_statement_t>& statement) { return std::get(statement.t.range); } - template - auto &get(internal::prepared_statement_t> &statement) { + template + auto& get(internal::prepared_statement_t>& statement) { return std::get(statement.t.range); } - template - const auto &get(const internal::prepared_statement_t> &statement) { + template + const auto& get(const internal::prepared_statement_t>& statement) { return std::get(statement.t.range); } template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.t.ids)); } template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for update statement"); return internal::get_ref(statement.t.obj); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for update statement"); return internal::get_ref(statement.t.obj); } template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.t.obj); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.t.obj); } template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for replace statement"); return internal::get_ref(statement.t.obj); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for replace statement"); return internal::get_ref(statement.t.obj); } template - auto &get(internal::prepared_statement_t> &statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.t.obj); } template - const auto &get(const internal::prepared_statement_t> &statement) { + const auto& get(const internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.t.obj); } template - const auto &get(const internal::prepared_statement_t &statement) { + const auto& get(const internal::prepared_statement_t& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; using node_tuple = typename internal::node_tuple::type; using bind_tuple = typename internal::bindable_filter::type; - using result_tupe = typename std::tuple_element::type; - const result_tupe *result = nullptr; + using result_tupe = typename std::tuple_element(N), bind_tuple>::type; + const result_tupe* result = nullptr; auto index = -1; - internal::iterate_ast(statement.t, [&result, &index](auto &node) { + internal::iterate_ast(statement.t, [&result, &index](auto& node) { using node_type = typename std::decay::type; if(internal::is_bindable::value) { ++index; } if(index == N) { - internal::static_if{}>([](auto &r, auto &n) { + internal::static_if{}>([](auto& r, auto& n) { r = const_cast::type>(&n); })(result, node); } @@ -13381,21 +17197,21 @@ namespace sqlite_orm { } template - auto &get(internal::prepared_statement_t &statement) { + auto& get(internal::prepared_statement_t& statement) { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; using node_tuple = typename internal::node_tuple::type; using bind_tuple = typename internal::bindable_filter::type; - using result_tupe = typename std::tuple_element::type; - result_tupe *result = nullptr; + using result_tupe = typename std::tuple_element(N), bind_tuple>::type; + result_tupe* result = nullptr; auto index = -1; - internal::iterate_ast(statement.t, [&result, &index](auto &node) { + internal::iterate_ast(statement.t, [&result, &index](auto& node) { using node_type = typename std::decay::type; if(internal::is_bindable::value) { ++index; } if(index == N) { - internal::static_if{}>([](auto &r, auto &n) { + internal::static_if{}>([](auto& r, auto& n) { r = const_cast::type>(&n); })(result, node); } diff --git a/not_single_header_include/sqlite_orm/sqlite_orm.h b/not_single_header_include/sqlite_orm/sqlite_orm.h new file mode 100644 index 000000000..de73e0784 --- /dev/null +++ b/not_single_header_include/sqlite_orm/sqlite_orm.h @@ -0,0 +1,51 @@ +#pragma once + +#include "../../dev/start_macros.h" +#include "../../dev/error_code.h" +#include "../../dev/ast/upsert_clause.h" +#include "../../dev/ast/excluded.h" +#include "../../dev/tuple_helper/tuple_helper.h" +#include "../../dev/tuple_helper/find_in_tuple.h" +#include "../../dev/tuple_helper/tuple_transformer.h" +#include "../../dev/tuple_helper/count_tuple.h" +#include "../../dev/tuple_helper/same_or_void.h" +#include "../../dev/member_traits/is_field_member_pointer.h" +#include "../../dev/member_traits/field_member_traits.h" +#include "../../dev/member_traits/getters.h" +#include "../../dev/member_traits/setters.h" +#include "../../dev/member_traits/is_getter.h" +#include "../../dev/member_traits/is_setter.h" +#include "../../dev/member_traits/getter_traits.h" +#include "../../dev/member_traits/setter_traits.h" +#include "../../dev/member_traits/member_traits.h" +#include "../../dev/type_printer.h" +#include "../../dev/collate_argument.h" +#include "../../dev/constraints.h" +#include "../../dev/type_is_nullable.h" +#include "../../dev/default_value_extractor.h" +#include "../../dev/operators.h" +#include "../../dev/column.h" +#include "../../dev/field_printer.h" +#include "../../dev/conditions.h" +#include "../../dev/alias.h" +#include "../../dev/join_iterator.h" +#include "../../dev/core_functions.h" +#include "../../dev/typed_comparator.h" +#include "../../dev/select_constraints.h" +#include "../../dev/table_info.h" +#include "../../dev/statement_finalizer.h" +#include "../../dev/arithmetic_tag.h" +#include "../../dev/statement_binder.h" +#include "../../dev/row_extractor.h" +#include "../../dev/util.h" +#include "../../dev/sync_schema_result.h" +#include "../../dev/index.h" +#include "../../dev/mapped_type_proxy.h" +#include "../../dev/rowid.h" +#include "../../dev/column_result.h" +#include "../../dev/table.h" +#include "../../dev/storage_impl.h" +#include "../../dev/storage.h" +#include "../../dev/finish_macros.h" +#include "../../dev/node_tuple.h" +#include "../../dev/get_prepared_statement.h" diff --git a/prepare-osx-with-clang.sh b/prepare-osx-with-clang.sh deleted file mode 100755 index c824f65d2..000000000 --- a/prepare-osx-with-clang.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -# exit on firts error -set -e - -brew install llvm -brew info llvm -export PATH="/usr/local/opt/llvm/bin:$PATH" -echo 'export PATH="/usr/local/opt/llvm/bin:$PATH"' >> ~/.zshenv diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fd41e703e..03392763a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,16 +2,108 @@ cmake_minimum_required (VERSION 3.2) option(SQLITE_ORM_OMITS_CODECVT "Omits codec testing" OFF) -option(SqliteOrm_SysSqlite "Use system version of sqlite library" OFF) - -if(SqliteOrm_SysSqlite) - message(FATAL_ERROR "WIP: please, disable the SqliteOrm_SysSqlite option.") -else() - add_subdirectory(third_party/sqlite) -endif() - -add_executable(unit_tests tests.cpp tests2.cpp tests3.cpp tests4.cpp tests5.cpp private_getters_tests.cpp pragma_tests.cpp explicit_columns.cpp core_functions_tests.cpp index_tests.cpp constraints/composite_key.cpp static_tests.cpp operators/arithmetic_operators.cpp operators/like.cpp operators/glob.cpp operators/in.cpp operators/cast.cpp operators/is_null.cpp operators/not_operator.cpp operators/bitwise.cpp dynamic_order_by.cpp prepared_statement_tests/select.cpp prepared_statement_tests/get_all.cpp prepared_statement_tests/get_all_pointer.cpp prepared_statement_tests/get_all_optional.cpp prepared_statement_tests/update_all.cpp prepared_statement_tests/remove_all.cpp prepared_statement_tests/get.cpp prepared_statement_tests/get_pointer.cpp prepared_statement_tests/get_optional.cpp prepared_statement_tests/update.cpp prepared_statement_tests/remove.cpp prepared_statement_tests/insert.cpp prepared_statement_tests/replace.cpp prepared_statement_tests/insert_range.cpp prepared_statement_tests/replace_range.cpp prepared_statement_tests/insert_explicit.cpp pragma_tests.cpp simple_query.cpp static_tests/is_bindable.cpp static_tests/arithmetic_operators_result_type.cpp static_tests/tuple_conc.cpp static_tests/node_tuple.cpp static_tests/bindable_filter.cpp static_tests/count_tuple.cpp static_tests/member_traits_tests.cpp static_tests/select_return_type.cpp constraints/default.cpp constraints/unique.cpp constraints/foreign_key.cpp constraints/check.cpp table_tests.cpp statement_serializator_tests/primary_key.cpp statement_serializator_tests/column_names.cpp statement_serializator_tests/autoincrement.cpp statement_serializator_tests/arithmetic_operators.cpp statement_serializator_tests/core_functions.cpp statement_serializator_tests/comparison_operators.cpp statement_serializator_tests/unique.cpp statement_serializator_tests/foreign_key.cpp statement_serializator_tests/collate.cpp statement_serializator_tests/check.cpp statement_serializator_tests/index.cpp statement_serializator_tests/indexed_column.cpp unique_cases/get_all_with_two_tables.cpp unique_cases/prepare_get_all_with_case.cpp unique_cases/index_named_table_with_fk.cpp unique_cases/issue525.cpp unique_cases/delete_with_two_fields.cpp unique_cases/join_iterator_ctor_compilation_error.cpp get_all_custom_containers.cpp select_asterisk.cpp backup_tests.cpp transaction_tests.cpp) - +add_executable(unit_tests + tests.cpp + tests2.cpp + tests3.cpp + tests4.cpp + tests5.cpp + private_getters_tests.cpp + pragma_tests.cpp + explicit_columns.cpp + built_in_functions_tests/core_functions_tests.cpp + built_in_functions_tests/datetime_function_tests.cpp + built_in_functions_tests/math_functions.cpp + index_tests.cpp + constraints/composite_key.cpp + operators/arithmetic_operators.cpp + operators/like.cpp + operators/glob.cpp + operators/in.cpp + operators/cast.cpp + operators/is_null.cpp + operators/not_operator.cpp + operators/bitwise.cpp + operators/binary_operators.cpp + prepared_statement_tests/select.cpp + prepared_statement_tests/get_all.cpp + prepared_statement_tests/get_all_pointer.cpp + prepared_statement_tests/get_all_optional.cpp + prepared_statement_tests/update_all.cpp + prepared_statement_tests/remove_all.cpp + prepared_statement_tests/get.cpp + prepared_statement_tests/get_pointer.cpp + prepared_statement_tests/get_optional.cpp + prepared_statement_tests/update.cpp + prepared_statement_tests/remove.cpp + prepared_statement_tests/insert.cpp + prepared_statement_tests/replace.cpp + prepared_statement_tests/insert_range.cpp + prepared_statement_tests/replace_range.cpp + prepared_statement_tests/insert_explicit.cpp + pragma_tests.cpp + simple_query.cpp + static_tests/is_bindable.cpp + static_tests/iterator_t.cpp + static_tests/arithmetic_operators_result_type.cpp + static_tests/tuple_conc.cpp + static_tests/node_tuple.cpp + static_tests/bindable_filter.cpp + static_tests/count_tuple.cpp + static_tests/member_traits_tests.cpp + static_tests/select_return_type.cpp + static_tests/column.cpp + static_tests/same_or_void.cpp + static_tests/foreign_key.cpp + static_tests/tuple_filter_single.cpp + static_tests/storage_traits.cpp + static_tests/tuple_helper.cpp + static_tests/function_static_tests.cpp + static_tests/aggregate_function_return_types.cpp + static_tests/column_result_t.cpp + constraints/default.cpp + constraints/unique.cpp + constraints/foreign_key.cpp + constraints/check.cpp + table_tests.cpp + statement_serializator_tests/primary_key.cpp + statement_serializator_tests/column_names.cpp + statement_serializator_tests/autoincrement.cpp + statement_serializator_tests/arithmetic_operators.cpp + statement_serializator_tests/core_functions.cpp + statement_serializator_tests/comparison_operators.cpp + statement_serializator_tests/unique.cpp + statement_serializator_tests/foreign_key.cpp + statement_serializator_tests/collate.cpp + statement_serializator_tests/check.cpp + statement_serializator_tests/index.cpp + statement_serializator_tests/indexed_column.cpp + statement_serializator_tests/logical_operators.cpp + statement_serializator_tests/select_constraints.cpp + statement_serializator_tests/insert_replace.cpp + statement_serializator_tests/ast/upsert_clause.cpp + statement_serializator_tests/ast/excluded.cpp + storage_tests.cpp + storage_non_crud_tests.cpp + unique_cases/get_all_with_two_tables.cpp + unique_cases/prepare_get_all_with_case.cpp + unique_cases/index_named_table_with_fk.cpp + unique_cases/issue525.cpp + unique_cases/delete_with_two_fields.cpp + unique_cases/join_iterator_ctor_compilation_error.cpp + get_all_custom_containers.cpp + select_constraints_tests.cpp + backup_tests.cpp + transaction_tests.cpp + json.cpp + tuple_helper_tests.cpp + row_id.cpp + ast_iterator_tests.cpp + unique_cases/issue663.cpp + unique_cases/nonstandart_primary_key.cpp + static_tests/has_some_type.cpp + static_tests/is_primary_key_insertable.cpp + static_tests/is_column_with_insertable_primary_key.cpp) if(SQLITE_ORM_OMITS_CODECVT) message(STATUS "SQLITE_ORM_OMITS_CODECVT is enabled") diff --git a/tests/ast_iterator_tests.cpp b/tests/ast_iterator_tests.cpp new file mode 100644 index 000000000..ccb833948 --- /dev/null +++ b/tests/ast_iterator_tests.cpp @@ -0,0 +1,90 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("ast_iterator") { + struct User { + int id = 0; + std::string name; + }; + std::vector typeIndexes; + decltype(typeIndexes) expected; + auto lambda = [&typeIndexes](auto &value) { + typeIndexes.push_back(typeid(value)); + }; + SECTION("excluded") { + auto node = excluded(&User::id); + expected.push_back(typeid(&User::id)); + internal::iterate_ast(node, lambda); + } + SECTION("upsert_clause") { + auto node = on_conflict(&User::id).do_update(set(c(&User::name) = excluded(&User::name))); + expected.push_back(typeid(&User::name)); + expected.push_back(typeid(&User::name)); + internal::iterate_ast(node, lambda); + } + SECTION("into") { + auto node = into(); + internal::iterate_ast(node, lambda); + } + SECTION("replace") { + auto node = + replace(into(), columns(&User::id, &User::name), values(std::make_tuple(1, std::string("Ellie")))); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(&User::name)); + expected.push_back(typeid(int)); + expected.push_back(typeid(std::string)); + internal::iterate_ast(node, lambda); + } + SECTION("insert") { + auto node = + insert(into(), columns(&User::id, &User::name), values(std::make_tuple(1, std::string("Ellie")))); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(&User::name)); + expected.push_back(typeid(int)); + expected.push_back(typeid(std::string)); + internal::iterate_ast(node, lambda); + } + SECTION("values") { + auto node = values(std::make_tuple(1, std::string("hi"))); + expected.push_back(typeid(int)); + expected.push_back(typeid(std::string)); + internal::iterate_ast(node, lambda); + } + SECTION("tuple") { + auto node = std::make_tuple(1, std::string("hi")); + expected.push_back(typeid(int)); + expected.push_back(typeid(std::string)); + internal::iterate_ast(node, lambda); + } + SECTION("in") { + SECTION("static") { + auto node = c(&User::id).in(1, 2, 3); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(int)); + expected.push_back(typeid(int)); + expected.push_back(typeid(int)); + internal::iterate_ast(node, lambda); + } + SECTION("dynamic") { + auto node = in(&User::id, {1, 2, 3}); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(int)); + expected.push_back(typeid(int)); + expected.push_back(typeid(int)); + internal::iterate_ast(node, lambda); + } + } + SECTION("function_call") { + struct Func { + bool operator()(int value) const { + return value % 2 == 0; + } + }; + auto node = func(&User::id); + expected.push_back(typeid(&User::id)); + internal::iterate_ast(node, lambda); + } + REQUIRE(typeIndexes == expected); +} diff --git a/tests/backup_tests.cpp b/tests/backup_tests.cpp index e20c68b38..07f9bfd5f 100644 --- a/tests/backup_tests.cpp +++ b/tests/backup_tests.cpp @@ -10,7 +10,7 @@ namespace BackupTests { std::string name; }; - bool operator==(const User &lhs, const User &rhs) { + bool operator==(const User& lhs, const User& rhs) { return lhs.id == rhs.id && lhs.name == rhs.name; } @@ -20,7 +20,7 @@ namespace BackupTests { std::string abilities; }; - inline auto initStorageMarvel(const std::string &path) { + inline auto initStorageMarvel(const std::string& path) { using namespace sqlite_orm; auto storage = make_storage(path, make_table("marvel", @@ -35,7 +35,7 @@ TEST_CASE("backup") { using namespace BackupTests; using Catch::Matchers::UnorderedEquals; const std::string usersTableName = "users"; - auto makeStorage = [&usersTableName](const std::string &filename) { + auto makeStorage = [&usersTableName](const std::string& filename) { return make_storage( filename, make_table(usersTableName, make_column("id", &User::id, primary_key()), make_column("name", &User::name))); diff --git a/tests/built_in_functions_tests/core_functions_tests.cpp b/tests/built_in_functions_tests/core_functions_tests.cpp new file mode 100644 index 000000000..be0ea5eeb --- /dev/null +++ b/tests/built_in_functions_tests/core_functions_tests.cpp @@ -0,0 +1,1297 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("substr") { + struct Test { + std::string text; + int x = 0; + int y = 0; + }; + auto storage = make_storage( + {}, + make_table("test", make_column("text", &Test::text), make_column("x", &Test::x), make_column("y", &Test::y))); + storage.sync_schema(); + + { + auto rows = storage.select(substr("SQLite substr", 8)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "substr"); + } + { + storage.insert(Test{"SQLite substr", 8}); + REQUIRE(storage.count() == 1); + auto rows = storage.select(substr(&Test::text, &Test::x)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "substr"); + } + { + auto rows = storage.select(substr("SQLite substr", 1, 6)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "SQLite"); + } + { + + storage.remove_all(); + storage.insert(Test{"SQLite substr", 1, 6}); + REQUIRE(storage.count() == 1); + + auto rows = storage.select(substr(&Test::text, &Test::x, &Test::y)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "SQLite"); + } +} + +TEST_CASE("zeroblob") { + struct Test { + int value = 0; + }; + + auto storage = make_storage({}, make_table("test", make_column("value", &Test::value))); + storage.sync_schema(); + + { + auto rows = storage.select(zeroblob(10)); + REQUIRE(rows.size() == 1); + auto& row = rows.front(); + REQUIRE(row.size() == 10); + std::vector expectedValue(10); + std::fill(expectedValue.begin(), expectedValue.end(), 0); + REQUIRE(row == expectedValue); + } + { + storage.insert(Test{100}); + + auto rows = storage.select(zeroblob(&Test::value)); + REQUIRE(rows.size() == 1); + auto& row = rows.front(); + REQUIRE(row.size() == 100); + std::vector expectedValue(100); + std::fill(expectedValue.begin(), expectedValue.end(), 0); + REQUIRE(row == expectedValue); + } +} + +#if SQLITE_VERSION_NUMBER >= 3007016 +TEST_CASE("char") { + auto storage = make_storage({}); + auto rows = storage.select(char_(67, 72, 65, 82)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "CHAR"); +} +#endif + +TEST_CASE("rtrim") { + auto storage = make_storage({}); + auto rows = storage.select(rtrim("ototo ")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "ototo"); + + rows = storage.select(rtrim("ototo ", " ")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "ototo"); +} + +TEST_CASE("ltrim") { + auto storage = make_storage({}); + auto rows = storage.select(ltrim(" ototo")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "ototo"); + + rows = storage.select(ltrim(" ototo", " ")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "ototo"); +} + +TEST_CASE("trim") { + auto storage = make_storage({}); + auto rows = storage.select(trim(" ototo ")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "ototo"); + + rows = storage.select(trim(" ototo ", " ")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "ototo"); +} + +TEST_CASE("upper") { + auto storage = make_storage({}); + auto rows = storage.select(upper("ototo")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "OTOTO"); +} + +TEST_CASE("lower") { + auto storage = make_storage({}); + auto rows = storage.select(lower("OTOTO")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "ototo"); +} + +TEST_CASE("length") { + auto storage = make_storage({}); + auto rows = storage.select(length("ototo")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == 5); +} + +TEST_CASE("abs") { + auto storage = make_storage({}); + auto rows = storage.select(sqlite_orm::abs(-10)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front()); + REQUIRE(*rows.front() == 10); +} + +TEST_CASE("hex") { + auto storage = make_storage({}); + { + auto rows = storage.select(hex(67)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "3637"); + } + { + auto rows = storage.select(hex("ä")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "C3A4"); + } + { + auto rows = storage.select(hex(nullptr)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == std::string()); + } +} + +TEST_CASE("quote") { + using Catch::Matchers::UnorderedEquals; + struct Department { + int id = 0; + std::string name; + int managerId = 0; + int locationId = 0; + }; + auto storage = make_storage({}, + make_table("departments", + make_column("department_id", &Department::id, primary_key()), + make_column("department_name", &Department::name), + make_column("manager_id", &Department::managerId), + make_column("location_id", &Department::locationId))); + storage.sync_schema(); + storage.replace(Department{10, "Administration", 200, 1700}); + storage.replace(Department{20, "Marketing", 201, 1800}); + storage.replace(Department{30, "Purchasing", 114, 1700}); + storage.replace(Department{40, "Human Resources", 203, 2400}); + storage.replace(Department{50, "Shipping", 121, 1500}); + storage.replace(Department{60, "IT", 103, 1400}); + storage.replace(Department{70, "Public Relation", 204, 2700}); + storage.replace(Department{80, "Sales", 145, 2500}); + storage.replace(Department{90, "Executive", 100, 1700}); + storage.replace(Department{100, "Finance", 108, 1700}); + storage.replace(Department{110, "Accounting", 205, 1700}); + storage.replace(Department{120, "Treasury", 0, 1700}); + storage.replace(Department{130, "Corporate Tax", 0, 1700}); + storage.replace(Department{140, "Control And Cre", 0, 1700}); + storage.replace(Department{150, "Shareholder Ser", 0, 1700}); + storage.replace(Department{160, "Benefits", 0, 1700}); + storage.replace(Department{170, "Manufacturing", 0, 1700}); + storage.replace(Department{180, "Construction", 0, 1700}); + storage.replace(Department{190, "Contracting", 0, 1700}); + storage.replace(Department{200, "Operations", 0, 1700}); + storage.replace(Department{210, "IT Support", 0, 1700}); + storage.replace(Department{220, "NOC", 0, 1700}); + storage.replace(Department{230, "IT Helpdesk", 0, 1700}); + storage.replace(Department{240, "Government Sale", 0, 1700}); + storage.replace(Department{250, "Retail Sales", 0, 1700}); + storage.replace(Department{260, "Recruiting", 0, 1700}); + storage.replace(Department{270, "Payroll", 0, 1700}); + { + auto rows = storage.select(quote("hi")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "'hi'"); + } + { + auto rows = + storage.select(columns(&Department::name, quote(&Department::name)), where(c(&Department::id) > 150)); + std::vector> expected; + expected.push_back(std::make_tuple("Benefits", "'Benefits'")); + expected.push_back(std::make_tuple("Manufacturing", "'Manufacturing'")); + expected.push_back(std::make_tuple("Construction", "'Construction'")); + expected.push_back(std::make_tuple("Contracting", "'Contracting'")); + expected.push_back(std::make_tuple("Operations", "'Operations'")); + expected.push_back(std::make_tuple("IT Support", "'IT Support'")); + expected.push_back(std::make_tuple("NOC", "'NOC'")); + expected.push_back(std::make_tuple("IT Helpdesk", "'IT Helpdesk'")); + expected.push_back(std::make_tuple("Government Sale", "'Government Sale'")); + expected.push_back(std::make_tuple("Retail Sales", "'Retail Sales'")); + expected.push_back(std::make_tuple("Recruiting", "'Recruiting'")); + expected.push_back(std::make_tuple("Payroll", "'Payroll'")); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } +} + +TEST_CASE("randomblob") { + auto storage = make_storage({}); + for(auto i = 0; i < 20; ++i) { + auto blobLength = i + 1; + auto rows = storage.select(randomblob(blobLength)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front().size() == size_t(blobLength)); + } +} + +TEST_CASE("instr") { + using Catch::Matchers::UnorderedEquals; + + struct Employee { + int id = 0; + std::string firstName; + std::string lastName; + std::string address; + }; + + struct sw : alias_tag { + static const std::string& get() { + static const std::string res = "sw"; + return res; + } + }; + auto storage = make_storage({}, + make_table("employees", + make_column("id", &Employee::id, primary_key()), + make_column("first_name", &Employee::firstName), + make_column("last_name", &Employee::lastName), + make_column("address", &Employee::address))); + storage.sync_schema(); + { + auto rows = storage.select(instr("SQLite Tutorial", "Tutorial")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == 8); + } + { + auto rows = storage.select(instr("SQLite Tutorial", "I")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == 0); + } + Employee nancy{1, "Nancy", "Edwards", "825 8 Ave SW"}; + Employee jane{2, "Jane", "Peacock", "1111 6 Ave SW"}; + Employee margaret{3, "Margaret", "Park", "683 10 Street SW"}; + Employee patrick{4, "Patrick", "Jane", "Sacramento Empty House"}; + Employee teresa{5, "Terese", "Lisbon", "Secramento Middle of Nowhere"}; + storage.replace(nancy); + storage.replace(jane); + storage.replace(margaret); + storage.replace(patrick); + storage.replace(teresa); + { + auto rows = storage.select( + columns(&Employee::lastName, &Employee::firstName, &Employee::address, instr(&Employee::address, "SW"))); + std::vector> expected; + expected.push_back(std::make_tuple(nancy.lastName, nancy.firstName, nancy.address, 11)); + expected.push_back(std::make_tuple(jane.lastName, jane.firstName, jane.address, 12)); + expected.push_back(std::make_tuple(margaret.lastName, margaret.firstName, margaret.address, 15)); + expected.push_back(std::make_tuple(patrick.lastName, patrick.firstName, patrick.address, 0)); + expected.push_back(std::make_tuple(teresa.lastName, teresa.firstName, teresa.address, 0)); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + { + auto rows = storage.select(columns(&Employee::lastName, + &Employee::firstName, + &Employee::address, + as(instr(&Employee::address, "SW"))), + where(greater_than(get(), 0))); + std::vector> expected; + expected.push_back(std::make_tuple(nancy.lastName, nancy.firstName, nancy.address, 11)); + expected.push_back(std::make_tuple(jane.lastName, jane.firstName, jane.address, 12)); + expected.push_back(std::make_tuple(margaret.lastName, margaret.firstName, margaret.address, 15)); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } +} + +namespace replace_func_local { + struct Contact { + int id = 0; + std::string firstName; + std::string lastName; + std::string phone; + }; + + bool operator==(const Contact& lhs, const Contact& rhs) { + return lhs.id == rhs.id && lhs.firstName == rhs.firstName && lhs.lastName == rhs.lastName && + lhs.phone == rhs.phone; + } +} + +TEST_CASE("replace func") { + using Catch::Matchers::UnorderedEquals; + using namespace replace_func_local; + + auto storage = make_storage({}, + make_table("contacts", + make_column("contact_id", &Contact::id, primary_key()), + make_column("first_name", &Contact::firstName), + make_column("last_name", &Contact::lastName), + make_column("phone", &Contact::phone))); + storage.sync_schema(); + { + auto rows = storage.select(replace("AA B CC AAA", "A", "Z")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "ZZ B CC ZZZ"); + } + { + auto rows = storage.select(replace("This is a cat", "This", "That")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "That is a cat"); + } + Contact john{0, "John", "Doe", "410-555-0168"}; + Contact lily{0, "Lily", "Bush", "410-444-9862"}; + john.id = storage.insert(john); + lily.id = storage.insert(lily); + { + auto contacts = storage.get_all(); + std::vector expected; + expected.push_back(john); + expected.push_back(lily); + REQUIRE_THAT(contacts, UnorderedEquals(expected)); + } + storage.update_all(set(c(&Contact::phone) = replace(&Contact::phone, "410", "+1-410"))); + { + auto contacts = storage.get_all(); + john.phone = "+1-410-555-0168"; + lily.phone = "+1-410-444-9862"; + std::vector expected; + expected.push_back(john); + expected.push_back(lily); + REQUIRE_THAT(contacts, UnorderedEquals(expected)); + } +} + +TEST_CASE("round") { + auto storage = make_storage({}); + auto test = [&storage](auto input, double expected) { + auto rows = storage.select(round(input)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == expected); + }; + auto test2 = [&storage](auto inputA, auto inputB, double expected) { + auto rows = storage.select(round(inputA, inputB)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == expected); + }; + test(23.4, 23.0); + test(23.6, 24.0); + test2(23.6985, 2, 23.7); + test2(190.3985, 3, 190.399); + test2(99.9, 0, 100.0); + test2(23.3985, nullptr, 0); // maybe this is an error but noone cares AFAIK + test2(1304.67, -1, 1305.0); + test2(1929.236, 2, 1929.24); + test2(1929.236, 1, 1929.2); + test(1929.236, 1929); + test(0.5, 1); + test2(59.9, 0, 60.0); + test2(-59.9, 0, -60.0); + test2(-4.535, 2, -4.54); + test2(34.4158, -1, 34.0); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +TEST_CASE("coalesce") { + struct Foo { + double field; + }; + + auto storage = make_storage({}, make_table("foo", make_column("field", &Foo::field))); + storage.sync_schema(); + SECTION("statement") { + SECTION("nullptr") { + auto statement = storage.prepare(select(coalesce>(&Foo::field, nullptr))); + std::ignore = statement; + } + SECTION("nullopt") { + auto statement = storage.prepare(select(coalesce>(&Foo::field, std::nullopt))); + std::ignore = statement; + } + } + SECTION("straight") { + SECTION("nullptr") { + storage.select(coalesce>(&Foo::field, nullptr)); + } + SECTION("nullopt") { + storage.select(coalesce>(&Foo::field, std::nullopt)); + } + } +} +#endif + +TEST_CASE("ifnull") { + // obtained from here https://www.sqlitetutorial.net/sqlite-functions/sqlite-ifnull/ + + using Catch::Matchers::UnorderedEquals; + + struct Customer { + int id = 0; + std::string firstName; + std::string lastName; + std::string company; + std::string address; + std::string city; + std::string state; + std::string country; + std::string postalCode; + std::string phone; + std::unique_ptr fax; + std::string email; + int supportRepId = 0; + }; + auto storage = make_storage({}, + make_table("customers", + make_column("CustomerId", &Customer::id, primary_key()), + make_column("FirstName", &Customer::firstName), + make_column("LastName", &Customer::lastName), + make_column("Company", &Customer::company), + make_column("Address", &Customer::address), + make_column("City", &Customer::city), + make_column("State", &Customer::state), + make_column("Country", &Customer::country), + make_column("PostalCode", &Customer::postalCode), + make_column("Phone", &Customer::phone), + make_column("Fax", &Customer::fax), + make_column("Email", &Customer::email), + make_column("SupportRepId", &Customer::supportRepId))); + storage.sync_schema(); + + storage.replace(Customer{1, + "Luís", + "Gonçalves", + "Embraer - Empresa Brasileira de Aeronáutica S.A.", + "Av. Brigadeiro Faria Lima, 2170", + "São José dos Campos", + "SP", + "Brazil", + "12227-000", + "+55 (12) 3923-5555", + std::make_unique("+55 (12) 3923-5566"), + "luisg@embraer.com.br", + 3}); + storage.replace(Customer{2, + "Leonie", + "Köhler", + "", + "Theodor-Heuss-Straße 34", + "Stuttgart", + "", + "Germany", + "70174", + "+49 0711 2842222", + nullptr, + "leonekohler@surfeu.de", + 5}); + storage.replace(Customer{3, + "François", + "Tremblay", + "", + "1498 rue Bélanger", + "Montréal", + "QC", + "Canada", + "H2G 1A7", + "+1 (514) 721-4711", + nullptr, + "ftremblay@gmail.com", + 3}); + storage.replace(Customer{4, + "Bjørn", + "Hansen", + "", + "Ullevålsveien 14", + "Oslo", + "", + "Norway", + "0171", + "+47 22 44 22 22", + nullptr, + "bjorn.hansen@yahoo.no", + 4}); + storage.replace(Customer{5, + "František", + "Wichterlová", + "JetBrains s.r.o.", + "Klanova 9/506", + "Prague", + "", + "Czech Republic", + "14700", + "+420 2 4172 5555", + std::make_unique("+420 2 4172 5555"), + "frantisekw@jetbrains.com", + 4}); + storage.replace(Customer{6, + "Helena", + "Holý", + "", + "Rilská 3174/6", + "Prague", + "", + "Czech Republic", + "14300", + "+420 2 4177 0449", + nullptr, + "hholy@gmail.com", + 5}); + storage.replace(Customer{7, + "Astrid", + "Gruber", + "", + "Rotenturmstraße 4, 1010 Innere Stadt", + "Vienne", + "", + "Austria", + "1010", + "+43 01 5134505", + nullptr, + "astrid.gruber@apple.at", + 5}); + storage.replace(Customer{8, + "Daan", + "Peeters", + "", + "Grétrystraat 63", + "Brussels", + "", + "Belgium", + "1000", + "+32 02 219 03 03", + nullptr, + "daan_peeters@apple.be", + 4}); + storage.replace(Customer{9, + "Kara", + "Nielsen", + "", + "Sønder Boulevard 51", + "Copenhagen", + "", + "Denmark", + "1720", + "+453 3331 9991", + nullptr, + "kara.nielsen@jubii.dk", + 4}); + storage.replace(Customer{10, + "Eduardo", + "Martins", + "Woodstock Discos", + "Rua Dr. Falcão Filho, 155", + "São Paulo", + "SP", + "Brazil", + "01007-010", + "+55 (11) 3033-5446", + std::make_unique("+55 (11) 3033-4564"), + "eduardo@woodstock.com.br", + 4}); + storage.replace(Customer{11, + "Alexandre", + "Rocha", + "Banco do Brasil S.A.", + "Av. Paulista, 2022", + "São Paulo", + "SP", + "Brazil", + "01310-200", + "+55 (11) 3055-3278", + std::make_unique("+55 (11) 3055-8131"), + "alero@uol.com.br", + 5}); + storage.replace(Customer{12, + "Roberto", + "Almeida", + "Riotur", + "Praça Pio X, 119", + "Rio de Janeiro", + "RJ", + "Brazil", + "20040-020", + "+55 (21) 2271-7000", + std::make_unique("+55 (21) 2271-7070"), + "roberto.almeida@riotur.gov.br", + 3}); + storage.replace(Customer{13, + "Fernanda", + "Ramos", + "", + "Qe 7 Bloco G", + "Brasília", + "DF", + "Brazil", + "71020-677", + "+55 (61) 3363-5547", + std::make_unique("+55 (61) 3363-7855"), + "fernadaramos4@uol.com.br", + 4}); + storage.replace(Customer{14, + "Mark", + "Philips", + "Telus", + "8210 111 ST NW", + "Edmonton", + "AB", + "Canada", + "T6G 2C7", + "+1 (780) 434-4554", + std::make_unique("+1 (780) 434-5565"), + "mphilips12@shaw.ca", + 5}); + storage.replace(Customer{15, + "Jennifer", + "Peterson", + "Rogers Canada", + "700 W Pender Street", + "Vancouver", + "BC", + "Canada", + "V6C 1G8", + "+1 (604) 688-2255", + std::make_unique("+1 (604) 688-8756"), + "jenniferp@rogers.ca", + 3}); + storage.replace(Customer{16, + "Frank", + "Harris", + "Google Inc.", + "1600 Amphitheatre Parkway", + "Mountain View", + "CA", + "USA", + "94043-1351", + "+1 (650) 253-0000", + std::make_unique("+1 (650) 253-0000"), + "fharris@google.com", + 4}); + storage.replace(Customer{17, + "Jack", + "Smith", + "Microsoft Corporation", + "1 Microsoft Way", + "Redmond", + "WA", + "USA", + "98052-8300", + "+1 (425) 882-8080", + std::make_unique("+1 (425) 882-8081"), + "jacksmith@microsoft.com", + 5}); + storage.replace(Customer{18, + "Michelle", + "Brooks", + "", + "627 Broadway", + "New York", + "NY", + "USA", + "10012-2612", + "+1 (212) 221-3546", + std::make_unique("+1 (212) 221-4679"), + "michelleb@aol.com", + 3}); + storage.replace(Customer{19, + "Tim", + "Goyer", + "Apple Inc.", + "1 Infinite Loop", + "Cupertino", + "CA", + "USA", + "95014", + "+1 (408) 996-1010", + std::make_unique("+1 (408) 996-1011"), + "tgoyer@apple.com", + 3}); + storage.replace(Customer{20, + "Dan", + "Miller", + "", + "541 Del Medio Avenue", + "Mountain View", + "CA", + "USA", + "94040-111", + "+1 (650) 644-3358", + nullptr, + "dmiller@comcast.com", + 4}); + storage.replace(Customer{21, + "Kathy", + "Chase", + "", + "801 W 4th Street", + "Reno", + "NV", + "USA", + "89503", + "+1 (775) 223-7665", + nullptr, + "kachase@hotmail.com", + 5}); + storage.replace(Customer{22, + "Heather", + "Leacock", + "", + "120 S Orange Ave", + "Orlando", + "FL", + "USA", + "32801", + "+1 (407) 999-7788", + nullptr, + "hleacock@gmail.com", + 4}); + storage.replace(Customer{23, + "John", + "Gordon", + "", + "69 Salem Street", + "Boston", + "MA", + "USA", + "2113", + "+1 (617) 522-1333", + nullptr, + "johngordon22@yahoo.com", + 4}); + storage.replace(Customer{24, + "Frank", + "Ralston", + "", + "162 E Superior Street", + "Chicago", + "IL", + "USA", + "60611", + "+1 (312) 332-3232", + nullptr, + "fralston@gmail.com", + 3}); + storage.replace(Customer{25, + "Victor", + "Stevens", + "", + "319 N. Frances Street", + "Madison", + "WI", + "USA", + "53703", + "+1 (608) 257-0597", + nullptr, + "vstevens@yahoo.com", + 5}); + storage.replace(Customer{26, + "Richard", + "Cunningham", + "", + "2211 W Berry Street", + "Fort Worth", + "TX", + "USA", + "76110", + "+1 (817) 924-7272", + nullptr, + "ricunningham@hotmail.com", + 4}); + storage.replace(Customer{27, + "Patrick", + "Gray", + "", + "1033 N Park Ave", + "Tucson", + "AZ", + "USA", + "85719", + "+1 (520) 622-4200", + nullptr, + "patrick.gray@aol.com", + 4}); + storage.replace(Customer{28, + "Julia", + "Barnett", + "", + "302 S 700 E", + "Salt Lake City", + "UT", + "USA", + "84102", + "+1 (801) 531-7272", + nullptr, + "jubarnett@gmail.com", + 5}); + storage.replace(Customer{29, + "Robert", + "Brown", + "", + "796 Dundas Street West", + "Toronto", + "ON", + "Canada", + "M6J 1V1", + "+1 (416) 363-8888", + nullptr, + "robbrown@shaw.ca", + 3}); + storage.replace(Customer{30, + "Edward", + "Francis", + "", + "230 Elgin Street", + "Ottawa", + "ON", + "Canada", + "K2P 1L7", + "+1 (613) 234-3322", + nullptr, + "edfrancis@yachoo.ca", + 3}); + storage.replace(Customer{31, + "Martha", + "Silk", + "", + "194A Chain Lake Drive", + "Halifax", + "NS", + "Canada", + "B3S 1C5", + "+1 (902) 450-0450", + nullptr, + "marthasilk@gmail.com", + 5}); + storage.replace(Customer{32, + "Aaron", + "Mitchell", + "", + "696 Osborne Street", + "Winnipeg", + "MB", + "Canada", + "R3L 2B9", + "+1 (204) 452-6452", + nullptr, + "aaronmitchell@yahoo.ca", + 4}); + storage.replace(Customer{33, + "Ellie", + "Sullivan", + "", + "5112 48 Street", + "Yellowknife", + "NT", + "Canada", + "X1A 1N6", + "+1 (867) 920-2233", + nullptr, + "ellie.sullivan@shaw.ca", + 3}); + storage.replace(Customer{34, + "João", + "Fernandes", + "", + "Rua da Assunção 53", + "Lisbon", + "", + "Portugal", + "", + "+351 (213) 466-111", + nullptr, + "jfernandes@yahoo.pt", + 4}); + storage.replace(Customer{35, + "Madalena", + "Sampaio", + "", + "Rua dos Campeões Europeus de Viena, 4350", + "Porto", + "", + "Portugal", + "", + "+351 (225) 022-448", + nullptr, + "masampaio@sapo.pt", + 4}); + storage.replace(Customer{36, + "Hannah", + "Schneider", + "", + "Tauentzienstraße 8", + "Berlin", + "", + "Germany", + "10789", + "+49 030 26550280", + nullptr, + "hannah.schneider@yahoo.de", + 5}); + storage.replace(Customer{37, + "Fynn", + "Zimmermann", + "", + "Berger Straße 10", + "Frankfurt", + "", + "Germany", + "60316", + "+49 069 40598889", + nullptr, + "fzimmermann@yahoo.de", + 3}); + storage.replace(Customer{38, + "Niklas", + "Schröder", + "", + "Barbarossastraße 19", + "Berlin", + "", + "Germany", + "10779", + "+49 030 2141444", + nullptr, + "nschroder@surfeu.de", + 3}); + storage.replace(Customer{39, + "Camille", + "Bernard", + "", + "4, Rue Milton", + "Paris", + "", + "France", + "75009", + "+33 01 49 70 65 65", + nullptr, + "camille.bernard@yahoo.fr", + 4}); + storage.replace(Customer{40, + "Dominique", + "Lefebvre", + "", + "8, Rue Hanovre", + "Paris", + "", + "France", + "75002", + "+33 01 47 42 71 71", + nullptr, + "dominiquelefebvre@gmail.com", + 4}); + storage.replace(Customer{41, + "Marc", + "Dubois", + "", + "11, Place Bellecour", + "Lyon", + "", + "France", + "69002", + "+33 04 78 30 30 30", + nullptr, + "marc.dubois@hotmail.com", + 5}); + storage.replace(Customer{42, + "Wyatt", + "Girard", + "", + "9, Place Louis Barthou", + "Bordeaux", + "", + "France", + "33000", + "+33 05 56 96 96 96", + nullptr, + "wyatt.girard@yahoo.fr", + 3}); + storage.replace(Customer{43, + "Isabelle", + "Mercier", + "", + "68, Rue Jouvence", + "Dijon", + "", + "France", + "21000", + "+33 03 80 73 66 99", + nullptr, + "isabelle_mercier@apple.fr", + 3}); + storage.replace(Customer{44, + "Terhi", + "Hämäläinen", + "", + "Porthaninkatu 9", + "Helsinki", + "", + "Finland", + "00530", + "+358 09 870 2000", + nullptr, + "terhi.hamalainen@apple.fi", + 3}); + storage.replace(Customer{45, + "Ladislav", + "Kovács", + "", + "Erzsébet krt. 58.", + "Budapest", + "", + "Hungary", + "H-1073", + "", + nullptr, + "ladislav_kovacs@apple.hu", + 3}); + storage.replace(Customer{46, + "Hugh", + "O'Reilly", + "", + "3 Chatham Street", + "Dublin", + "Dublin", + "Ireland", + "", + "+353 01 6792424", + nullptr, + "hughoreilly@apple.ie", + 3}); + storage.replace(Customer{47, + "Lucas", + "Mancini", + "", + "Via Degli Scipioni, 43", + "Rome", + "RM", + "Italy", + "00192", + "+39 06 39733434", + nullptr, + "lucas.mancini@yahoo.it", + 5}); + storage.replace(Customer{48, + "Johannes", + "Van der Berg", + "", + "Lijnbaansgracht 120bg", + "Amsterdam", + "VV", + "Netherlands", + "1016", + "+31 020 6223130", + nullptr, + "johavanderberg@yahoo.nl", + 5}); + storage.replace(Customer{49, + "Stanisław", + "Wójcik", + "", + "Ordynacka 10", + "Warsaw", + "", + "Poland", + "00-358", + "+48 22 828 37 39", + nullptr, + "stanisław.wójcik@wp.pl", + 4}); + storage.replace(Customer{50, + "Enrique", + "Muñoz", + "", + "C/ San Bernardo 85", + "Madrid", + "", + "Spain", + "28015", + "+34 914 454 454", + nullptr, + "enrique_munoz@yahoo.es", + 5}); + storage.replace(Customer{51, + "Joakim", + "Johansson", + "", + "Celsiusg. 9", + "Stockholm", + "", + "Sweden", + "11230", + "+46 08-651 52 52", + nullptr, + "joakim.johansson@yahoo.se", + 5}); + storage.replace(Customer{52, + "Emma", + "Jones", + "", + "202 Hoxton Street", + "London", + "", + "United Kingdom", + "N1 5LH", + "+44 020 7707 0707", + nullptr, + "emma_jones@hotmail.com", + 3}); + storage.replace(Customer{53, + "Phil", + "Hughes", + "", + "113 Lupus St", + "London", + "", + "United Kingdom", + "SW1V 3EN", + "+44 020 7976 5722", + nullptr, + "phil.hughes@gmail.com", + 3}); + storage.replace(Customer{54, + "Steve", + "Murray", + "", + "110 Raeburn Pl", + "Edinburgh ", + "", + "United Kingdom", + "EH4 1HH", + "+44 0131 315 3300", + nullptr, + "steve.murray@yahoo.uk", + 5}); + storage.replace(Customer{55, + "Mark", + "Taylor", + "", + "421 Bourke Street", + "Sidney", + "NSW", + "Australia", + "2010", + "+61 (02) 9332 3633", + nullptr, + "mark.taylor@yahoo.au", + 4}); + storage.replace(Customer{56, + "Diego", + "Gutiérrez", + "", + "307 Macacha Güemes", + "Buenos Aires", + "", + "Argentina", + "1106", + "+54 (0)11 4311 4333", + nullptr, + "diego.gutierrez@yahoo.ar", + 4}); + storage.replace(Customer{57, + "Luis", + "Rojas", + "", + "Calle Lira, 198", + "Santiago", + "", + "Chile", + "", + "+56 (0)2 635 4444", + nullptr, + "luisrojas@yahoo.cl", + 5}); + storage.replace(Customer{58, + "Manoj", + "Pareek", + "", + "12,Community Centre", + "Delhi", + "", + "India", + "110017", + "+91 0124 39883988", + nullptr, + "manoj.pareek@rediff.com", + 3}); + storage.replace(Customer{59, + "Puja", + "Srivastava", + "", + "3,Raj Bhavan Road", + "Bangalore", + "", + "India", + "560001", + "+91 080 22289999", + nullptr, + "puja_srivastava@yahoo.in", + 3}); + + auto rows = storage.select(columns(&Customer::firstName, + &Customer::lastName, + ifnull(&Customer::fax, "Call:" || c(&Customer::phone))), + order_by(&Customer::firstName)); + decltype(rows) expected; + expected.reserve(rows.size()); + expected.push_back({"Aaron", "Mitchell", "Call:+1 (204) 452-6452"}); + expected.push_back({"Alexandre", "Rocha", "+55 (11) 3055-8131"}); + expected.push_back({"Astrid", "Gruber", "Call:+43 01 5134505"}); + expected.push_back({"Bjørn", "Hansen", "Call:+47 22 44 22 22"}); + expected.push_back({"Camille", "Bernard", "Call:+33 01 49 70 65 65"}); + expected.push_back({"Daan", "Peeters", "Call:+32 02 219 03 03"}); + expected.push_back({"Dan", "Miller", "Call:+1 (650) 644-3358"}); + expected.push_back({"Diego", "Gutiérrez", "Call:+54 (0)11 4311 4333"}); + expected.push_back({"Dominique", "Lefebvre", "Call:+33 01 47 42 71 71"}); + expected.push_back({"Eduardo", "Martins", "+55 (11) 3033-4564"}); + expected.push_back({"Edward", "Francis", "Call:+1 (613) 234-3322"}); + expected.push_back({"Ellie", "Sullivan", "Call:+1 (867) 920-2233"}); + expected.push_back({"Emma", "Jones", "Call:+44 020 7707 0707"}); + expected.push_back({"Enrique", "Muñoz", "Call:+34 914 454 454"}); + expected.push_back({"Fernanda", "Ramos", "+55 (61) 3363-7855"}); + expected.push_back({"Frank", "Harris", "+1 (650) 253-0000"}); + expected.push_back({"Frank", "Ralston", "Call:+1 (312) 332-3232"}); + expected.push_back({"František", "Wichterlová", "+420 2 4172 5555"}); + expected.push_back({"François", "Tremblay", "Call:+1 (514) 721-4711"}); + expected.push_back({"Fynn", "Zimmermann", "Call:+49 069 40598889"}); + expected.push_back({"Hannah", "Schneider", "Call:+49 030 26550280"}); + expected.push_back({"Heather", "Leacock", "Call:+1 (407) 999-7788"}); + expected.push_back({"Helena", "Holý", "Call:+420 2 4177 0449"}); + expected.push_back({"Hugh", "O'Reilly", "Call:+353 01 6792424"}); + expected.push_back({"Isabelle", "Mercier", "Call:+33 03 80 73 66 99"}); + expected.push_back({"Jack", "Smith", "+1 (425) 882-8081"}); + expected.push_back({"Jennifer", "Peterson", "+1 (604) 688-8756"}); + expected.push_back({"Joakim", "Johansson", "Call:+46 08-651 52 52"}); + expected.push_back({"Johannes", "Van der Berg", "Call:+31 020 6223130"}); + expected.push_back({"John", "Gordon", "Call:+1 (617) 522-1333"}); + expected.push_back({"João", "Fernandes", "Call:+351 (213) 466-111"}); + expected.push_back({"Julia", "Barnett", "Call:+1 (801) 531-7272"}); + expected.push_back({"Kara", "Nielsen", "Call:+453 3331 9991"}); + expected.push_back({"Kathy", "Chase", "Call:+1 (775) 223-7665"}); + expected.push_back({"Ladislav", "Kovács", "Call:"}); + expected.push_back({"Leonie", "Köhler", "Call:+49 0711 2842222"}); + expected.push_back({"Lucas", "Mancini", "Call:+39 06 39733434"}); + expected.push_back({"Luis", "Rojas", "Call:+56 (0)2 635 4444"}); + expected.push_back({"Luís", "Gonçalves", "+55 (12) 3923-5566"}); + expected.push_back({"Madalena", "Sampaio", "Call:+351 (225) 022-448"}); + expected.push_back({"Manoj", "Pareek", "Call:+91 0124 39883988"}); + expected.push_back({"Marc", "Dubois", "Call:+33 04 78 30 30 30"}); + expected.push_back({"Mark", "Philips", "+1 (780) 434-5565"}); + expected.push_back({"Mark", "Taylor", "Call:+61 (02) 9332 3633"}); + expected.push_back({"Martha", "Silk", "Call:+1 (902) 450-0450"}); + expected.push_back({"Michelle", "Brooks", "+1 (212) 221-4679"}); + expected.push_back({"Niklas", "Schröder", "Call:+49 030 2141444"}); + expected.push_back({"Patrick", "Gray", "Call:+1 (520) 622-4200"}); + expected.push_back({"Phil", "Hughes", "Call:+44 020 7976 5722"}); + expected.push_back({"Puja", "Srivastava", "Call:+91 080 22289999"}); + expected.push_back({"Richard", "Cunningham", "Call:+1 (817) 924-7272"}); + expected.push_back({"Robert", "Brown", "Call:+1 (416) 363-8888"}); + expected.push_back({"Roberto", "Almeida", "+55 (21) 2271-7070"}); + expected.push_back({"Stanisław", "Wójcik", "Call:+48 22 828 37 39"}); + expected.push_back({"Steve", "Murray", "Call:+44 0131 315 3300"}); + expected.push_back({"Terhi", "Hämäläinen", "Call:+358 09 870 2000"}); + expected.push_back({"Tim", "Goyer", "+1 (408) 996-1011"}); + expected.push_back({"Victor", "Stevens", "Call:+1 (608) 257-0597"}); + expected.push_back({"Wyatt", "Girard", "Call:+33 05 56 96 96 96"}); + REQUIRE_THAT(rows, UnorderedEquals(expected)); +} diff --git a/tests/datetime_function_tests.cpp b/tests/built_in_functions_tests/datetime_function_tests.cpp similarity index 96% rename from tests/datetime_function_tests.cpp rename to tests/built_in_functions_tests/datetime_function_tests.cpp index f360c3204..4cc113e15 100644 --- a/tests/datetime_function_tests.cpp +++ b/tests/built_in_functions_tests/datetime_function_tests.cpp @@ -24,7 +24,7 @@ TEST_CASE("julianday") { auto storage = make_storage({}, make_table("test", make_column("text", &Test::text))); storage.sync_schema(); - auto singleTestCase = [&storage](const std::string &arg, double expected) { + auto singleTestCase = [&storage](const std::string& arg, double expected) { { auto rows = storage.select(julianday(arg)); REQUIRE(rows.size() == 1); diff --git a/tests/built_in_functions_tests/math_functions.cpp b/tests/built_in_functions_tests/math_functions.cpp new file mode 100644 index 000000000..7c76d2366 --- /dev/null +++ b/tests/built_in_functions_tests/math_functions.cpp @@ -0,0 +1,464 @@ +#include +#include + +using namespace sqlite_orm; + +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + +constexpr double Epsilon = 0.0001; +static bool is_double_eq(double a, double b, double epsilon) { + return ((a - b) < epsilon) && ((b - a) < epsilon); +} + +TEST_CASE("math functions") { + using namespace std::placeholders; + auto storage = make_storage(""); + auto doubleComparator = std::bind(is_double_eq, _1, _2, Epsilon); + auto optionalComparator = [](const std::optional &lhs, const std::optional &rhs) { + if(lhs.has_value() && rhs.has_value()) { + return is_double_eq(*lhs, *rhs, Epsilon); + } else if(!lhs.has_value() && !rhs.has_value()) { + return true; + } else { + return false; + } + }; + SECTION("acos"){SECTION("simple"){auto rows = storage.select(sqlite_orm::acos(1)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::acos>(1)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("acosh"){SECTION("simple"){auto rows = storage.select(sqlite_orm::acosh(1)); +decltype(rows) expected; +expected.push_back(0); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::acosh>(1)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("asin"){SECTION("simple"){auto rows = storage.select(sqlite_orm::asin(0)); +decltype(rows) expected; +expected.push_back(0); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::asin>(0)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("asinh"){SECTION("simple"){auto rows = storage.select(sqlite_orm::asinh(0)); +decltype(rows) expected; +expected.push_back(0); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::asinh>(0)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("atan"){SECTION("simple"){auto rows = storage.select(sqlite_orm::atan(0)); +decltype(rows) expected; +expected.push_back(0); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::atan>(0)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("atan2"){SECTION("simple"){auto rows = storage.select(sqlite_orm::atan2(0, 1)); +decltype(rows) expected; +expected.push_back(0); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::atan2>(0, 1)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("atanh"){SECTION("simple"){auto rows = storage.select(sqlite_orm::atanh(0)); +decltype(rows) expected; +expected.push_back(0); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::atanh>(0)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("ceil"){SECTION("simple"){auto rows = storage.select(sqlite_orm::ceil(0.5)); +decltype(rows) expected; +expected.push_back(1); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::ceil>(0.5)); + decltype(rows) expected; + expected.push_back(1); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("ceiling"){SECTION("simple"){auto rows = storage.select(sqlite_orm::ceiling(0.5)); +decltype(rows) expected; +expected.push_back(1); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::ceiling>(0.5)); + decltype(rows) expected; + expected.push_back(1); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("cos"){SECTION("simple"){auto rows = storage.select(sqlite_orm::cos(0)); +decltype(rows) expected; +expected.push_back(1); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::cos>(0)); + decltype(rows) expected; + expected.push_back(1); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("cosh"){SECTION("simple"){auto rows = storage.select(sqlite_orm::cosh(0)); +decltype(rows) expected; +expected.push_back(1); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::cosh>(0)); + decltype(rows) expected; + expected.push_back(1); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("degrees"){SECTION("simple"){auto rows = storage.select(sqlite_orm::degrees(0)); +decltype(rows) expected; +expected.push_back(0); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::degrees>(0)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("exp"){SECTION("simple"){auto rows = storage.select(sqlite_orm::exp(0)); +decltype(rows) expected; +expected.push_back(1); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::exp>(0)); + decltype(rows) expected; + expected.push_back(1); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("floor"){SECTION("simple"){auto rows = storage.select(sqlite_orm::floor(1.5)); +decltype(rows) expected; +expected.push_back(1); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::floor>(1.5)); + decltype(rows) expected; + expected.push_back(1); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("ln"){SECTION("simple"){auto rows = storage.select(sqlite_orm::ln(1)); +decltype(rows) expected; +expected.push_back(0); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::ln>(1)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("log(x)"){SECTION("simple"){auto rows = storage.select(sqlite_orm::log(10)); +decltype(rows) expected; +expected.push_back(1); +REQUIRE(std::equal(rows.begin(), rows.end(), expected.begin(), doubleComparator)); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::log>(10)); + decltype(rows) expected; + expected.push_back(1); + REQUIRE(std::equal(rows.begin(), rows.end(), expected.begin(), optionalComparator)); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("log10"){SECTION("simple"){auto rows = storage.select(sqlite_orm::log10(10)); +decltype(rows) expected; +expected.push_back(1); +REQUIRE(std::equal(rows.begin(), rows.end(), expected.begin(), doubleComparator)); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::log10>(10)); + decltype(rows) expected; + expected.push_back(1); + REQUIRE(std::equal(rows.begin(), rows.end(), expected.begin(), optionalComparator)); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("log(b, x)"){SECTION("simple"){auto rows = storage.select(sqlite_orm::log(25, 625)); +decltype(rows) expected; +expected.push_back(2); +REQUIRE(std::equal(rows.begin(), rows.end(), expected.begin(), doubleComparator)); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::log>(25, 625)); + decltype(rows) expected; + expected.push_back(2); + REQUIRE(std::equal(rows.begin(), rows.end(), expected.begin(), optionalComparator)); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("log2"){SECTION("simple"){auto rows = storage.select(sqlite_orm::log2(4)); +decltype(rows) expected; +expected.push_back(2); +REQUIRE(std::equal(rows.begin(), rows.end(), expected.begin(), doubleComparator)); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::log2>(4)); + decltype(rows) expected; + expected.push_back(2); + REQUIRE(std::equal(rows.begin(), rows.end(), expected.begin(), optionalComparator)); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("mod"){SECTION("simple"){auto rows = storage.select(sqlite_orm::mod_f(6, 5)); +decltype(rows) expected; +expected.push_back(1); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::mod_f>(6, 5)); + decltype(rows) expected; + expected.push_back(1); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("pi"){SECTION("simple"){auto rows = storage.select(sqlite_orm::pi()); +decltype(rows) expected; +expected.push_back(3.141592654); +REQUIRE(std::equal(rows.begin(), rows.end(), expected.begin(), doubleComparator)); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::pi>()); + decltype(rows) expected; + expected.push_back(3.141592654); + REQUIRE(std::equal(rows.begin(), rows.end(), expected.begin(), optionalComparator)); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("pow"){SECTION("simple"){auto rows = storage.select(sqlite_orm::pow(2, 3)); +decltype(rows) expected; +expected.push_back(8); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::pow>(2, 3)); + decltype(rows) expected; + expected.push_back(8); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("power"){SECTION("simple"){auto rows = storage.select(sqlite_orm::power(2, 3)); +decltype(rows) expected; +expected.push_back(8); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::power>(2, 3)); + decltype(rows) expected; + expected.push_back(8); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("radians"){SECTION("simple"){auto rows = storage.select(sqlite_orm::radians(0)); +decltype(rows) expected; +expected.push_back(0); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::radians>(0)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("sin"){SECTION("simple"){auto rows = storage.select(sqlite_orm::sin(0)); +decltype(rows) expected; +expected.push_back(0); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::sin>(0)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("sinh"){SECTION("simple"){auto rows = storage.select(sqlite_orm::sinh(0)); +decltype(rows) expected; +expected.push_back(0); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::sinh>(0)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("sqrt"){SECTION("simple"){auto rows = storage.select(sqlite_orm::sqrt(1)); +decltype(rows) expected; +expected.push_back(1); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::sqrt>(1)); + decltype(rows) expected; + expected.push_back(1); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("tan"){SECTION("simple"){auto rows = storage.select(sqlite_orm::tan(0)); +decltype(rows) expected; +expected.push_back(0); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::tan>(0)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("tanh"){SECTION("simple"){auto rows = storage.select(sqlite_orm::tanh(0)); +decltype(rows) expected; +expected.push_back(0); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::tanh>(0)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("tanh"){SECTION("simple"){auto rows = storage.select(sqlite_orm::tanh(0)); +decltype(rows) expected; +expected.push_back(0); +REQUIRE(rows == expected); +} +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::tanh>(0)); + decltype(rows) expected; + expected.push_back(0); + REQUIRE(rows == expected); +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +SECTION("trunc") { + SECTION("simple") { + auto rows = storage.select(sqlite_orm::trunc(1.5)); + decltype(rows) expected; + expected.push_back(1); + REQUIRE(rows == expected); + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + SECTION("explicit type") { + auto rows = storage.select(sqlite_orm::trunc>(1.5)); + decltype(rows) expected; + expected.push_back(1); + REQUIRE(rows == expected); + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +} + +#endif // SQLITE_ENABLE_MATH_FUNCTIONS diff --git a/tests/constraints/default.cpp b/tests/constraints/default.cpp index 07617c34b..6658e8f73 100644 --- a/tests/constraints/default.cpp +++ b/tests/constraints/default.cpp @@ -12,7 +12,11 @@ TEST_CASE("Default value") { std::string email; }; - auto storage1 = make_storage("test_db.sqlite", + auto filename = "test_db.sqlite"; + + ::remove(filename); + + auto storage1 = make_storage(filename, make_table("User", make_column("Id", &User::userId, primary_key()), make_column("Name", &User::name), @@ -22,7 +26,7 @@ TEST_CASE("Default value") { auto emailColumn = make_column("Email", &User::email, default_value("example@email.com")); - auto storage2 = make_storage("test_db.sqlite", + auto storage2 = make_storage(filename, make_table("User", make_column("Id", &User::userId, primary_key()), make_column("Name", &User::name), @@ -33,7 +37,7 @@ TEST_CASE("Default value") { auto emailDefault = emailColumn.default_value(); REQUIRE(emailDefault); - auto &emailDefaultString = *emailDefault; + auto& emailDefaultString = *emailDefault; REQUIRE(emailDefaultString == "'example@email.com'"); } @@ -48,3 +52,22 @@ TEST_CASE("Default datetime") { make_column("timestamp", &Induction::time, default_value(datetime("now", "localtime"))))); storage.sync_schema(); } + +TEST_CASE("default value for string") { + struct Contact { + int id = 0; + std::string firstName; + std::string lastName; + std::string phone; + }; + + using namespace sqlite_orm; + auto storage = + make_storage({}, + make_table("contacts", + make_column("contact_id", &Contact::id, primary_key()), + make_column("first_name", &Contact::firstName, default_value("")), + make_column("last_name", &Contact::lastName, default_value("")), + make_column("phone", &Contact::phone))); + storage.sync_schema(); +} diff --git a/tests/constraints/foreign_key.cpp b/tests/constraints/foreign_key.cpp index 328e5ce5e..4fbd729d1 100644 --- a/tests/constraints/foreign_key.cpp +++ b/tests/constraints/foreign_key.cpp @@ -1,6 +1,8 @@ #include #include +#include // std::is_same + using namespace sqlite_orm; TEST_CASE("Foreign key") { @@ -36,6 +38,19 @@ TEST_CASE("Foreign key") { make_column("visited_at", &Visit::visited_at), make_column("mark", &Visit::mark), foreign_key(&Visit::location).references(&Location::id))); + { + using namespace internal::storage_traits; + + using Storage = decltype(storage); + static_assert(storage_foreign_keys_count::value == 1, ""); + static_assert(storage_foreign_keys_count::value == 0, ""); + + using LocationFks = storage_fk_references::type; + static_assert(std::is_same>::value, ""); + + using VisitFks = storage_fk_references::type; + static_assert(std::is_same>::value, ""); + } storage.sync_schema(); int fromDate = int(std::time(nullptr)); diff --git a/tests/constraints/unique.cpp b/tests/constraints/unique.cpp index f207d70c0..cba98d686 100644 --- a/tests/constraints/unique.cpp +++ b/tests/constraints/unique.cpp @@ -41,7 +41,7 @@ TEST_CASE("Unique") { try { storage.insert(Contact{0, "Johnny", "Doe", "john.doe@gmail.com"}); REQUIRE(false); - } catch(const std::system_error &e) { + } catch(const std::system_error& e) { //.. } catch(...) { REQUIRE(false); @@ -52,7 +52,7 @@ TEST_CASE("Unique") { try { storage.insert(Shape{0, "red", "green"}); REQUIRE(false); - } catch(const std::system_error &e) { + } catch(const std::system_error& e) { //.. } catch(...) { REQUIRE(false); diff --git a/tests/core_functions_tests.cpp b/tests/core_functions_tests.cpp deleted file mode 100644 index 0ea38cae0..000000000 --- a/tests/core_functions_tests.cpp +++ /dev/null @@ -1,395 +0,0 @@ -#include -#include - -using namespace sqlite_orm; - -TEST_CASE("substr") { - struct Test { - std::string text; - int x = 0; - int y = 0; - }; - auto storage = make_storage( - {}, - make_table("test", make_column("text", &Test::text), make_column("x", &Test::x), make_column("y", &Test::y))); - storage.sync_schema(); - - { - auto rows = storage.select(substr("SQLite substr", 8)); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "substr"); - } - { - storage.insert(Test{"SQLite substr", 8}); - REQUIRE(storage.count() == 1); - auto rows = storage.select(substr(&Test::text, &Test::x)); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "substr"); - } - { - auto rows = storage.select(substr("SQLite substr", 1, 6)); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "SQLite"); - } - { - - storage.remove_all(); - storage.insert(Test{"SQLite substr", 1, 6}); - REQUIRE(storage.count() == 1); - auto rows = storage.select(substr(&Test::text, &Test::x, &Test::y)); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "SQLite"); - } -} - -TEST_CASE("zeroblob") { - struct Test { - int value = 0; - }; - - auto storage = make_storage({}, make_table("test", make_column("value", &Test::value))); - storage.sync_schema(); - - { - auto rows = storage.select(zeroblob(10)); - REQUIRE(rows.size() == 1); - auto &row = rows.front(); - REQUIRE(row.size() == 10); - std::vector expectedValue(10); - std::fill(expectedValue.begin(), expectedValue.end(), 0); - REQUIRE(row == expectedValue); - } - { - storage.insert(Test{100}); - - auto rows = storage.select(zeroblob(&Test::value)); - REQUIRE(rows.size() == 1); - auto &row = rows.front(); - REQUIRE(row.size() == 100); - std::vector expectedValue(100); - std::fill(expectedValue.begin(), expectedValue.end(), 0); - REQUIRE(row == expectedValue); - } -} - -#if SQLITE_VERSION_NUMBER >= 3007016 -TEST_CASE("char") { - auto storage = make_storage({}); - auto rows = storage.select(char_(67, 72, 65, 82)); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "CHAR"); -} -#endif - -TEST_CASE("rtrim") { - auto storage = make_storage({}); - auto rows = storage.select(rtrim("ototo ")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "ototo"); - - rows = storage.select(rtrim("ototo ", " ")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "ototo"); -} - -TEST_CASE("ltrim") { - auto storage = make_storage({}); - auto rows = storage.select(ltrim(" ototo")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "ototo"); - - rows = storage.select(ltrim(" ototo", " ")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "ototo"); -} - -TEST_CASE("trim") { - auto storage = make_storage({}); - auto rows = storage.select(trim(" ototo ")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "ototo"); - - rows = storage.select(trim(" ototo ", " ")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "ototo"); -} - -TEST_CASE("upper") { - auto storage = make_storage({}); - auto rows = storage.select(upper("ototo")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "OTOTO"); -} - -TEST_CASE("lower") { - auto storage = make_storage({}); - auto rows = storage.select(lower("OTOTO")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "ototo"); -} - -TEST_CASE("length") { - auto storage = make_storage({}); - auto rows = storage.select(length("ototo")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == 5); -} - -TEST_CASE("abs") { - auto storage = make_storage({}); - auto rows = storage.select(sqlite_orm::abs(-10)); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front()); - REQUIRE(*rows.front() == 10); -} - -TEST_CASE("hex") { - auto storage = make_storage({}); - { - auto rows = storage.select(hex(67)); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "3637"); - } - { - auto rows = storage.select(hex("ä")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "C3A4"); - } - { - auto rows = storage.select(hex(nullptr)); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == std::string()); - } -} - -TEST_CASE("quote") { - using Catch::Matchers::UnorderedEquals; - struct Department { - int id = 0; - std::string name; - int managerId = 0; - int locationId = 0; - }; - auto storage = make_storage({}, - make_table("departments", - make_column("department_id", &Department::id, primary_key()), - make_column("department_name", &Department::name), - make_column("manager_id", &Department::managerId), - make_column("location_id", &Department::locationId))); - storage.sync_schema(); - storage.replace(Department{10, "Administration", 200, 1700}); - storage.replace(Department{20, "Marketing", 201, 1800}); - storage.replace(Department{30, "Purchasing", 114, 1700}); - storage.replace(Department{40, "Human Resources", 203, 2400}); - storage.replace(Department{50, "Shipping", 121, 1500}); - storage.replace(Department{60, "IT", 103, 1400}); - storage.replace(Department{70, "Public Relation", 204, 2700}); - storage.replace(Department{80, "Sales", 145, 2500}); - storage.replace(Department{90, "Executive", 100, 1700}); - storage.replace(Department{100, "Finance", 108, 1700}); - storage.replace(Department{110, "Accounting", 205, 1700}); - storage.replace(Department{120, "Treasury", 0, 1700}); - storage.replace(Department{130, "Corporate Tax", 0, 1700}); - storage.replace(Department{140, "Control And Cre", 0, 1700}); - storage.replace(Department{150, "Shareholder Ser", 0, 1700}); - storage.replace(Department{160, "Benefits", 0, 1700}); - storage.replace(Department{170, "Manufacturing", 0, 1700}); - storage.replace(Department{180, "Construction", 0, 1700}); - storage.replace(Department{190, "Contracting", 0, 1700}); - storage.replace(Department{200, "Operations", 0, 1700}); - storage.replace(Department{210, "IT Support", 0, 1700}); - storage.replace(Department{220, "NOC", 0, 1700}); - storage.replace(Department{230, "IT Helpdesk", 0, 1700}); - storage.replace(Department{240, "Government Sale", 0, 1700}); - storage.replace(Department{250, "Retail Sales", 0, 1700}); - storage.replace(Department{260, "Recruiting", 0, 1700}); - storage.replace(Department{270, "Payroll", 0, 1700}); - { - auto rows = storage.select(quote("hi")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "'hi'"); - } - { - auto rows = - storage.select(columns(&Department::name, quote(&Department::name)), where(c(&Department::id) > 150)); - std::vector> expected; - expected.push_back(std::make_tuple("Benefits", "'Benefits'")); - expected.push_back(std::make_tuple("Manufacturing", "'Manufacturing'")); - expected.push_back(std::make_tuple("Construction", "'Construction'")); - expected.push_back(std::make_tuple("Contracting", "'Contracting'")); - expected.push_back(std::make_tuple("Operations", "'Operations'")); - expected.push_back(std::make_tuple("IT Support", "'IT Support'")); - expected.push_back(std::make_tuple("NOC", "'NOC'")); - expected.push_back(std::make_tuple("IT Helpdesk", "'IT Helpdesk'")); - expected.push_back(std::make_tuple("Government Sale", "'Government Sale'")); - expected.push_back(std::make_tuple("Retail Sales", "'Retail Sales'")); - expected.push_back(std::make_tuple("Recruiting", "'Recruiting'")); - expected.push_back(std::make_tuple("Payroll", "'Payroll'")); - REQUIRE_THAT(rows, UnorderedEquals(expected)); - } -} - -TEST_CASE("randomblob") { - auto storage = make_storage({}); - for(auto i = 0; i < 20; ++i) { - auto blobLength = i + 1; - auto rows = storage.select(randomblob(blobLength)); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front().size() == size_t(blobLength)); - } -} - -TEST_CASE("instr") { - using Catch::Matchers::UnorderedEquals; - - struct Employee { - int id = 0; - std::string firstName; - std::string lastName; - std::string address; - }; - - struct sw : alias_tag { - static const std::string &get() { - static const std::string res = "sw"; - return res; - } - }; - auto storage = make_storage({}, - make_table("employees", - make_column("id", &Employee::id, primary_key()), - make_column("first_name", &Employee::firstName), - make_column("last_name", &Employee::lastName), - make_column("address", &Employee::address))); - storage.sync_schema(); - { - auto rows = storage.select(instr("SQLite Tutorial", "Tutorial")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == 8); - } - { - auto rows = storage.select(instr("SQLite Tutorial", "I")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == 0); - } - Employee nancy{1, "Nancy", "Edwards", "825 8 Ave SW"}; - Employee jane{2, "Jane", "Peacock", "1111 6 Ave SW"}; - Employee margaret{3, "Margaret", "Park", "683 10 Street SW"}; - Employee patrick{4, "Patrick", "Jane", "Sacramento Empty House"}; - Employee teresa{5, "Terese", "Lisbon", "Secramento Middle of Nowhere"}; - storage.replace(nancy); - storage.replace(jane); - storage.replace(margaret); - storage.replace(patrick); - storage.replace(teresa); - { - auto rows = storage.select( - columns(&Employee::lastName, &Employee::firstName, &Employee::address, instr(&Employee::address, "SW"))); - std::vector> expected; - expected.push_back(std::make_tuple(nancy.lastName, nancy.firstName, nancy.address, 11)); - expected.push_back(std::make_tuple(jane.lastName, jane.firstName, jane.address, 12)); - expected.push_back(std::make_tuple(margaret.lastName, margaret.firstName, margaret.address, 15)); - expected.push_back(std::make_tuple(patrick.lastName, patrick.firstName, patrick.address, 0)); - expected.push_back(std::make_tuple(teresa.lastName, teresa.firstName, teresa.address, 0)); - REQUIRE_THAT(rows, UnorderedEquals(expected)); - } - { - auto rows = storage.select(columns(&Employee::lastName, - &Employee::firstName, - &Employee::address, - as(instr(&Employee::address, "SW"))), - where(greater_than(get(), 0))); - std::vector> expected; - expected.push_back(std::make_tuple(nancy.lastName, nancy.firstName, nancy.address, 11)); - expected.push_back(std::make_tuple(jane.lastName, jane.firstName, jane.address, 12)); - expected.push_back(std::make_tuple(margaret.lastName, margaret.firstName, margaret.address, 15)); - REQUIRE_THAT(rows, UnorderedEquals(expected)); - } -} - -namespace replace_func_local { - struct Contact { - int id = 0; - std::string firstName; - std::string lastName; - std::string phone; - }; - - bool operator==(const Contact &lhs, const Contact &rhs) { - return lhs.id == rhs.id && lhs.firstName == rhs.firstName && lhs.lastName == rhs.lastName && - lhs.phone == rhs.phone; - } -} - -TEST_CASE("replace func") { - using Catch::Matchers::UnorderedEquals; - using namespace replace_func_local; - - auto storage = make_storage({}, - make_table("contacts", - make_column("contact_id", &Contact::id, primary_key()), - make_column("first_name", &Contact::firstName), - make_column("last_name", &Contact::lastName), - make_column("phone", &Contact::phone))); - storage.sync_schema(); - { - auto rows = storage.select(replace("AA B CC AAA", "A", "Z")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "ZZ B CC ZZZ"); - } - { - auto rows = storage.select(replace("This is a cat", "This", "That")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == "That is a cat"); - } - Contact john{0, "John", "Doe", "410-555-0168"}; - Contact lily{0, "Lily", "Bush", "410-444-9862"}; - john.id = storage.insert(john); - lily.id = storage.insert(lily); - { - auto contacts = storage.get_all(); - std::vector expected; - expected.push_back(john); - expected.push_back(lily); - REQUIRE_THAT(contacts, UnorderedEquals(expected)); - } - storage.update_all(set(c(&Contact::phone) = replace(&Contact::phone, "410", "+1-410"))); - { - auto contacts = storage.get_all(); - john.phone = "+1-410-555-0168"; - lily.phone = "+1-410-444-9862"; - std::vector expected; - expected.push_back(john); - expected.push_back(lily); - REQUIRE_THAT(contacts, UnorderedEquals(expected)); - } -} - -TEST_CASE("round") { - auto storage = make_storage({}); - auto test = [&storage](auto input, double expected) { - auto rows = storage.select(round(input)); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == expected); - }; - auto test2 = [&storage](auto inputA, auto inputB, double expected) { - auto rows = storage.select(round(inputA, inputB)); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == expected); - }; - test(23.4, 23.0); - test(23.6, 24.0); - test2(23.6985, 2, 23.7); - test2(190.3985, 3, 190.399); - test2(99.9, 0, 100.0); - test2(23.3985, nullptr, 0); // maybe this is an error but noone cares AFAIK - test2(1304.67, -1, 1305.0); - test2(1929.236, 2, 1929.24); - test2(1929.236, 1, 1929.2); - test(1929.236, 1929); - test(0.5, 1); - test2(59.9, 0, 60.0); - test2(-59.9, 0, -60.0); - test2(-4.535, 2, -4.54); - test2(34.4158, -1, 34.0); -} diff --git a/tests/dynamic_order_by.cpp b/tests/dynamic_order_by.cpp deleted file mode 100644 index 0091ceab3..000000000 --- a/tests/dynamic_order_by.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include - -using namespace sqlite_orm; - -TEST_CASE("Dynamic order by") { - struct User { - int id = 0; - std::string firstName; - std::string lastName; - long registerTime = 0; - }; - - auto storage = make_storage({}, - make_table("users", - make_column("id", &User::id, primary_key()), - make_column("first_name", &User::firstName), - make_column("last_name", &User::lastName), - make_column("register_time", &User::registerTime))); - storage.sync_schema(); - - storage.replace(User{1, "Jack", "Johnson", 100}); - storage.replace(User{2, "John", "Jackson", 90}); - storage.replace(User{3, "Elena", "Alexandra", 80}); - storage.replace(User{4, "Kaye", "Styles", 70}); - - auto orderBy = dynamic_order_by(storage); - std::vector expectedIds; - - SECTION("id") { - auto ob = order_by(&User::id); - orderBy.push_back(ob); - expectedIds = { - 1, - 2, - 3, - 4, - }; - } - - SECTION("id desc") { - orderBy.push_back(order_by(&User::id).desc()); - expectedIds = { - 4, - 3, - 2, - 1, - }; - } - - SECTION("firstName") { - orderBy.push_back(order_by(&User::firstName)); - expectedIds = { - 3, - 1, - 2, - 4, - }; - } - - SECTION("firstName asc") { - orderBy.push_back(order_by(&User::firstName).asc()); - expectedIds = { - 3, - 1, - 2, - 4, - }; - } - - SECTION("firstName desc") { - orderBy.push_back(order_by(&User::firstName).desc()); - expectedIds = { - 4, - 2, - 1, - 3, - }; - } - - SECTION("firstName asc + id desc") { - orderBy.push_back(order_by(&User::firstName).asc()); - orderBy.push_back(order_by(&User::id).desc()); - expectedIds = { - 3, - 1, - 2, - 4, - }; - } - - SECTION("lastName + firstName + id") { - orderBy.push_back(order_by(&User::lastName)); - orderBy.push_back(order_by(&User::firstName)); - orderBy.push_back(order_by(&User::id)); - expectedIds = { - 3, - 2, - 1, - 4, - }; - } - - SECTION("lastName + firstName desc + id") { - orderBy.push_back(order_by(&User::lastName)); - orderBy.push_back(order_by(&User::firstName).desc()); - orderBy.push_back(order_by(&User::id)); - expectedIds = { - 3, - 2, - 1, - 4, - }; - } - - auto rows = storage.get_all(orderBy); - REQUIRE(rows.size() == 4); - for(auto i = 0; i < int(rows.size()); ++i) { - auto &row = rows[i]; - REQUIRE(row.id == expectedIds[i]); - } - orderBy.clear(); -} diff --git a/tests/get_all_custom_containers.cpp b/tests/get_all_custom_containers.cpp index 592be9028..56104c7e5 100644 --- a/tests/get_all_custom_containers.cpp +++ b/tests/get_all_custom_containers.cpp @@ -13,11 +13,11 @@ struct User { struct Comparator { - bool operator()(const User &lhs, const User &rhs) const { + bool operator()(const User& lhs, const User& rhs) const { return lhs.id == rhs.id && lhs.name == rhs.name; } - bool operator()(const std::unique_ptr &lhs, const User &rhs) const { + bool operator()(const std::unique_ptr& lhs, const User& rhs) const { if(lhs) { return this->operator()(*lhs, rhs); } else { @@ -25,7 +25,7 @@ struct Comparator { } } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - bool operator()(const std::optional &lhs, const User &rhs) const { + bool operator()(const std::optional& lhs, const User& rhs) const { if(lhs.has_value()) { return this->operator()(*lhs, rhs); } else { @@ -36,16 +36,16 @@ struct Comparator { }; struct Tester { - const std::vector &expected; + const std::vector& expected; template - void testContainer(const T &users) const { + void testContainer(const T& users) const { REQUIRE(std::equal(users.begin(), users.end(), this->expected.begin(), this->expected.end(), Comparator{})); static_assert(std::is_same::value, ""); } template - void testPreparedStatement(S &storage, const T &statement) const { + void testPreparedStatement(S& storage, const T& statement) const { this->testContainer(storage.execute(statement)); } }; diff --git a/tests/index_tests.cpp b/tests/index_tests.cpp index ccb64f6ad..aa047c1a9 100644 --- a/tests/index_tests.cpp +++ b/tests/index_tests.cpp @@ -45,3 +45,13 @@ TEST_CASE("index") { storage.sync_schema(); } } + +TEST_CASE("Escaped index name") { + struct User { + std::string group; + }; + auto storage = make_storage("index_group.sqlite", + make_index("index", &User::group), + make_table("users", make_column("group", &User::group))); + storage.sync_schema(); +} diff --git a/tests/json.cpp b/tests/json.cpp new file mode 100644 index 000000000..48cb973c9 --- /dev/null +++ b/tests/json.cpp @@ -0,0 +1,466 @@ +#include +#include + +using namespace sqlite_orm; + +#ifdef SQLITE_ENABLE_JSON1 +TEST_CASE("json") { + auto storage = make_storage(""); + std::vector expected; + std::vector rows; + rows = storage.select(json(" { \"this\" : \"is\", \"a\": [ \"test\" ] } ")); + expected.push_back("{\"this\":\"is\",\"a\":[\"test\"]}"); + REQUIRE(expected == rows); +} + +TEST_CASE("json_array") { + auto storage = make_storage(""); + std::vector expected; + std::vector rows; + SECTION("1") { + rows = storage.select(json_array(1, 2, "3", 4)); + expected.push_back("[1,2,\"3\",4]"); + } + SECTION("2") { + rows = storage.select(json_array("[1,2]")); + expected.push_back("[\"[1,2]\"]"); + } + SECTION("3") { + rows = storage.select(json_array(json_array(1, 2))); + expected.push_back("[[1,2]]"); + } + SECTION("4") { + rows = storage.select(json_array(1, nullptr, "3", "[4,5]", "{\"six\":7.7}")); + expected.push_back(R"([1,null,"3","[4,5]","{\"six\":7.7}"])"); + } + SECTION("5") { + rows = storage.select(json_array(1, nullptr, "3", json("[4,5]"), json("{\"six\":7.7}"))); + expected.push_back(R"([1,null,"3",[4,5],{"six":7.7}])"); + } + REQUIRE(expected == rows); +} + +TEST_CASE("json_array_length") { + auto storage = make_storage(""); + std::vector expected; + std::vector rows; + SECTION("1") { + rows = storage.select(json_array_length("[1,2,3,4]")); + expected.push_back(4); + } + SECTION("2") { + rows = storage.select(json_array_length("[1,2,3,4]", "$")); + expected.push_back(4); + } + SECTION("3") { + rows = storage.select(json_array_length("[1,2,3,4]", "$[2]")); + expected.push_back(0); + } + SECTION("4") { + rows = storage.select(json_array_length("{\"one\":[1,2,3]}")); + expected.push_back(0); + } + SECTION("5") { + rows = storage.select(json_array_length("{\"one\":[1,2,3]}", "$.one")); + expected.push_back(3); + } + REQUIRE(expected == rows); +} + +TEST_CASE("json_array_length nullable") { + auto storage = make_storage(""); + using Type = std::unique_ptr; + Type expected; + Type value; + std::vector rows; + SECTION("1") { + rows = storage.select(json_array_length("[1,2,3,4]")); + expected = std::make_unique(4); + } + SECTION("2") { + rows = storage.select(json_array_length("[1,2,3,4]", "$")); + expected = std::make_unique(4); + } + SECTION("3") { + rows = storage.select(json_array_length("[1,2,3,4]", "$[2]")); + expected = std::make_unique(0); + } + SECTION("4") { + rows = storage.select(json_array_length("{\"one\":[1,2,3]}")); + expected = std::make_unique(0); + } + SECTION("5") { + rows = storage.select(json_array_length("{\"one\":[1,2,3]}", "$.one")); + expected = std::make_unique(3); + } + SECTION("6") { + rows = storage.select(json_array_length("{\"one\":[1,2,3]}", "$.two")); + expected = nullptr; + } + value = move(rows[0]); + REQUIRE(rows.size() == 1); + REQUIRE(bool(expected) == bool(value)); + if(expected) { + REQUIRE(*expected == *value); + } +} + +TEST_CASE("json_extract") { + auto storage = make_storage(""); + SECTION("1") { + auto rows = storage.select(json_extract("{\"a\":2,\"c\":[4,5,{\"f\":7}]}", "$")); + decltype(rows) expected; + expected.push_back(R"({"a":2,"c":[4,5,{"f":7}]})"); + REQUIRE(expected == rows); + } + SECTION("2") { + auto rows = storage.select(json_extract("{\"a\":2,\"c\":[4,5,{\"f\":7}]}", "$.c")); + decltype(rows) expected; + expected.push_back(R"([4,5,{"f":7}])"); + REQUIRE(expected == rows); + } + SECTION("3") { + auto rows = storage.select(json_extract("{\"a\":2,\"c\":[4,5,{\"f\":7}]}", "$.c[2]")); + decltype(rows) expected; + expected.push_back(R"({"f":7})"); + REQUIRE(expected == rows); + } + SECTION("4") { + auto rows = storage.select(json_extract("{\"a\":2,\"c\":[4,5,{\"f\":7}]}", "$.c[2].f")); + decltype(rows) expected; + expected.push_back(7); + REQUIRE(expected == rows); + } + SECTION("5") { + auto rows = storage.select(json_extract(R"({"a":2,"c":[4,5],"f":7})", "$.c", "$.a")); + decltype(rows) expected; + expected.push_back("[[4,5],2]"); + REQUIRE(expected == rows); + } + SECTION("6") { + auto rows = storage.select(json_extract(R"({"a":2,"c":[4,5],"f":7})", "$.c[#-1]")); + decltype(rows) expected; + expected.push_back(5); + REQUIRE(expected == rows); + } + SECTION("7") { + auto rows = storage.select(json_extract>(R"({"a":2,"c":[4,5,{"f":7}]})", "$.x")); + REQUIRE(rows.size() == 1); + REQUIRE(!rows[0]); + } + SECTION("8") { + auto rows = storage.select(json_extract(R"({"a":2,"c":[4,5,{"f":7}]})", "$.x", "$.a")); + decltype(rows) expected; + expected.push_back("[null,2]"); + REQUIRE(expected == rows); + } +} + +TEST_CASE("json_insert") { + auto storage = make_storage(""); + SECTION("1") { + auto rows = storage.select(json_insert("[1,2,3,4]", "$[#]", 99)); + decltype(rows) expected; + expected.push_back("[1,2,3,4,99]"); + REQUIRE(expected == rows); + } + SECTION("2") { + auto rows = storage.select(json_insert("[1,[2,3],4]", "$[1][#]", 99)); + decltype(rows) expected; + expected.push_back("[1,[2,3,99],4]"); + REQUIRE(expected == rows); + } + SECTION("3") { + auto rows = storage.select(json_insert(R"({"a":2,"c":4})", "$.a", 99)); + decltype(rows) expected; + expected.push_back(R"({"a":2,"c":4})"); + REQUIRE(expected == rows); + } + SECTION("4") { + auto rows = storage.select(json_insert(R"({"a":2,"c":4})", "$.e", 99)); + decltype(rows) expected; + expected.push_back(R"({"a":2,"c":4,"e":99})"); + REQUIRE(expected == rows); + } +} + +TEST_CASE("json_replace") { + auto storage = make_storage(""); + SECTION("1") { + auto rows = storage.select(json_replace(R"({"a":2,"c":4})", "$.a", 99)); + decltype(rows) expected; + expected.push_back(R"({"a":99,"c":4})"); + REQUIRE(expected == rows); + } + SECTION("2") { + auto rows = storage.select(json_replace(R"({"a":2,"c":4})", "$.e", 99)); + decltype(rows) expected; + expected.push_back(R"({"a":2,"c":4})"); + REQUIRE(expected == rows); + } +} + +TEST_CASE("json_set") { + auto storage = make_storage(""); + SECTION("1") { + auto rows = storage.select(json_set(R"({"a":2,"c":4})", "$.a", 99)); + decltype(rows) expected; + expected.push_back(R"({"a":99,"c":4})"); + REQUIRE(expected == rows); + } + SECTION("2") { + auto rows = storage.select(json_set(R"({"a":2,"c":4})", "$.e", 99)); + decltype(rows) expected; + expected.push_back(R"({"a":2,"c":4,"e":99})"); + REQUIRE(expected == rows); + } + SECTION("3") { + auto rows = storage.select(json_set(R"({"a":2,"c":4})", "$.c", "[97,96]")); + decltype(rows) expected; + expected.push_back(R"({"a":2,"c":"[97,96]"})"); + REQUIRE(expected == rows); + } + SECTION("4") { + auto rows = storage.select(json_set(R"({"a":2,"c":4})", "$.c", json("[97,96]"))); + decltype(rows) expected; + expected.push_back(R"({"a":2,"c":[97,96]})"); + REQUIRE(expected == rows); + } + SECTION("5") { + auto rows = storage.select(json_set(R"({"a":2,"c":4})", "$.c", json_array(97, 96))); + decltype(rows) expected; + expected.push_back(R"({"a":2,"c":[97,96]})"); + REQUIRE(expected == rows); + } +} + +TEST_CASE("json_object") { + auto storage = make_storage(""); + SECTION("1") { + auto rows = storage.select(json_object("a", 2, "c", 4)); + decltype(rows) expected; + expected.push_back(R"({"a":2,"c":4})"); + REQUIRE(expected == rows); + } + SECTION("2") { + auto rows = storage.select(json_object("a", 2, "c", "{e:5}")); + decltype(rows) expected; + expected.push_back(R"({"a":2,"c":"{e:5}"})"); + REQUIRE(expected == rows); + } + SECTION("2") { + auto rows = storage.select(json_object("a", 2, "c", json_object("e", 5))); + decltype(rows) expected; + expected.push_back(R"({"a":2,"c":{"e":5}})"); + REQUIRE(expected == rows); + } +} + +TEST_CASE("json_patch") { + auto storage = make_storage(""); + SECTION("1") { + auto rows = storage.select(json_patch(R"({"a":1,"b":2})", R"({"c":3,"d":4})")); + decltype(rows) expected; + expected.push_back(R"({"a":1,"b":2,"c":3,"d":4})"); + REQUIRE(expected == rows); + } + SECTION("2") { + auto rows = storage.select(json_patch(R"({"a":[1,2],"b":2})", R"({"a":9})")); + decltype(rows) expected; + expected.push_back(R"({"a":9,"b":2})"); + REQUIRE(expected == rows); + } + SECTION("3") { + auto rows = storage.select(json_patch(R"({"a":[1,2],"b":2})", R"({"a":null})")); + decltype(rows) expected; + expected.push_back(R"({"b":2})"); + REQUIRE(expected == rows); + } + SECTION("4") { + auto rows = storage.select(json_patch(R"({"a":1,"b":2})", R"({"a":9,"b":null,"c":8})")); + decltype(rows) expected; + expected.push_back(R"({"a":9,"c":8})"); + REQUIRE(expected == rows); + } + SECTION("5") { + auto rows = storage.select(json_patch(R"({"a":{"x":1,"y":2},"b":3})", R"({"a":{"y":9},"c":8})")); + decltype(rows) expected; + expected.push_back(R"({"a":{"x":1,"y":9},"b":3,"c":8})"); + REQUIRE(expected == rows); + } +} + +TEST_CASE("json_remove") { + auto storage = make_storage(""); + SECTION("1") { + auto rows = storage.select(json_remove(R"([0,1,2,3,4])", "$[2]")); + decltype(rows) expected; + expected.push_back(R"([0,1,3,4])"); + REQUIRE(expected == rows); + } + SECTION("2") { + auto rows = storage.select(json_remove(R"([0,1,2,3,4])", "$[2]", "$[0]")); + decltype(rows) expected; + expected.push_back(R"([1,3,4])"); + REQUIRE(expected == rows); + } + SECTION("3") { + auto rows = storage.select(json_remove(R"([0,1,2,3,4])", "$[0]", "$[2]")); + decltype(rows) expected; + expected.push_back(R"([1,2,4])"); + REQUIRE(expected == rows); + } + SECTION("4") { + auto rows = storage.select(json_remove(R"([0,1,2,3,4])", "$[#-1]", "$[0]")); + decltype(rows) expected; + expected.push_back(R"([1,2,3])"); + REQUIRE(expected == rows); + } + SECTION("5") { + auto rows = storage.select(json_remove(R"({"x":25,"y":42})")); + decltype(rows) expected; + expected.push_back(R"({"x":25,"y":42})"); + REQUIRE(expected == rows); + } + SECTION("6") { + auto rows = storage.select(json_remove(R"({"x":25,"y":42})", "$.z")); + decltype(rows) expected; + expected.push_back(R"({"x":25,"y":42})"); + REQUIRE(expected == rows); + } + SECTION("7") { + auto rows = storage.select(json_remove(R"({"x":25,"y":42})", "$.y")); + decltype(rows) expected; + expected.push_back(R"({"x":25})"); + REQUIRE(expected == rows); + } + SECTION("8") { + auto rows = storage.select(json_remove>(R"({"x":25,"y":42})", "$")); + REQUIRE(rows.size() == 1); + REQUIRE(!bool(rows[0])); + } +} + +TEST_CASE("json_type") { + auto storage = make_storage(""); + SECTION("1") { + auto argument = R"({"a":[2,3.5,true,false,null,"x"]})"; + auto result = "object"; + SECTION("not null") { + auto rows = storage.select(json_type(argument)); + decltype(rows) expected; + expected.push_back(result); + REQUIRE(expected == rows); + } + SECTION("null") { + auto rows = storage.select(json_type>(argument)); + REQUIRE(rows.size() == 1); + REQUIRE(rows[0]); + REQUIRE(*rows[0] == result); + } + } + SECTION("2") { + struct TestCase { + std::string argument; + std::string result; + std::string secondArgument; + }; + std::vector testCases; + testCases.push_back(TestCase{R"({"a":[2,3.5,true,false,null,"x"]})", "object", "$"}); + testCases.push_back(TestCase{R"({"a":[2,3.5,true,false,null,"x"]})", "array", "$.a"}); + testCases.push_back(TestCase{R"({"a":[2,3.5,true,false,null,"x"]})", "integer", "$.a[0]"}); + testCases.push_back(TestCase{R"({"a":[2,3.5,true,false,null,"x"]})", "real", "$.a[1]"}); + testCases.push_back(TestCase{R"({"a":[2,3.5,true,false,null,"x"]})", "true", "$.a[2]"}); + testCases.push_back(TestCase{R"({"a":[2,3.5,true,false,null,"x"]})", "false", "$.a[3]"}); + testCases.push_back(TestCase{R"({"a":[2,3.5,true,false,null,"x"]})", "null", "$.a[4]"}); + testCases.push_back(TestCase{R"({"a":[2,3.5,true,false,null,"x"]})", "text", "$.a[5]"}); + testCases.push_back(TestCase{R"({"a":[2,3.5,true,false,null,"x"]})", "", "$.a[6]"}); + for(auto &testCase: testCases) { + { + auto rows = storage.select(json_type(testCase.argument, testCase.secondArgument)); + decltype(rows) expected; + expected.push_back(testCase.result); + REQUIRE(expected == rows); + } + { + auto rows = + storage.select(json_type>(testCase.argument, testCase.secondArgument)); + REQUIRE(rows.size() == 1); + if(!testCase.result.empty()) { + REQUIRE(rows[0]); + REQUIRE(*rows[0] == testCase.result); + } else { + REQUIRE(!rows[0]); + } + } + } + } +} + +TEST_CASE("json_valid") { + auto storage = make_storage(""); + struct TestCase { + std::string argument; + bool expected = false; + }; + std::vector testCases; + testCases.push_back(TestCase{R"({"x":35})", true}); + testCases.push_back(TestCase{R"({"x":35)", false}); + for(auto &testCase: testCases) { + auto rows = storage.select(json_valid(testCase.argument)); + decltype(rows) expected; + expected.push_back(testCase.expected); + REQUIRE(rows == expected); + } +} + +TEST_CASE("json_quote") { + auto storage = make_storage(""); + { + auto rows = storage.select(json_quote(3.14159)); + decltype(rows) expected; + expected.push_back(3.14159); + REQUIRE(rows == expected); + } + { + auto rows = storage.select(json_quote("verdant")); + decltype(rows) expected; + expected.push_back("\"verdant\""); + REQUIRE(rows == expected); + } +} + +TEST_CASE("json_group_array && json_group_object") { + struct User { + int id = 0; + std::string name; + }; + auto storage = + make_storage({}, make_table("users", make_column("id", &User::id), make_column("name", &User::name))); + storage.sync_schema(); + + storage.insert(User{1, "Bob"}); + storage.insert(User{2, "Alice"}); + + SECTION("json_group_array") { + { + auto rows = storage.select(json_group_array(&User::id)); + decltype(rows) expected; + expected.push_back("[1,2]"); + REQUIRE(rows == expected); + } + { + auto rows = storage.select(json_group_array(&User::name)); + decltype(rows) expected; + expected.push_back(R"(["Bob","Alice"])"); + REQUIRE(rows == expected); + } + } + SECTION("json_group_object") { + auto rows = storage.select(json_group_object(&User::id, &User::name)); + decltype(rows) expected; + expected.push_back(R"({"1":"Bob","2":"Alice"})"); + REQUIRE(rows == expected); + } +} +#endif // SQLITE_ENABLE_JSON1 diff --git a/tests/operators/arithmetic_operators.cpp b/tests/operators/arithmetic_operators.cpp index 85318eacb..07e7c42d1 100644 --- a/tests/operators/arithmetic_operators.cpp +++ b/tests/operators/arithmetic_operators.cpp @@ -23,22 +23,22 @@ TEST_CASE("Arithmetic operators") { "Upside down", }; auto number = 10; - for(auto &name: names) { + for(auto& name: names) { storage.insert(Object{name, int(name.length()), number}); } { // + auto rows = storage.select(c(&Object::nameLen) + 1000); for(size_t i = 0; i < rows.size(); ++i) { - auto &row = rows[i]; - auto &name = names[i]; + auto& row = rows[i]; + auto& name = names[i]; REQUIRE(int(row) == name.length() + 1000); } } { // + auto rows = storage.select(columns(c(&Object::nameLen) + 1000)); for(size_t i = 0; i < rows.size(); ++i) { - auto &row = rows[i]; - auto &name = names[i]; + auto& row = rows[i]; + auto& name = names[i]; REQUIRE(int(std::get<0>(row)) == name.length() + 1000); } } @@ -46,8 +46,8 @@ TEST_CASE("Arithmetic operators") { std::string suffix = "ototo"; auto rows = storage.select(c(&Object::name) || suffix); for(size_t i = 0; i < rows.size(); ++i) { - auto &row = rows[i]; - auto &name = names[i]; + auto& row = rows[i]; + auto& name = names[i]; REQUIRE(row == name + suffix); } } @@ -55,8 +55,8 @@ TEST_CASE("Arithmetic operators") { std::string suffix = "ototo"; auto rows = storage.select(columns(conc(&Object::name, suffix))); for(size_t i = 0; i < rows.size(); ++i) { - auto &row = rows[i]; - auto &name = names[i]; + auto& row = rows[i]; + auto& name = names[i]; REQUIRE(std::get<0>(row) == name + suffix); } } @@ -92,8 +92,8 @@ TEST_CASE("Arithmetic operators") { c(&Object::nameLen) / 2)); for(size_t i = 0; i < rows.size(); ++i) { - auto &row = rows[i]; - auto &name = names[i]; + auto& row = rows[i]; + auto& name = names[i]; REQUIRE(std::get<0>(row) == name + suffix); REQUIRE(std::get<1>(row) == std::get<0>(row)); REQUIRE(std::get<2>(row) == std::get<1>(row)); @@ -105,7 +105,7 @@ TEST_CASE("Arithmetic operators") { REQUIRE(std::get<6>(row) == std::get<5>(row)); REQUIRE(std::get<7>(row) == std::get<6>(row)); { - auto &rowValue = std::get<8>(row); + auto& rowValue = std::get<8>(row); REQUIRE(rowValue == int(name.length()) + 1000); } @@ -138,8 +138,8 @@ TEST_CASE("Arithmetic operators") { c(&Object::nameLen) % c(&Object::number), c(&Object::nameLen) % 5)); for(size_t i = 0; i < rows.size(); ++i) { - auto &row = rows[i]; - auto &name = names[i]; + auto& row = rows[i]; + auto& name = names[i]; REQUIRE(std::get<0>(row) == static_cast(name.length()) % number); REQUIRE(std::get<1>(row) == std::get<0>(row)); REQUIRE(std::get<2>(row) == std::get<1>(row)); diff --git a/tests/operators/binary_operators.cpp b/tests/operators/binary_operators.cpp new file mode 100644 index 000000000..ce1241f6b --- /dev/null +++ b/tests/operators/binary_operators.cpp @@ -0,0 +1,169 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("binary operators") { + using Catch::Matchers::UnorderedEquals; + + struct User { + int id = 0; + }; + auto storage = make_storage({}, make_table("users", make_column("id", &User::id))); + storage.sync_schema(); + + storage.replace(User{1}); + storage.replace(User{2}); + storage.replace(User{3}); + + std::vector rows; + decltype(rows) expected; + + SECTION("is_equal") { + SECTION("is_equal") { + rows = storage.select(is_equal(&User::id, 1)); + } + SECTION("eq") { + rows = storage.select(eq(&User::id, 1)); + } + SECTION("==") { + SECTION("left") { + rows = storage.select(c(&User::id) == 1); + } + SECTION("right") { + rows = storage.select(&User::id == c(1)); + } + SECTION("explicit column") { + rows = storage.select(column(&User::id) == 1); + } + } + expected.push_back(true); + expected.push_back(false); + expected.push_back(false); + } + SECTION("is_not_equal") { + SECTION("is_not_equal") { + rows = storage.select(is_not_equal(&User::id, 1)); + } + SECTION("ne") { + rows = storage.select(ne(&User::id, 1)); + } + SECTION("!=") { + SECTION("left") { + rows = storage.select(c(&User::id) != 1); + } + SECTION("right") { + rows = storage.select(&User::id != c(1)); + } + SECTION("explicit column") { + rows = storage.select(column(&User::id) != 1); + } + } + expected.push_back(false); + expected.push_back(true); + expected.push_back(true); + } + SECTION("greater_than") { + SECTION("greater_than") { + rows = storage.select(greater_than(&User::id, 2)); + } + SECTION("gt") { + rows = storage.select(gt(&User::id, 2)); + } + SECTION(">") { + SECTION("left") { + rows = storage.select(c(&User::id) > 2); + } + SECTION("right") { + rows = storage.select(&User::id > c(2)); + } + SECTION("explicit column") { + rows = storage.select(column(&User::id) > 2); + } + } + expected.push_back(true); + expected.push_back(false); + expected.push_back(false); + } + SECTION("greater_or_equal") { + SECTION("greater_or_equal") { + rows = storage.select(greater_or_equal(&User::id, 2)); + } + SECTION("ge") { + rows = storage.select(ge(&User::id, 2)); + } + SECTION(">=") { + SECTION("left") { + rows = storage.select(c(&User::id) >= 2); + } + SECTION("right") { + rows = storage.select(&User::id >= c(2)); + } + + SECTION("explicit column") { + rows = storage.select(column(&User::id) >= 2); + } + } + expected.push_back(true); + expected.push_back(true); + expected.push_back(false); + } + SECTION("lesser_than") { + SECTION("lesser_than") { + rows = storage.select(lesser_than(&User::id, 2)); + } + SECTION("lt") { + rows = storage.select(lt(&User::id, 2)); + } + SECTION("<") { + SECTION("left") { + rows = storage.select(c(&User::id) < 2); + } + SECTION("right") { + rows = storage.select(&User::id < c(2)); + } + SECTION("explicit column") { + rows = storage.select(column(&User::id) < 2); + } + } + expected.push_back(true); + expected.push_back(false); + expected.push_back(false); + } + SECTION("lesser_or_equal") { + SECTION("lesser_or_equal") { + rows = storage.select(lesser_or_equal(&User::id, 2)); + } + SECTION("le") { + rows = storage.select(le(&User::id, 2)); + } + SECTION("<=") { + SECTION("left") { + rows = storage.select(c(&User::id) <= 2); + } + SECTION("right") { + rows = storage.select(&User::id <= c(2)); + } + SECTION("explicit column") { + rows = storage.select(column(&User::id) <= 2); + } + } + expected.push_back(true); + expected.push_back(true); + expected.push_back(false); + } + SECTION("and") { + rows = storage.select(greater_than(&User::id, 1) and lesser_than(&User::id, 3)); + expected.push_back(false); + expected.push_back(true); + expected.push_back(false); + } + SECTION("or") { + rows = storage.select(lesser_than(&User::id, 2) or greater_than(&User::id, 2)); + expected.push_back(true); + expected.push_back(false); + expected.push_back(true); + } + + REQUIRE_THAT(rows, UnorderedEquals(expected)); +} diff --git a/tests/operators/cast.cpp b/tests/operators/cast.cpp index 94bd158ec..a214de379 100644 --- a/tests/operators/cast.cpp +++ b/tests/operators/cast.cpp @@ -22,14 +22,14 @@ TEST_CASE("Cast") { { auto rows = storage.select(columns(cast(&Student::scoreFloat), cast(&Student::scoreString))); REQUIRE(rows.size() == 1); - auto &row = rows.front(); + auto& row = rows.front(); REQUIRE(std::get<0>(row) == 10); REQUIRE(std::get<1>(row) == 14); } { auto rows = storage.select(cast(5)); REQUIRE(rows.size() == 1); - auto &row = rows.front(); + auto& row = rows.front(); REQUIRE(row == "5"); } } diff --git a/tests/operators/glob.cpp b/tests/operators/glob.cpp index 7b461b892..a10bace09 100644 --- a/tests/operators/glob.cpp +++ b/tests/operators/glob.cpp @@ -40,9 +40,9 @@ TEST_CASE("Glob") { storage.replace_range(employees.begin(), employees.end()); } - auto expectIds = [](const std::vector &employees, const std::vector ids) { + auto expectIds = [](const std::vector& employees, const std::vector ids) { for(auto expectedId: ids) { - REQUIRE(find_if(employees.begin(), employees.end(), [expectedId](auto &employee) { + REQUIRE(find_if(employees.begin(), employees.end(), [expectedId](auto& employee) { return employee.id == expectedId; }) != employees.end()); } diff --git a/tests/operators/in.cpp b/tests/operators/in.cpp index e673aa969..d71911ebb 100644 --- a/tests/operators/in.cpp +++ b/tests/operators/in.cpp @@ -4,9 +4,18 @@ using namespace sqlite_orm; TEST_CASE("In") { + using Catch::Matchers::UnorderedEquals; { struct User { - int id; + int id = 0; + + bool operator==(const User &other) const { + return this->id == other.id; + } + + std::ostream &operator<<(std::ostream &os) const { + return os << this->id; + } }; auto storage = make_storage("", make_table("users", make_column("id", &User::id, primary_key()))); @@ -14,19 +23,62 @@ TEST_CASE("In") { storage.replace(User{1}); storage.replace(User{2}); storage.replace(User{3}); - - { - auto rows = storage.get_all(where(in(&User::id, {1, 2, 3}))); - REQUIRE(rows.size() == 3); + std::vector rows; + decltype(rows) expected; + SECTION("as is") { + SECTION("dynamic") { + rows = storage.get_all(where(in(&User::id, {1, 2, 3}))); + expected.push_back({1}); + expected.push_back({2}); + expected.push_back({3}); + } + SECTION("static") { + SECTION("simple") { + rows = storage.get_all(where(c(&User::id).in(1, 2, 3))); + expected.push_back({1}); + expected.push_back({2}); + expected.push_back({3}); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all(where(c(&User::id).in(1, 2, 3)))); + REQUIRE(get<0>(statement) == 1); + REQUIRE(get<1>(statement) == 2); + REQUIRE(get<2>(statement) == 3); + SECTION("as is") { + expected.push_back({1}); + expected.push_back({2}); + expected.push_back({3}); + } + SECTION("1 -> 4") { + get<0>(statement) = 4; + expected.push_back({2}); + expected.push_back({3}); + } + SECTION("1 -> 4, 2 -> 5") { + get<0>(statement) = 4; + get<1>(statement) = 5; + expected.push_back({3}); + } + SECTION("1 -> 4, 2 -> 5, 3 -> 6") { + get<0>(statement) = 4; + get<1>(statement) = 5; + get<2>(statement) = 6; + } + rows = storage.execute(statement); + } + } } - { + SECTION("vector") { std::vector inArgument; inArgument.push_back(1); inArgument.push_back(2); inArgument.push_back(3); - auto rows = storage.get_all(where(in(&User::id, inArgument))); - REQUIRE(rows.size() == 3); + rows = storage.get_all(where(in(&User::id, inArgument))); + expected.push_back({1}); + expected.push_back({2}); + expected.push_back({3}); } + REQUIRE_THAT(rows, UnorderedEquals(expected)); } { struct Letter { diff --git a/tests/pragma_tests.cpp b/tests/pragma_tests.cpp index acec737ad..2f1808e19 100644 --- a/tests/pragma_tests.cpp +++ b/tests/pragma_tests.cpp @@ -9,7 +9,7 @@ TEST_CASE("Journal mode") { ::remove(filename); auto storage = make_storage(filename); auto storageCopy = storage; - decltype(storage) *stor = nullptr; + decltype(storage)* stor = nullptr; SECTION("Storage as is") { stor = &storage; }; @@ -63,7 +63,7 @@ TEST_CASE("Synchronous") { try { storage.pragma.synchronous(newValue); throw std::runtime_error("Must not fire"); - } catch(const std::system_error &) { + } catch(const std::system_error&) { // Safety level may not be changed inside a transaction REQUIRE(storage.pragma.synchronous() == value); } @@ -118,3 +118,28 @@ TEST_CASE("busy_timeout") { value = storage.pragma.busy_timeout(); REQUIRE(value == 0); } + +TEST_CASE("Integrity Check") { + struct User { + int id; + std::string name; + int age; + std::string email; + }; + + auto filename = "integrity.sqlite"; + ::remove(filename); + + std::string tablename = "users"; + auto storage = make_storage(filename, + make_table(tablename, + make_column("id", &User::id, primary_key()), + make_column("name", &User::name), + make_column("age", &User::age), + make_column("email", &User::email, default_value("dummy@email.com")))); + storage.sync_schema(); + + REQUIRE(storage.pragma.integrity_check() == std::vector{"ok"}); + REQUIRE(storage.pragma.integrity_check(5) == std::vector{"ok"}); + REQUIRE(storage.pragma.integrity_check(tablename) == std::vector{"ok"}); +} diff --git a/tests/prepared_statement_tests/get.cpp b/tests/prepared_statement_tests/get.cpp index 96a91313d..c17d1d626 100644 --- a/tests/prepared_statement_tests/get.cpp +++ b/tests/prepared_statement_tests/get.cpp @@ -75,7 +75,7 @@ TEST_CASE("Prepared get") { auto user = storage.execute(statement); std::ignore = user; REQUIRE(false); - } catch(const std::system_error &e) { + } catch(const std::system_error&) { REQUIRE(true); } } @@ -102,7 +102,7 @@ TEST_CASE("Prepared get") { try { auto user = storage.execute(statement); REQUIRE(false); - } catch(const std::system_error &e) { + } catch(const std::system_error&) { //.. } } @@ -110,8 +110,8 @@ TEST_CASE("Prepared get") { { storage.replace(Visit{1, /*userId*/ 2, 1000}); auto statement = storage.prepare(get(2, 1)); - std::ignore = get<0>(static_cast(statement)); - std::ignore = get<1>(static_cast(statement)); + std::ignore = get<0>(static_cast(statement)); + std::ignore = get<1>(static_cast(statement)); REQUIRE(get<0>(statement) == 2); REQUIRE(get<1>(statement) == 1); { diff --git a/tests/prepared_statement_tests/get_optional.cpp b/tests/prepared_statement_tests/get_optional.cpp index 8d6b750ec..78c4d3a16 100644 --- a/tests/prepared_statement_tests/get_optional.cpp +++ b/tests/prepared_statement_tests/get_optional.cpp @@ -41,7 +41,7 @@ TEST_CASE("Prepared get optional") { { auto statement = storage.prepare(get_optional(1)); REQUIRE(get<0>(statement) == 1); - std::ignore = get<0>(static_cast(statement)); + std::ignore = get<0>(static_cast(statement)); testSerializing(statement); SECTION("nothing") { //.. @@ -100,8 +100,8 @@ TEST_CASE("Prepared get optional") { { storage.replace(Visit{1, /*userId*/ 2, 1000}); auto statement = storage.prepare(get_optional(2, 1)); - std::ignore = get<0>(static_cast(statement)); - std::ignore = get<1>(static_cast(statement)); + std::ignore = get<0>(static_cast(statement)); + std::ignore = get<1>(static_cast(statement)); REQUIRE(get<0>(statement) == 2); REQUIRE(get<1>(statement) == 1); { diff --git a/tests/prepared_statement_tests/get_pointer.cpp b/tests/prepared_statement_tests/get_pointer.cpp index ecc946ba9..b71c31386 100644 --- a/tests/prepared_statement_tests/get_pointer.cpp +++ b/tests/prepared_statement_tests/get_pointer.cpp @@ -40,7 +40,7 @@ TEST_CASE("Prepared get pointer") { { auto statement = storage.prepare(get_pointer(1)); REQUIRE(get<0>(statement) == 1); - std::ignore = get<0>(static_cast(statement)); + std::ignore = get<0>(static_cast(statement)); testSerializing(statement); SECTION("nothing") { //.. @@ -99,8 +99,8 @@ TEST_CASE("Prepared get pointer") { { storage.replace(Visit{1, /*userId*/ 2, 1000}); auto statement = storage.prepare(get_pointer(2, 1)); - std::ignore = get<0>(static_cast(statement)); - std::ignore = get<1>(static_cast(statement)); + std::ignore = get<0>(static_cast(statement)); + std::ignore = get<1>(static_cast(statement)); REQUIRE(get<0>(statement) == 2); REQUIRE(get<1>(statement) == 1); { diff --git a/tests/prepared_statement_tests/insert.cpp b/tests/prepared_statement_tests/insert.cpp index 6834d4193..73ab674b7 100644 --- a/tests/prepared_statement_tests/insert.cpp +++ b/tests/prepared_statement_tests/insert.cpp @@ -9,115 +9,231 @@ TEST_CASE("Prepared insert") { using namespace PreparedStatementTests; using Catch::Matchers::UnorderedEquals; + struct Artist { + int id = 0; + std::unique_ptr name; + + Artist() = default; + + Artist(decltype(id) id_, decltype(name) name_) : id(id_), name(move(name_)) {} + + Artist(const Artist &other) : + id(other.id), name(other.name ? std::make_unique(*other.name) : nullptr) {} + + bool operator==(const Artist &other) const { + return this->id == other.id && this->compareNames(this->name, other.name); + } + + bool compareNames(const decltype(name) &lhs, const decltype(name) &rhs) const { + if(lhs && rhs) { + return *lhs == *rhs; + } else if(!lhs && !rhs) { + return true; + } else { + return false; + } + } + }; + + struct ArtistBackup : Artist { + ArtistBackup() = default; + ArtistBackup(Artist other) : Artist(std::move(other)) {} + }; + const int defaultVisitTime = 50; auto filename = "prepared.sqlite"; remove(filename); - auto storage = make_storage(filename, - make_index("user_id_index", &User::id), - make_table("users", - make_column("id", &User::id, primary_key(), autoincrement()), - make_column("name", &User::name)), - make_table("visits", - make_column("id", &Visit::id, primary_key(), autoincrement()), - make_column("user_id", &Visit::userId), - make_column("time", &Visit::time, default_value(defaultVisitTime)), - foreign_key(&Visit::userId).references(&User::id)), - make_table("users_and_visits", - make_column("user_id", &UserAndVisit::userId), - make_column("visit_id", &UserAndVisit::visitId), - make_column("description", &UserAndVisit::description), - primary_key(&UserAndVisit::userId, &UserAndVisit::visitId))); + auto storage = make_storage( + filename, + make_index("user_id_index", &User::id), + make_table("users", + make_column("id", &User::id, primary_key(), autoincrement()), + make_column("name", &User::name)), + make_table("visits", + make_column("id", &Visit::id, primary_key(), autoincrement()), + make_column("user_id", &Visit::userId), + make_column("time", &Visit::time, default_value(defaultVisitTime)), + foreign_key(&Visit::userId).references(&User::id)), + make_table("users_and_visits", + make_column("user_id", &UserAndVisit::userId), + make_column("visit_id", &UserAndVisit::visitId), + make_column("description", &UserAndVisit::description), + primary_key(&UserAndVisit::userId, &UserAndVisit::visitId)), + make_table("artists", make_column("id", &Artist::id, primary_key()), make_column("name", &Artist::name)), + make_table("artists_backup", + make_column("id", &Artist::id, primary_key()), + make_column("name", &Artist::name))); storage.sync_schema(); + SECTION("raw insert") { + SECTION("values") { + std::vector allUsers; + decltype(allUsers) expected; + SECTION("one user") { + SECTION("statement") { + auto statement = storage.prepare( + insert(into(), columns(&User::id, &User::name), values(std::make_tuple(1, "Ellie")))); + storage.execute(statement); + REQUIRE(get<0>(statement) == 1); + REQUIRE(::strcmp(get<1>(statement), "Ellie") == 0); + } + SECTION("no statement") { + storage.insert(into(), columns(&User::id, &User::name), values(std::make_tuple(1, "Ellie"))); + } - storage.replace(User{1, "Team BS"}); - storage.replace(User{2, "Shy'm"}); - storage.replace(User{3, "Maître Gims"}); + allUsers = storage.get_all(); + expected.push_back({1, "Ellie"}); + } + SECTION("two users") { + SECTION("statement") { + auto statement = + storage.prepare(insert(into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "Ellie"), std::make_tuple(5, "Calvin")))); + storage.execute(statement); + REQUIRE(get<0>(statement) == 1); + REQUIRE(::strcmp(get<1>(statement), "Ellie") == 0); + REQUIRE(get<2>(statement) == 5); + REQUIRE(::strcmp(get<3>(statement), "Calvin") == 0); + } + SECTION("no statement") { + storage.insert(into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "Ellie"), std::make_tuple(5, "Calvin"))); + } - storage.replace(UserAndVisit{2, 1, "Glad you came"}); - storage.replace(UserAndVisit{3, 1, "Shine on"}); + allUsers = storage.get_all(); + expected.push_back({1, "Ellie"}); + expected.push_back({5, "Calvin"}); + } + REQUIRE_THAT(allUsers, UnorderedEquals(expected)); + } + SECTION("default values") { + SECTION("statement") { + auto statement = storage.prepare(insert(into(), default_values())); + storage.execute(statement); + } + SECTION("no statement") { + storage.insert(into(), default_values()); + } - User user{0, "Stromae"}; - SECTION("by ref") { - auto statement = storage.prepare(insert(std::ref(user))); - testSerializing(statement); - SECTION("nothing") { - //.. + auto allArtists = storage.get_all(); + decltype(allArtists) expected; + expected.push_back({1, nullptr}); + REQUIRE(allArtists == expected); } - SECTION("execute") { - auto insertedId = storage.execute(statement); - { - auto rows = storage.get_all(); - std::vector expected; - expected.push_back(User{1, "Team BS"}); - expected.push_back(User{2, "Shy'm"}); - expected.push_back(User{3, "Maître Gims"}); - expected.push_back(User{4, "Stromae"}); - REQUIRE_THAT(rows, UnorderedEquals(expected)); + SECTION("select") { + Artist artist1{1, std::make_unique("Deepend")}; + Artist artist4{4, std::make_unique("Robin Schulz")}; + storage.replace(artist1); + storage.replace(artist4); + REQUIRE(storage.count() == 0); + SECTION("statement") { + auto statement = + storage.prepare(insert(into(), select(columns(&Artist::id, &Artist::name)))); + storage.execute(statement); } - REQUIRE(insertedId == 4); - user.name = "Sia"; - std::ignore = get<0>(static_cast(statement)); - REQUIRE(get<0>(statement) == user); - REQUIRE(&get<0>(statement) == &user); - insertedId = storage.execute(statement); - { - auto rows = storage.get_all(); - std::vector expected; - expected.push_back(User{1, "Team BS"}); - expected.push_back(User{2, "Shy'm"}); - expected.push_back(User{3, "Maître Gims"}); - expected.push_back(User{4, "Stromae"}); - expected.push_back(User{5, "Sia"}); - REQUIRE_THAT(rows, UnorderedEquals(expected)); + SECTION("no statement") { + storage.insert(into(), select(columns(&Artist::id, &Artist::name))); } - REQUIRE(insertedId == 5); + + auto allArtistBackups = storage.get_all(); + decltype(allArtistBackups) expected; + expected.push_back(artist1); + expected.push_back(artist4); + REQUIRE_THAT(allArtistBackups, UnorderedEquals(expected)); } } - SECTION("by val") { - auto statement = storage.prepare(insert(user)); - testSerializing(statement); - SECTION("nothing") { - //.. - } - SECTION("execute") { - auto insertedId = storage.execute(statement); - { - auto rows = storage.get_all(); - std::vector expected; - expected.push_back(User{1, "Team BS"}); - expected.push_back(User{2, "Shy'm"}); - expected.push_back(User{3, "Maître Gims"}); - expected.push_back(User{4, "Stromae"}); - REQUIRE_THAT(rows, UnorderedEquals(expected)); + SECTION("insert object") { + storage.replace(User{1, "Team BS"}); + storage.replace(User{2, "Shy'm"}); + storage.replace(User{3, "Maître Gims"}); + + storage.replace(UserAndVisit{2, 1, "Glad you came"}); + storage.replace(UserAndVisit{3, 1, "Shine on"}); + + User user{0, "Stromae"}; + SECTION("by ref") { + auto statement = storage.prepare(insert(std::ref(user))); + testSerializing(statement); + SECTION("nothing") { + //.. } - REQUIRE(insertedId == 4); - user.name = "Sia"; - insertedId = storage.execute(statement); - { - auto rows = storage.get_all(); - std::vector expected; - expected.push_back(User{1, "Team BS"}); - expected.push_back(User{2, "Shy'm"}); - expected.push_back(User{3, "Maître Gims"}); - expected.push_back(User{4, "Stromae"}); - expected.push_back(User{5, "Stromae"}); - REQUIRE_THAT(rows, UnorderedEquals(expected)); + SECTION("execute") { + auto insertedId = storage.execute(statement); + { + auto rows = storage.get_all(); + std::vector expected; + expected.push_back(User{1, "Team BS"}); + expected.push_back(User{2, "Shy'm"}); + expected.push_back(User{3, "Maître Gims"}); + expected.push_back(User{4, "Stromae"}); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + REQUIRE(insertedId == 4); + user.name = "Sia"; + std::ignore = get<0>(static_cast(statement)); + REQUIRE(get<0>(statement) == user); + REQUIRE(&get<0>(statement) == &user); + insertedId = storage.execute(statement); + { + auto rows = storage.get_all(); + std::vector expected; + expected.push_back(User{1, "Team BS"}); + expected.push_back(User{2, "Shy'm"}); + expected.push_back(User{3, "Maître Gims"}); + expected.push_back(User{4, "Stromae"}); + expected.push_back(User{5, "Sia"}); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + REQUIRE(insertedId == 5); } - REQUIRE(insertedId == 5); - - get<0>(statement).name = "Sia"; - insertedId = storage.execute(statement); - { - auto rows = storage.get_all(); - std::vector expected; - expected.push_back(User{1, "Team BS"}); - expected.push_back(User{2, "Shy'm"}); - expected.push_back(User{3, "Maître Gims"}); - expected.push_back(User{4, "Stromae"}); - expected.push_back(User{5, "Stromae"}); - expected.push_back(User{6, "Sia"}); - REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + SECTION("by val") { + auto statement = storage.prepare(insert(user)); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto insertedId = storage.execute(statement); + { + auto rows = storage.get_all(); + std::vector expected; + expected.push_back(User{1, "Team BS"}); + expected.push_back(User{2, "Shy'm"}); + expected.push_back(User{3, "Maître Gims"}); + expected.push_back(User{4, "Stromae"}); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + REQUIRE(insertedId == 4); + user.name = "Sia"; + insertedId = storage.execute(statement); + { + auto rows = storage.get_all(); + std::vector expected; + expected.push_back(User{1, "Team BS"}); + expected.push_back(User{2, "Shy'm"}); + expected.push_back(User{3, "Maître Gims"}); + expected.push_back(User{4, "Stromae"}); + expected.push_back(User{5, "Stromae"}); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + REQUIRE(insertedId == 5); + + get<0>(statement).name = "Sia"; + insertedId = storage.execute(statement); + { + auto rows = storage.get_all(); + std::vector expected; + expected.push_back(User{1, "Team BS"}); + expected.push_back(User{2, "Shy'm"}); + expected.push_back(User{3, "Maître Gims"}); + expected.push_back(User{4, "Stromae"}); + expected.push_back(User{5, "Stromae"}); + expected.push_back(User{6, "Sia"}); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } } } } diff --git a/tests/prepared_statement_tests/insert_explicit.cpp b/tests/prepared_statement_tests/insert_explicit.cpp index 062b949ab..b1f01ddcb 100644 --- a/tests/prepared_statement_tests/insert_explicit.cpp +++ b/tests/prepared_statement_tests/insert_explicit.cpp @@ -41,7 +41,7 @@ TEST_CASE("Prepared insert explicit") { User user{5, "Eminem"}; SECTION("by ref") { auto statement = storage.prepare(insert(std::ref(user), columns(&User::id, &User::name))); - std::ignore = get<0>(static_cast(statement)); + std::ignore = get<0>(static_cast(statement)); { auto insertedId = storage.execute(statement); REQUIRE(insertedId == user.id); @@ -69,7 +69,7 @@ TEST_CASE("Prepared insert explicit") { try { storage.execute(statement); REQUIRE(false); - } catch(const std::system_error &e) { + } catch(const std::system_error& e) { REQUIRE(storage.count(where(is_equal(&User::name, "Nate Dogg"))) == 0); } catch(...) { REQUIRE(false); diff --git a/tests/prepared_statement_tests/insert_range.cpp b/tests/prepared_statement_tests/insert_range.cpp index 6600e25d7..03f19a0b5 100644 --- a/tests/prepared_statement_tests/insert_range.cpp +++ b/tests/prepared_statement_tests/insert_range.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "prepared_common.h" @@ -44,20 +45,53 @@ TEST_CASE("Prepared insert range") { expected.push_back(User{3, "Maître Gims"}); SECTION("empty") { - try { - auto statement = storage.prepare(insert_range(users.begin(), users.end())); - REQUIRE(false); - } catch(const std::system_error &e) { - //.. + SECTION("strict") { + try { + auto statement = storage.prepare(insert_range(users.begin(), users.end())); + REQUIRE(false); + } catch(const std::system_error &e) { + //.. + } + } + SECTION("container with pointers") { + try { + std::vector> usersPointers; + auto statement = storage.prepare(insert_range(usersPointers.begin(), + usersPointers.end(), + [](const std::unique_ptr &pointer) { + return *pointer; + })); + REQUIRE(false); + } catch(const std::system_error &e) { + //.. + } } } SECTION("one") { User user{4, "The Weeknd"}; users.push_back(user); - auto statement = storage.prepare(insert_range(users.begin(), users.end())); - REQUIRE(get<0>(statement) == users.begin()); - REQUIRE(get<1>(statement) == users.end()); - storage.execute(statement); + SECTION("strict container") { + auto statement = storage.prepare(insert_range(users.begin(), users.end())); + REQUIRE(get<0>(statement) == users.begin()); + REQUIRE(get<1>(statement) == users.end()); + storage.execute(statement); + } + SECTION("container of pointers") { + std::vector> usersPointers; + usersPointers.reserve(users.size()); + std::transform(users.begin(), users.end(), std::back_inserter(usersPointers), [](const User &user) { + return std::make_unique(user); + }); + auto statement = + storage.prepare(insert_range(usersPointers.begin(), + usersPointers.end(), + [](const std::unique_ptr &pointer) -> const User & { + return *pointer; + })); + REQUIRE(get<0>(statement) == usersPointers.begin()); + REQUIRE(get<1>(statement) == usersPointers.end()); + storage.execute(statement); + } expected.push_back(user); } SECTION("two") { @@ -66,25 +100,57 @@ TEST_CASE("Prepared insert range") { users.push_back(user1); users.push_back(user2); - auto statement = storage.prepare(insert_range(users.begin(), users.end())); - REQUIRE(get<0>(statement) == users.begin()); - REQUIRE(get<1>(statement) == users.end()); - storage.execute(statement); - expected.push_back(user1); - expected.push_back(user2); + SECTION("strict") { + auto statement = storage.prepare(insert_range(users.begin(), users.end())); + REQUIRE(get<0>(statement) == users.begin()); + REQUIRE(get<1>(statement) == users.end()); + storage.execute(statement); + expected.push_back(user1); + expected.push_back(user2); + + decltype(users) otherUsers; + otherUsers.push_back(User{6, "DJ Alban"}); + otherUsers.push_back(User{7, "Flo Rida"}); + for(auto &user: otherUsers) { + expected.push_back(user); + } + get<0>(statement) = otherUsers.begin(); + get<1>(statement) = otherUsers.end(); + storage.execute(statement); - decltype(users) otherUsers; - otherUsers.push_back(User{6, "DJ Alban"}); - otherUsers.push_back(User{7, "Flo Rida"}); - for(auto &user: otherUsers) { - expected.push_back(user); + std::ignore = get<0>(static_cast(statement)); + std::ignore = get<1>(static_cast(statement)); } - get<0>(statement) = otherUsers.begin(); - get<1>(statement) = otherUsers.end(); - storage.execute(statement); + SECTION("container of pointers") { + std::vector> usersPointers; + std::transform(users.begin(), users.end(), std::back_inserter(usersPointers), [](const User &user) { + return std::make_unique(user); + }); + auto statement = + storage.prepare(insert_range(usersPointers.begin(), + usersPointers.end(), + [](const std::unique_ptr &pointer) -> const User & { + return *pointer; + })); + REQUIRE(get<0>(statement) == usersPointers.begin()); + REQUIRE(get<1>(statement) == usersPointers.end()); + storage.execute(statement); + expected.push_back(user1); + expected.push_back(user2); - std::ignore = get<0>(static_cast(statement)); - std::ignore = get<1>(static_cast(statement)); + decltype(usersPointers) otherUsers; + otherUsers.emplace_back(new User{6, "DJ Alban"}); + otherUsers.emplace_back(new User{7, "Flo Rida"}); + for(auto &user: otherUsers) { + expected.push_back(*user); + } + get<0>(statement) = otherUsers.begin(); + get<1>(statement) = otherUsers.end(); + storage.execute(statement); + + std::ignore = get<0>(static_cast(statement)); + std::ignore = get<1>(static_cast(statement)); + } } auto rows = storage.get_all(); REQUIRE_THAT(rows, UnorderedEquals(expected)); diff --git a/tests/prepared_statement_tests/prepared_common.h b/tests/prepared_statement_tests/prepared_common.h index cbcfd0e00..576b9eecd 100644 --- a/tests/prepared_statement_tests/prepared_common.h +++ b/tests/prepared_statement_tests/prepared_common.h @@ -23,15 +23,15 @@ namespace PreparedStatementTests { std::string description; }; - inline bool operator==(const User &lhs, const User &rhs) { + inline bool operator==(const User& lhs, const User& rhs) { return lhs.id == rhs.id && lhs.name == rhs.name; } - inline bool operator!=(const User &lhs, const User &rhs) { + inline bool operator!=(const User& lhs, const User& rhs) { return !(lhs == rhs); } - inline void testSerializing(const sqlite_orm::internal::prepared_statement_base &statement) { + inline void testSerializing(const sqlite_orm::internal::prepared_statement_base& statement) { auto sql = statement.sql(); std::ignore = sql; #if SQLITE_VERSION_NUMBER >= 3014000 @@ -44,7 +44,7 @@ namespace PreparedStatementTests { #endif } - inline std::ostream &operator<<(std::ostream &os, const User &user) { + inline std::ostream& operator<<(std::ostream& os, const User& user) { return os << "{" << user.id << ", " << user.name << "}"; } } diff --git a/tests/prepared_statement_tests/remove.cpp b/tests/prepared_statement_tests/remove.cpp index 4341d5cde..438e97e3c 100644 --- a/tests/prepared_statement_tests/remove.cpp +++ b/tests/prepared_statement_tests/remove.cpp @@ -40,7 +40,7 @@ TEST_CASE("Prepared remove") { SECTION("by val") { { auto statement = storage.prepare(remove(1)); - std::ignore = get<0>(static_cast(statement)); + std::ignore = get<0>(static_cast(statement)); REQUIRE(get<0>(statement) == 1); testSerializing(statement); storage.execute(statement); diff --git a/tests/prepared_statement_tests/remove_all.cpp b/tests/prepared_statement_tests/remove_all.cpp index 8d7c854cb..311de5360 100644 --- a/tests/prepared_statement_tests/remove_all.cpp +++ b/tests/prepared_statement_tests/remove_all.cpp @@ -115,7 +115,7 @@ TEST_CASE("Prepared remove all") { REQUIRE_THAT(ids, UnorderedEquals(expected)); } get<0>(statement) = "Team BS"; - get<1>(statement) = 20.0; // assign double to int + get<1>(statement) = 20.0; // assign double to int, sorry for warning REQUIRE(strcmp(get<0>(statement), "Team BS") == 0); REQUIRE(get<1>(statement) == 20); storage.execute(statement); diff --git a/tests/prepared_statement_tests/replace.cpp b/tests/prepared_statement_tests/replace.cpp index b08291950..30a1bcabc 100644 --- a/tests/prepared_statement_tests/replace.cpp +++ b/tests/prepared_statement_tests/replace.cpp @@ -17,7 +17,7 @@ TEST_CASE("Prepared replace") { make_index("user_id_index", &User::id), make_table("users", make_column("id", &User::id, primary_key(), autoincrement()), - make_column("name", &User::name)), + make_column("name", &User::name, default_value(""))), make_table("visits", make_column("id", &Visit::id, primary_key(), autoincrement()), make_column("user_id", &Visit::userId), @@ -30,73 +30,131 @@ TEST_CASE("Prepared replace") { primary_key(&UserAndVisit::userId, &UserAndVisit::visitId))); storage.sync_schema(); - storage.replace(User{1, "Team BS"}); - storage.replace(User{2, "Shy'm"}); - storage.replace(User{3, "Maître Gims"}); - - storage.replace(UserAndVisit{2, 1, "Glad you came"}); - storage.replace(UserAndVisit{3, 1, "Shine on"}); - std::vector expected; User user; - SECTION("by ref existing") { - user = {1, "Stromae"}; - expected.push_back(User{1, "Stromae"}); - expected.push_back(User{2, "Shy'm"}); - expected.push_back(User{3, "Maître Gims"}); - auto statement = storage.prepare(replace(std::ref(user))); - storage.execute(statement); - - std::ignore = get<0>(static_cast(statement)); - REQUIRE(user == get<0>(statement)); - REQUIRE(&user == &get<0>(statement)); + SECTION("raw") { + SECTION("values") { + SECTION("one user") { + SECTION("statement") { + auto statement = storage.prepare( + replace(into(), columns(&User::id, &User::name), values(std::make_tuple(1, "Ellie")))); + storage.execute(statement); + REQUIRE(get<0>(statement) == 1); + REQUIRE(::strcmp(get<1>(statement), "Ellie") == 0); + } + SECTION("no statement") { + storage.replace(into(), columns(&User::id, &User::name), values(std::make_tuple(1, "Ellie"))); + } + expected.push_back({1, "Ellie"}); + } + SECTION("two users") { + SECTION("statement") { + auto statement = + storage.prepare(replace(into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "Ellie"), std::make_tuple(5, "Calvin")))); + storage.execute(statement); + REQUIRE(get<0>(statement) == 1); + REQUIRE(::strcmp(get<1>(statement), "Ellie") == 0); + REQUIRE(get<2>(statement) == 5); + REQUIRE(::strcmp(get<3>(statement), "Calvin") == 0); + } + SECTION("no statement") { + storage.replace(into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "Ellie"), std::make_tuple(5, "Calvin"))); + } + expected.push_back({1, "Ellie"}); + expected.push_back({5, "Calvin"}); + } + } + SECTION("default values") { + SECTION("statement") { + auto statement = storage.prepare(replace(into(), default_values())); + storage.execute(statement); + } + SECTION("no statement") { + storage.replace(into(), default_values()); + } + expected.push_back({1, ""}); + } + SECTION("select") { + SECTION("statement") { + auto statement = storage.prepare(replace(into(), select(columns(5, "Carma")))); + storage.execute(statement); + } + SECTION("no statement") { + storage.replace(into(), select(columns(5, "Carma"))); + } + expected.push_back({5, "Carma"}); + } } - SECTION("by ref new") { - user = {4, "Stromae"}; - expected.push_back(User{1, "Team BS"}); - expected.push_back(User{2, "Shy'm"}); - expected.push_back(User{3, "Maître Gims"}); - expected.push_back(user); - auto statement = storage.prepare(replace(std::ref(user))); - storage.execute(statement); - auto rows = storage.get_all(); - REQUIRE_THAT(rows, UnorderedEquals(expected)); - - user = {5, "LP"}; - expected.push_back(user); - storage.execute(statement); + SECTION("crud") { + storage.replace(User{1, "Team BS"}); + storage.replace(User{2, "Shy'm"}); + storage.replace(User{3, "Maître Gims"}); - REQUIRE(user == get<0>(statement)); - REQUIRE(&user == &get<0>(statement)); - } - SECTION("by val existing") { - SECTION("straight assign") { + storage.replace(UserAndVisit{2, 1, "Glad you came"}); + storage.replace(UserAndVisit{3, 1, "Shine on"}); + SECTION("by ref existing") { user = {1, "Stromae"}; + expected.push_back(User{1, "Stromae"}); + expected.push_back(User{2, "Shy'm"}); + expected.push_back(User{3, "Maître Gims"}); + auto statement = storage.prepare(replace(std::ref(user))); + storage.execute(statement); + + std::ignore = get<0>(static_cast(statement)); + REQUIRE(user == get<0>(statement)); + REQUIRE(&user == &get<0>(statement)); } - expected.push_back(User{1, "Stromae"}); - expected.push_back(User{2, "Shy'm"}); - expected.push_back(User{3, "Maître Gims"}); - auto statement = storage.prepare(replace(user)); - REQUIRE(&user != &get<0>(statement)); - SECTION("assign with get") { - get<0>(statement) = {1, "Stromae"}; + SECTION("by ref new") { + user = {4, "Stromae"}; + expected.push_back(User{1, "Team BS"}); + expected.push_back(User{2, "Shy'm"}); + expected.push_back(User{3, "Maître Gims"}); + expected.push_back(user); + auto statement = storage.prepare(replace(std::ref(user))); + storage.execute(statement); + auto rows = storage.get_all(); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + + user = {5, "LP"}; + expected.push_back(user); + storage.execute(statement); + + REQUIRE(user == get<0>(statement)); + REQUIRE(&user == &get<0>(statement)); } - storage.execute(statement); - } - SECTION("by val new") { - user = {4, "Stromae"}; - expected.push_back(User{1, "Team BS"}); - expected.push_back(User{2, "Shy'm"}); - expected.push_back(User{3, "Maître Gims"}); - expected.push_back(user); - auto statement = storage.prepare(replace(user)); - REQUIRE(&user != &get<0>(statement)); - storage.execute(statement); - auto rows = storage.get_all(); - REQUIRE_THAT(rows, UnorderedEquals(expected)); + SECTION("by val existing") { + SECTION("straight assign") { + user = {1, "Stromae"}; + } + expected.push_back(User{1, "Stromae"}); + expected.push_back(User{2, "Shy'm"}); + expected.push_back(User{3, "Maître Gims"}); + auto statement = storage.prepare(replace(user)); + REQUIRE(&user != &get<0>(statement)); + SECTION("assign with get") { + get<0>(statement) = {1, "Stromae"}; + } + storage.execute(statement); + } + SECTION("by val new") { + user = {4, "Stromae"}; + expected.push_back(User{1, "Team BS"}); + expected.push_back(User{2, "Shy'm"}); + expected.push_back(User{3, "Maître Gims"}); + expected.push_back(user); + auto statement = storage.prepare(replace(user)); + REQUIRE(&user != &get<0>(statement)); + storage.execute(statement); + auto rows = storage.get_all(); + REQUIRE_THAT(rows, UnorderedEquals(expected)); - user = {5, "LP"}; - storage.execute(statement); + user = {5, "LP"}; + storage.execute(statement); + } } auto rows = storage.get_all(); REQUIRE_THAT(rows, UnorderedEquals(expected)); diff --git a/tests/prepared_statement_tests/replace_range.cpp b/tests/prepared_statement_tests/replace_range.cpp index fc4bc08cf..9fc10cb50 100644 --- a/tests/prepared_statement_tests/replace_range.cpp +++ b/tests/prepared_statement_tests/replace_range.cpp @@ -38,16 +38,30 @@ TEST_CASE("Prepared replace range") { storage.replace(UserAndVisit{3, 1, "Shine on"}); std::vector users; + std::vector> userPointers; std::vector expected; + auto lambda = [](const std::unique_ptr& pointer) -> const User& { + return *pointer; + }; SECTION("empty") { expected.push_back(User{1, "Team BS"}); expected.push_back(User{2, "Shy'm"}); expected.push_back(User{3, "Maître Gims"}); - try { - auto statement = storage.prepare(replace_range(users.begin(), users.end())); - REQUIRE(false); - } catch(const std::system_error &e) { - //.. + SECTION("straight") { + try { + auto statement = storage.prepare(replace_range(users.begin(), users.end())); + REQUIRE(false); + } catch(const std::system_error& e) { + //.. + } + } + SECTION("pointers") { + try { + auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), lambda)); + REQUIRE(false); + } catch(const std::system_error& e) { + //.. + } } } SECTION("one existing") { @@ -55,11 +69,20 @@ TEST_CASE("Prepared replace range") { expected.push_back(user); expected.push_back(User{2, "Shy'm"}); expected.push_back(User{3, "Maître Gims"}); - users.push_back(user); - auto statement = storage.prepare(replace_range(users.begin(), users.end())); - REQUIRE(get<0>(statement) == users.begin()); - REQUIRE(get<1>(statement) == users.end()); - storage.execute(statement); + SECTION("straight") { + users.push_back(user); + auto statement = storage.prepare(replace_range(users.begin(), users.end())); + REQUIRE(get<0>(statement) == users.begin()); + REQUIRE(get<1>(statement) == users.end()); + storage.execute(statement); + } + SECTION("pointers") { + userPointers.push_back(std::make_unique(user)); + auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), lambda)); + REQUIRE(get<0>(statement) == userPointers.begin()); + REQUIRE(get<1>(statement) == userPointers.end()); + storage.execute(statement); + } } SECTION("one existing and one new") { User user{2, "Raye"}; @@ -68,22 +91,46 @@ TEST_CASE("Prepared replace range") { expected.push_back(user); expected.push_back(User{3, "Maître Gims"}); expected.push_back(user2); - users.push_back(user); - users.push_back(user2); - auto statement = storage.prepare(replace_range(users.begin(), users.end())); - REQUIRE(get<0>(statement) == users.begin()); - REQUIRE(get<1>(statement) == users.end()); - storage.execute(statement); + SECTION("straight") { + users.push_back(user); + users.push_back(user2); + auto statement = storage.prepare(replace_range(users.begin(), users.end())); + REQUIRE(get<0>(statement) == users.begin()); + REQUIRE(get<1>(statement) == users.end()); + storage.execute(statement); + } + SECTION("pointers") { + userPointers.push_back(std::make_unique(user)); + userPointers.push_back(std::make_unique(user2)); + auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), lambda)); + REQUIRE(get<0>(statement) == userPointers.begin()); + REQUIRE(get<1>(statement) == userPointers.end()); + storage.execute(statement); + } } SECTION("All existing") { - users.push_back(User{1, "Selena Gomez"}); - users.push_back(User{2, "Polina"}); - users.push_back(User{3, "Polina"}); + User user{1, "Selena Gomez"}; + User user2{2, "Polina"}; + User user3{3, "Polina"}; + users.push_back(user); + users.push_back(user2); + users.push_back(user3); expected = users; - auto statement = storage.prepare(replace_range(users.begin(), users.end())); - REQUIRE(get<0>(statement) == users.begin()); - REQUIRE(get<1>(statement) == users.end()); - storage.execute(statement); + SECTION("straight") { + auto statement = storage.prepare(replace_range(users.begin(), users.end())); + REQUIRE(get<0>(statement) == users.begin()); + REQUIRE(get<1>(statement) == users.end()); + storage.execute(statement); + } + SECTION("pointers") { + userPointers.push_back(std::make_unique(user)); + userPointers.push_back(std::make_unique(user2)); + userPointers.push_back(std::make_unique(user3)); + auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), lambda)); + REQUIRE(get<0>(statement) == userPointers.begin()); + REQUIRE(get<1>(statement) == userPointers.end()); + storage.execute(statement); + } } auto rows = storage.get_all(); REQUIRE_THAT(rows, UnorderedEquals(expected)); diff --git a/tests/prepared_statement_tests/select.cpp b/tests/prepared_statement_tests/select.cpp index 97b98e1bf..6ffabfba6 100644 --- a/tests/prepared_statement_tests/select.cpp +++ b/tests/prepared_statement_tests/select.cpp @@ -37,262 +37,199 @@ TEST_CASE("Prepared select") { storage.replace(UserAndVisit{2, 1, "Glad you came"}); storage.replace(UserAndVisit{3, 1, "Shine on"}); - {// one simple argument - {// by val - auto statement = storage.prepare(select(10)); - auto str = storage.dump(statement); - REQUIRE(get<0>(statement) == 10); - { - auto rows = storage.execute(statement); - REQUIRE_THAT(rows, UnorderedEquals({10})); - } - get<0>(statement) = 20; - REQUIRE(get<0>(statement) == 20); - { - auto rows = storage.execute(statement); - REQUIRE_THAT(rows, UnorderedEquals({20})); - } -} -{ // by ref - auto id = 10; - auto statement = storage.prepare(select(std::ref(id))); - auto str = storage.dump(statement); - REQUIRE(get<0>(statement) == 10); - REQUIRE(&get<0>(statement) == &id); - { - auto rows = storage.execute(statement); - REQUIRE_THAT(rows, UnorderedEquals({10})); - } - id = 20; - REQUIRE(get<0>(statement) == 20); - str = storage.dump(statement); - REQUIRE(&get<0>(statement) == &id); - { - auto rows = storage.execute(statement); - REQUIRE_THAT(rows, UnorderedEquals({20})); - } -} -} -{// two simple arguments - {// by val - auto statement = storage.prepare(select(columns("ototo", 25))); -auto str = storage.dump(statement); -REQUIRE(strcmp(get<0>(statement), "ototo") == 0); -REQUIRE(get<1>(statement) == 25); -{ - auto rows = storage.execute(statement); - REQUIRE(rows.size() == 1); - auto &row = rows.front(); - REQUIRE(get<0>(row) == "ototo"); - REQUIRE(get<1>(row) == 25); -} -get<0>(statement) = "Rock"; -get<1>(statement) = -15; -str = storage.dump(statement); -{ - auto rows = storage.execute(statement); - REQUIRE(rows.size() == 1); - auto &row = rows.front(); - REQUIRE(get<0>(row) == "Rock"); - REQUIRE(get<1>(row) == -15); -} -} -{ // by ref - std::string ototo = "ototo"; - auto id = 25; - auto statement = storage.prepare(select(columns(std::ref(ototo), std::ref(id)))); - auto str = storage.dump(statement); - REQUIRE(get<0>(statement) == "ototo"); - REQUIRE(&get<0>(statement) == &ototo); - REQUIRE(get<1>(statement) == 25); - REQUIRE(&get<1>(statement) == &id); - { - auto rows = storage.execute(statement); - REQUIRE(rows.size() == 1); - auto &row = rows.front(); - REQUIRE(get<0>(row) == "ototo"); - REQUIRE(get<1>(row) == 25); - } - ototo = "Rock"; - REQUIRE(get<0>(statement) == ototo); - REQUIRE(&get<0>(statement) == &ototo); - id = -15; - REQUIRE(get<1>(statement) == id); - REQUIRE(&get<1>(statement) == &id); - { - auto rows = storage.execute(statement); - REQUIRE(rows.size() == 1); - auto &row = rows.front(); - REQUIRE(get<0>(row) == "Rock"); - REQUIRE(get<1>(row) == -15); - } -} -} -{// three columns, aggregate func and where - {// by val - auto statement = - storage.prepare(select(columns(5.0, &User::id, count(&User::name)), where(lesser_than(&User::id, 10)))); -auto str = storage.dump(statement); -REQUIRE(get<0>(statement) == 5.0); -REQUIRE(get<1>(statement) == 10); -{ - auto rows = storage.execute(statement); - REQUIRE(rows.size() == 1); - auto &row = rows.front(); - REQUIRE(get<0>(row) == 5.0); - REQUIRE(get<2>(row) == 3); -} -get<0>(statement) = 4; -get<1>(statement) = 2; -str = storage.dump(statement); -{ - auto rows = storage.execute(statement); - REQUIRE(rows.size() == 1); - auto &row = rows.front(); - REQUIRE(get<0>(row) == 4.0); - REQUIRE(get<2>(row) == 1); -} -} -{ // by ref - auto first = 5.0; - auto id = 10; - auto statement = storage.prepare( - select(columns(std::ref(first), &User::id, count(&User::name)), where(lesser_than(&User::id, std::ref(id))))); - auto str = storage.dump(statement); - REQUIRE(get<0>(statement) == 5.0); - REQUIRE(&get<0>(statement) == &first); - REQUIRE(get<1>(statement) == 10); - REQUIRE(&get<1>(statement) == &id); - { - auto rows = storage.execute(statement); - REQUIRE(rows.size() == 1); - auto &row = rows.front(); - REQUIRE(get<0>(row) == 5.0); - REQUIRE(get<2>(row) == 3); - } - first = 4; - REQUIRE(&get<0>(statement) == &first); - id = 2; - str = storage.dump(statement); - { - auto rows = storage.execute(statement); - REQUIRE(rows.size() == 1); - auto &row = rows.front(); - REQUIRE(get<0>(row) == 4.0); - REQUIRE(get<2>(row) == 1); - } -} -} -{ - for(auto i = 0; i < 2; ++i) { - auto statement = storage.prepare(select(&User::id)); - testSerializing(statement); - SECTION("nothing") { - //.. + SECTION("one simple argument") { + SECTION("by val") { + auto statement = storage.prepare(select(10)); + auto str = storage.dump(statement); + REQUIRE(get<0>(statement) == 10); + auto rows = storage.execute(statement); + REQUIRE_THAT(rows, UnorderedEquals({10})); + get<0>(statement) = 20; + REQUIRE(get<0>(statement) == 20); + auto rows2 = storage.execute(statement); + REQUIRE_THAT(rows2, UnorderedEquals({20})); } - SECTION("execute") { + SECTION("by ref") { + auto id = 10; + auto statement = storage.prepare(select(std::ref(id))); + auto str = storage.dump(statement); + REQUIRE(get<0>(statement) == 10); + REQUIRE(&get<0>(statement) == &id); auto rows = storage.execute(statement); - REQUIRE_THAT(rows, UnorderedEquals({1, 2, 3})); + REQUIRE_THAT(rows, UnorderedEquals({10})); + id = 20; + REQUIRE(get<0>(statement) == 20); + str = storage.dump(statement); + REQUIRE(&get<0>(statement) == &id); + auto rows2 = storage.execute(statement); + REQUIRE_THAT(rows2, UnorderedEquals({20})); } } -} -{ - for(auto i = 0; i < 2; ++i) { - auto statement = storage.prepare(select(&User::name, order_by(&User::id))); - auto str = storage.dump(statement); - testSerializing(statement); - SECTION("nothing") { - //.. + SECTION("two simple arguments") { + SECTION("by val") { + auto statement = storage.prepare(select(columns("ototo", 25))); + auto str = storage.dump(statement); + REQUIRE(strcmp(get<0>(statement), "ototo") == 0); + REQUIRE(get<1>(statement) == 25); + auto rows = storage.execute(statement); + REQUIRE(rows == decltype(rows){std::make_tuple("ototo", 25)}); + get<0>(statement) = "Rock"; + get<1>(statement) = -15; + str = storage.dump(statement); + auto rows2 = storage.execute(statement); + REQUIRE(rows2 == decltype(rows2){std::make_tuple("Rock", -15)}); } - SECTION("execute") { + SECTION("by ref") { + std::string ototo = "ototo"; + auto id = 25; + auto statement = storage.prepare(select(columns(std::ref(ototo), std::ref(id)))); + auto str = storage.dump(statement); + REQUIRE(get<0>(statement) == "ototo"); + REQUIRE(&get<0>(statement) == &ototo); + REQUIRE(get<1>(statement) == 25); + REQUIRE(&get<1>(statement) == &id); auto rows = storage.execute(statement); - REQUIRE_THAT(rows, UnorderedEquals({"Team BS", "Shy'm", "Maître Gims"})); + REQUIRE(rows == decltype(rows){std::make_tuple("ototo", 25)}); + ototo = "Rock"; + REQUIRE(get<0>(statement) == ototo); + REQUIRE(&get<0>(statement) == &ototo); + id = -15; + REQUIRE(get<1>(statement) == id); + REQUIRE(&get<1>(statement) == &id); + auto rows2 = storage.execute(statement); + REQUIRE(rows2 == decltype(rows2){std::make_tuple("Rock", -15)}); } } -} -{{// by val - auto statement = storage.prepare(select(&User::id, where(length(&User::name) > 5))); -auto str = storage.dump(statement); -REQUIRE(get<0>(statement) == 5); -testSerializing(statement); -SECTION("nothing") { - //.. -} -SECTION("execute") { - auto rows = storage.execute(statement); - REQUIRE_THAT(rows, UnorderedEquals({1, 3})); -} -} -{ // by ref - auto len = 5; - auto statement = storage.prepare(select(&User::id, where(length(&User::name) > std::ref(len)))); - auto str = storage.dump(statement); - REQUIRE(get<0>(statement) == len); - REQUIRE(&get<0>(statement) == &len); - testSerializing(statement); - SECTION("nothing") { - //.. - } - SECTION("execute") { - auto rows = storage.execute(statement); - REQUIRE_THAT(rows, UnorderedEquals({1, 3})); + SECTION("three columns, aggregate func and where") { + SECTION("by val") { + auto statement = + storage.prepare(select(columns(5.0, &User::id, count(&User::name)), where(lesser_than(&User::id, 10)))); + auto str = storage.dump(statement); + REQUIRE(get<0>(statement) == 5.0); + REQUIRE(get<1>(statement) == 10); + auto rows = storage.execute(statement); + REQUIRE(rows == decltype(rows){std::make_tuple(5.0, 1, 3)}); + get<0>(statement) = 4; + get<1>(statement) = 2; + str = storage.dump(statement); + auto rows2 = storage.execute(statement); + REQUIRE(rows2 == decltype(rows2){std::make_tuple(4.0, 1, 1)}); + } + SECTION("by ref") { + auto first = 5.0; + auto id = 10; + auto statement = storage.prepare(select(columns(std::ref(first), &User::id, count(&User::name)), + where(lesser_than(&User::id, std::ref(id))))); + auto str = storage.dump(statement); + REQUIRE(get<0>(statement) == 5.0); + REQUIRE(&get<0>(statement) == &first); + REQUIRE(get<1>(statement) == 10); + REQUIRE(&get<1>(statement) == &id); + auto rows = storage.execute(statement); + REQUIRE(rows == decltype(rows){std::make_tuple(5.0, 1, 3)}); + first = 4; + REQUIRE(&get<0>(statement) == &first); + id = 2; + str = storage.dump(statement); + auto rows2 = storage.execute(statement); + REQUIRE(rows2 == decltype(rows2){std::make_tuple(4.0, 1, 1)}); + } } -} -} -{{// by val - auto statement = storage.prepare(select(&User::id, where(length(&User::name) > 5 and like(&User::name, "T%")))); -auto str = storage.dump(statement); -REQUIRE(get<0>(statement) == 5); -REQUIRE(strcmp(get<1>(statement), "T%") == 0); -testSerializing(statement); -SECTION("nothing") { - //.. -} -SECTION("execute") { - auto rows = storage.execute(statement); - REQUIRE_THAT(rows, UnorderedEquals({1})); -} -} -{ // by ref - auto len = 5; - std::string pattern = "T%"; - auto statement = storage.prepare( - select(&User::id, where(length(&User::name) > std::ref(len) and like(&User::name, std::ref(pattern))))); - auto str = storage.dump(statement); - REQUIRE(get<0>(statement) == len); - REQUIRE(&get<0>(statement) == &len); - REQUIRE(get<1>(statement) == pattern); - REQUIRE(&get<1>(statement) == &pattern); - testSerializing(statement); - SECTION("nothing") { - //.. + SECTION("serialize one column") { + for(auto i = 0; i < 2; ++i) { + auto statement = storage.prepare(select(&User::id)); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto rows = storage.execute(statement); + REQUIRE_THAT(rows, UnorderedEquals({1, 2, 3})); + } + } } - SECTION("execute") { - auto rows = storage.execute(statement); - REQUIRE_THAT(rows, UnorderedEquals({1})); + SECTION("serialize one column with order by") { + for(auto i = 0; i < 2; ++i) { + auto statement = storage.prepare(select(&User::name, order_by(&User::id))); + auto str = storage.dump(statement); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto rows = storage.execute(statement); + REQUIRE_THAT(rows, UnorderedEquals({"Team BS", "Shy'm", "Maître Gims"})); + } + } } -} -} -{ - auto statement = storage.prepare(select(columns(&User::id, &User::name))); - auto str = storage.dump(statement); - testSerializing(statement); - SECTION("nothing") { - //.. + SECTION("serialize one column with where") { + SECTION("by val") { + auto statement = storage.prepare(select(&User::id, where(length(&User::name) > 5))); + auto str = storage.dump(statement); + REQUIRE(get<0>(statement) == 5); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto rows = storage.execute(statement); + REQUIRE_THAT(rows, UnorderedEquals({1, 3})); + } + } + SECTION("by ref") { + auto len = 5; + auto statement = storage.prepare(select(&User::id, where(length(&User::name) > std::ref(len)))); + auto str = storage.dump(statement); + REQUIRE(get<0>(statement) == len); + REQUIRE(&get<0>(statement) == &len); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto rows = storage.execute(statement); + REQUIRE_THAT(rows, UnorderedEquals({1, 3})); + } + } } - SECTION("execute") { - auto rows = storage.execute(statement); - std::vector> expected; - expected.push_back(std::make_tuple(1, "Team BS")); - expected.push_back(std::make_tuple(2, "Shy'm")); - expected.push_back(std::make_tuple(3, "Maître Gims")); - REQUIRE_THAT(rows, UnorderedEquals(expected)); + SECTION("one column with where and") { + SECTION("by val") { + auto statement = + storage.prepare(select(&User::id, where(length(&User::name) > 5 and like(&User::name, "T%")))); + auto str = storage.dump(statement); + REQUIRE(get<0>(statement) == 5); + REQUIRE(strcmp(get<1>(statement), "T%") == 0); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto rows = storage.execute(statement); + REQUIRE_THAT(rows, UnorderedEquals({1})); + } + } + SECTION("by ref") { + auto len = 5; + std::string pattern = "T%"; + auto statement = storage.prepare( + select(&User::id, where(length(&User::name) > std::ref(len) and like(&User::name, std::ref(pattern))))); + auto str = storage.dump(statement); + REQUIRE(get<0>(statement) == len); + REQUIRE(&get<0>(statement) == &len); + REQUIRE(get<1>(statement) == pattern); + REQUIRE(&get<1>(statement) == &pattern); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto rows = storage.execute(statement); + REQUIRE_THAT(rows, UnorderedEquals({1})); + } + } } -} -{ - { // by val - auto statement = storage.prepare( - select(columns(&User::name, &User::id), where(is_equal(mod(&User::id, 2), 0)), order_by(&User::name))); + SECTION("two columns") { + auto statement = storage.prepare(select(columns(&User::id, &User::name))); auto str = storage.dump(statement); testSerializing(statement); SECTION("nothing") { @@ -300,32 +237,50 @@ SECTION("execute") { } SECTION("execute") { auto rows = storage.execute(statement); - std::vector> expected; - expected.push_back(std::make_tuple("Shy'm", 2)); + std::vector> expected; + expected.push_back(std::make_tuple(1, "Team BS")); + expected.push_back(std::make_tuple(2, "Shy'm")); + expected.push_back(std::make_tuple(3, "Maître Gims")); REQUIRE_THAT(rows, UnorderedEquals(expected)); } } - { // by ref - auto m = 2; - auto v = 0; - auto statement = storage.prepare(select(columns(&User::name, &User::id), - where(is_equal(mod(&User::id, std::ref(m)), std::ref(v))), - order_by(&User::name))); - auto str = storage.dump(statement); - testSerializing(statement); - REQUIRE(get<0>(statement) == m); - REQUIRE(&get<0>(statement) == &m); - REQUIRE(get<1>(statement) == v); - REQUIRE(&get<1>(statement) == &v); - SECTION("nothing") { - //.. + SECTION("two columns with where + order by") { + SECTION("by val") { + auto statement = storage.prepare( + select(columns(&User::name, &User::id), where(is_equal(mod(&User::id, 2), 0)), order_by(&User::name))); + auto str = storage.dump(statement); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto rows = storage.execute(statement); + std::vector> expected; + expected.push_back(std::make_tuple("Shy'm", 2)); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } } - SECTION("execute") { - auto rows = storage.execute(statement); - std::vector> expected; - expected.push_back(std::make_tuple("Shy'm", 2)); - REQUIRE_THAT(rows, UnorderedEquals(expected)); + SECTION("by ref") { + auto m = 2; + auto v = 0; + auto statement = storage.prepare(select(columns(&User::name, &User::id), + where(is_equal(mod(&User::id, std::ref(m)), std::ref(v))), + order_by(&User::name))); + auto str = storage.dump(statement); + testSerializing(statement); + REQUIRE(get<0>(statement) == m); + REQUIRE(&get<0>(statement) == &m); + REQUIRE(get<1>(statement) == v); + REQUIRE(&get<1>(statement) == &v); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto rows = storage.execute(statement); + std::vector> expected; + expected.push_back(std::make_tuple("Shy'm", 2)); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } } } } -} diff --git a/tests/prepared_statement_tests/update.cpp b/tests/prepared_statement_tests/update.cpp index e9753a734..aa35409b3 100644 --- a/tests/prepared_statement_tests/update.cpp +++ b/tests/prepared_statement_tests/update.cpp @@ -41,7 +41,7 @@ TEST_CASE("Prepared update") { SECTION("by ref") { auto statement = storage.prepare(update(std::ref(user))); REQUIRE(get<0>(statement) == user); - std::ignore = get<0>(static_cast(statement)); + std::ignore = get<0>(static_cast(statement)); REQUIRE(&get<0>(statement) == &user); testSerializing(statement); SECTION("nothing") { diff --git a/tests/prepared_statement_tests/update_all.cpp b/tests/prepared_statement_tests/update_all.cpp index 00efd6c32..43829ef26 100644 --- a/tests/prepared_statement_tests/update_all.cpp +++ b/tests/prepared_statement_tests/update_all.cpp @@ -46,7 +46,7 @@ TEST_CASE("Prepared update all") { static_assert(std::tuple_size::value == 1, ""); { using Arg0 = std::tuple_element<0, SetBind>::type; - static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); } REQUIRE(strcmp(get<0>(statement), "_") == 0); testSerializing(statement); diff --git a/tests/row_id.cpp b/tests/row_id.cpp new file mode 100644 index 000000000..7c3892371 --- /dev/null +++ b/tests/row_id.cpp @@ -0,0 +1,56 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Row id") { + + struct SimpleTable { + std::string letter; + std::string desc; + }; + + auto storage = make_storage( + "rowid.sqlite", + make_table("tbl1", make_column("letter", &SimpleTable::letter), make_column("desc", &SimpleTable::desc))); + storage.sync_schema(); + storage.remove_all(); + + storage.insert(SimpleTable{"A", "first letter"}); + storage.insert(SimpleTable{"B", "second letter"}); + storage.insert(SimpleTable{"C", "third letter"}); + SECTION("select everything") { + auto rows = storage.select(columns(rowid(), + oid(), + _rowid_(), + rowid(), + oid(), + _rowid_(), + &SimpleTable::letter, + &SimpleTable::desc)); + for(size_t i = 0; i < rows.size(); ++i) { + auto& row = rows[i]; + REQUIRE(std::get<0>(row) == std::get<1>(row)); + REQUIRE(std::get<1>(row) == std::get<2>(row)); + REQUIRE(std::get<2>(row) == static_cast(i + 1)); + REQUIRE(std::get<2>(row) == std::get<3>(row)); + REQUIRE(std::get<3>(row) == std::get<4>(row)); + REQUIRE(std::get<4>(row) == std::get<5>(row)); + } + } + SECTION("select single") { + std::vector> rows; + SECTION("rowid") { + rows = storage.select(max(rowid())); + } + SECTION("oid") { + rows = storage.select(max(oid())); + } + SECTION("_rowid_") { + rows = storage.select(max(_rowid_())); + } + REQUIRE(rows.size() == 1); + REQUIRE(rows[0]); + REQUIRE(*rows[0] == 3); + } +} diff --git a/tests/select_asterisk.cpp b/tests/select_asterisk.cpp deleted file mode 100644 index aa973d466..000000000 --- a/tests/select_asterisk.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include - -using namespace sqlite_orm; - -TEST_CASE("select asterisk") { - using Catch::Matchers::UnorderedEquals; - - struct Employee { - int id; - std::string name; - int age; - std::string address; // optional - double salary; // optional - - bool operator==(const Employee &other) const { - return this->id == other.id && this->name == other.name && this->age == other.age && - this->address == other.address && this->salary == other.salary; - } - }; - - auto storage = make_storage({}, - make_table("COMPANY", - make_column("ID", &Employee::id, primary_key()), - make_column("NAME", &Employee::name), - make_column("AGE", &Employee::age), - make_column("ADDRESS", &Employee::address), - make_column("SALARY", &Employee::salary))); - storage.sync_schema(); - - // create employees.. - Employee paul{-1, "Paul", 32, "California", 20000.0}; - Employee allen{-1, "Allen", 25, "Texas", 15000.0}; - Employee teddy{-1, "Teddy", 23, "Norway", 20000.0}; - Employee mark{-1, "Mark", 25, "Rich-Mond", 65000.0}; - Employee david{-1, "David", 27, "Texas", 85000.0}; - Employee kim{-1, "Kim", 22, "South-Hall", 45000.0}; - Employee james{-1, "James", 24, "Houston", 10000.0}; - - // insert employees. `insert` function returns id of inserted object.. - paul.id = storage.insert(paul); - allen.id = storage.insert(allen); - teddy.id = storage.insert(teddy); - mark.id = storage.insert(mark); - david.id = storage.insert(david); - kim.id = storage.insert(kim); - james.id = storage.insert(james); - - SECTION("asterisk") { - auto allEmployeesTuples = storage.select(asterisk()); - - std::vector> expected; - - expected.push_back(std::make_tuple(paul.id, "Paul", 32, "California", 20000.0)); - expected.push_back(std::make_tuple(allen.id, "Allen", 25, "Texas", 15000.0)); - expected.push_back(std::make_tuple(teddy.id, "Teddy", 23, "Norway", 20000.0)); - expected.push_back(std::make_tuple(mark.id, "Mark", 25, "Rich-Mond", 65000.0)); - expected.push_back(std::make_tuple(david.id, "David", 27, "Texas", 85000.0)); - expected.push_back(std::make_tuple(kim.id, "Kim", 22, "South-Hall", 45000.0)); - expected.push_back(std::make_tuple(james.id, "James", 24, "Houston", 10000.0)); - REQUIRE_THAT(allEmployeesTuples, UnorderedEquals(expected)); - } - SECTION("object") { - auto allEmployees = storage.select(object()); - std::vector expected; - expected.push_back(paul); - expected.push_back(allen); - expected.push_back(teddy); - expected.push_back(mark); - expected.push_back(david); - expected.push_back(kim); - expected.push_back(james); - REQUIRE_THAT(allEmployees, UnorderedEquals(expected)); - } -} diff --git a/tests/select_constraints_tests.cpp b/tests/select_constraints_tests.cpp new file mode 100644 index 000000000..2204c5cee --- /dev/null +++ b/tests/select_constraints_tests.cpp @@ -0,0 +1,546 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("select constraints") { + using Catch::Matchers::UnorderedEquals; + + struct Employee { + int id; + std::string name; + int age; + std::string address; // optional + double salary; // optional + + bool operator==(const Employee& other) const { + return this->id == other.id && this->name == other.name && this->age == other.age && + this->address == other.address && this->salary == other.salary; + } + }; + + auto storage = make_storage({}, + make_table("COMPANY", + make_column("ID", &Employee::id, primary_key()), + make_column("NAME", &Employee::name), + make_column("AGE", &Employee::age), + make_column("ADDRESS", &Employee::address), + make_column("SALARY", &Employee::salary))); + storage.sync_schema(); + + // create employees.. + Employee paul{-1, "Paul", 32, "California", 20000.0}; + Employee allen{-1, "Allen", 25, "Texas", 15000.0}; + Employee teddy{-1, "Teddy", 23, "Norway", 20000.0}; + Employee mark{-1, "Mark", 25, "Rich-Mond", 65000.0}; + Employee david{-1, "David", 27, "Texas", 85000.0}; + Employee kim{-1, "Kim", 22, "South-Hall", 45000.0}; + Employee james{-1, "James", 24, "Houston", 10000.0}; + + // insert employees. `insert` function returns id of inserted object.. + paul.id = storage.insert(paul); + allen.id = storage.insert(allen); + teddy.id = storage.insert(teddy); + mark.id = storage.insert(mark); + david.id = storage.insert(david); + kim.id = storage.insert(kim); + james.id = storage.insert(james); + + SECTION("select asterisk") { + SECTION("asterisk") { + auto allEmployeesTuples = storage.select(asterisk()); + + std::vector> expected; + + expected.push_back(std::make_tuple(paul.id, "Paul", 32, "California", 20000.0)); + expected.push_back(std::make_tuple(allen.id, "Allen", 25, "Texas", 15000.0)); + expected.push_back(std::make_tuple(teddy.id, "Teddy", 23, "Norway", 20000.0)); + expected.push_back(std::make_tuple(mark.id, "Mark", 25, "Rich-Mond", 65000.0)); + expected.push_back(std::make_tuple(david.id, "David", 27, "Texas", 85000.0)); + expected.push_back(std::make_tuple(kim.id, "Kim", 22, "South-Hall", 45000.0)); + expected.push_back(std::make_tuple(james.id, "James", 24, "Houston", 10000.0)); + REQUIRE_THAT(allEmployeesTuples, UnorderedEquals(expected)); + } + SECTION("object") { + auto allEmployees = storage.select(object()); + std::vector expected; + expected.push_back(paul); + expected.push_back(allen); + expected.push_back(teddy); + expected.push_back(mark); + expected.push_back(david); + expected.push_back(kim); + expected.push_back(james); + REQUIRE_THAT(allEmployees, UnorderedEquals(expected)); + } + } + SECTION("distinct") { + storage.insert(Employee{-1, "Paul", 24, "Houston", 20000.0}); + storage.insert(Employee{-1, "James", 44, "Norway", 5000.0}); + storage.insert(Employee{-1, "James", 45, "Texas", 5000.0}); + + std::vector names; + decltype(names) expected; + SECTION("without distinct") { + SECTION("without prepared statement") { + names = storage.select(&Employee::name); + } + SECTION("with prepared statement") { + auto statement = storage.prepare(select(&Employee::name)); + names = storage.execute(statement); + } + expected.push_back("Paul"); + expected.push_back("Allen"); + expected.push_back("Teddy"); + expected.push_back("Mark"); + expected.push_back("David"); + expected.push_back("Kim"); + expected.push_back("James"); + expected.push_back("Paul"); + expected.push_back("James"); + expected.push_back("James"); + } + SECTION("with distinct") { + SECTION("without prepared statement") { + names = storage.select(distinct(&Employee::name)); + } + SECTION("with prepared statement") { + auto statement = storage.prepare(select(distinct(&Employee::name))); + names = storage.execute(statement); + } + expected.push_back("Paul"); + expected.push_back("Allen"); + expected.push_back("Teddy"); + expected.push_back("Mark"); + expected.push_back("David"); + expected.push_back("Kim"); + expected.push_back("James"); + } + REQUIRE_THAT(names, UnorderedEquals(expected)); + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + SECTION("as_optional") { + SECTION("prepared statements bindings") { + auto statement = storage.prepare(select(as_optional(5))); + auto rows = storage.execute(statement); + decltype(rows) expected; + expected.push_back(5); + REQUIRE(rows == expected); + + get<0>(statement) = 10; + expected.clear(); + expected.push_back(10); + rows = storage.execute(statement); + REQUIRE(rows == expected); + } + SECTION("just names") { + auto rows = storage.select(as_optional(&Employee::name)); + decltype(rows) expected; + expected.push_back("Paul"); + expected.push_back("Allen"); + expected.push_back("Teddy"); + expected.push_back("Mark"); + expected.push_back("David"); + expected.push_back("Kim"); + expected.push_back("James"); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + SECTION("left join") { + struct Author { + int id = 0; + std::string name; + }; + struct Book { + int id = 0; + std::string title; + int authorId = 0; + }; + auto storage2 = make_storage({}, + make_table("Author", + make_column("id", &Author::id), + make_column("name", &Author::name), + primary_key(&Author::id)), + make_table("Book", + make_column("id", &Book::id), + make_column("title", &Book::title), + make_column("author_id", &Book::authorId), + primary_key(&Book::id), + foreign_key(&Book::authorId).references(&Author::id))); + storage2.sync_schema(); + storage2.replace(Author{1, "Dostoevsky"}); + storage2.replace(Author{2, "Tolstoy"}); + storage2.replace(Author{3, "Chekhov"}); + storage2.replace(Author{4, "Joanne Rowling"}); + storage2.replace(Book{1, "War and Peace", 2}); + storage2.replace(Book{2, "Crime and Punishment", 1}); + storage2.replace(Book{3, "Harry Potter", 4}); + SECTION("without optional") { + auto rows = + storage2.select(columns(&Author::id, &Author::name, &Book::id, &Book::title, &Book::authorId), + left_join(on(c(&Author::id) == &Book::authorId))); + decltype(rows) expected; + expected.push_back(std::make_tuple(1, "Dostoevsky", 2, "Crime and Punishment", 1)); + expected.push_back(std::make_tuple(2, "Tolstoy", 1, "War and Peace", 2)); + expected.push_back(std::make_tuple(3, "Chekhov", 0, std::string(), 0)); + expected.push_back(std::make_tuple(4, "Joanne Rowling", 3, "Harry Potter", 4)); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + SECTION("with optional") { + using Rows = std::vector< + std::tuple, std::optional, std::optional>>; + Rows rows; + SECTION("without prepared statement") { + rows = storage2.select(columns(&Author::id, + &Author::name, + as_optional(&Book::id), + as_optional(&Book::title), + as_optional(&Book::authorId)), + left_join(on(c(&Author::id) == &Book::authorId))); + } + SECTION("with prepared statement") { + auto statement = storage2.prepare(select(columns(&Author::id, + &Author::name, + as_optional(&Book::id), + as_optional(&Book::title), + as_optional(&Book::authorId)), + left_join(on(c(&Author::id) == &Book::authorId)))); + rows = storage2.execute(statement); + } + decltype(rows) expected; + expected.push_back(std::make_tuple(1, "Dostoevsky", 2, "Crime and Punishment", 1)); + expected.push_back(std::make_tuple(2, "Tolstoy", 1, "War and Peace", 2)); + expected.push_back(std::make_tuple(3, "Chekhov", std::nullopt, std::nullopt, std::nullopt)); + expected.push_back(std::make_tuple(4, "Joanne Rowling", 3, "Harry Potter", 4)); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} + +TEST_CASE("Exists") { + struct User { + int id = 0; + std::string name; + }; + + struct Visit { + int id = 0; + int userId = 0; + time_t time = 0; + }; + + auto storage = + make_storage("", + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)), + make_table("visits", + make_column("id", &Visit::id, primary_key()), + make_column("userId", &Visit::userId), + make_column("time", &Visit::time), + foreign_key(&Visit::userId).references(&User::id))); + storage.sync_schema(); + + storage.replace(User{1, "Daddy Yankee"}); + storage.replace(User{2, "Don Omar"}); + + storage.replace(Visit{1, 1, 100000}); + storage.replace(Visit{2, 1, 100001}); + storage.replace(Visit{3, 1, 100002}); + storage.replace(Visit{4, 1, 200000}); + + storage.replace(Visit{5, 2, 100000}); + + auto rows = storage.select( + &User::id, + where(exists(select(&Visit::id, where(c(&Visit::time) == 200000 and eq(&Visit::userId, &User::id)))))); + REQUIRE(!rows.empty() == 1); +} + +TEST_CASE("Case") { + + struct User { + int id = 0; + std::string firstName; + std::string lastName; + std::string country; + }; + + struct Track { + int id = 0; + std::string name; + long milliseconds = 0; + }; + + auto storage = make_storage({}, + make_table("users", + make_column("id", &User::id, autoincrement(), primary_key()), + make_column("first_name", &User::firstName), + make_column("last_name", &User::lastName), + make_column("country", &User::country)), + make_table("tracks", + make_column("trackid", &Track::id, autoincrement(), primary_key()), + make_column("name", &Track::name), + make_column("milliseconds", &Track::milliseconds))); + storage.sync_schema(); + + struct GradeAlias : alias_tag { + static const std::string& get() { + static const std::string res = "Grade"; + return res; + } + }; + + { + storage.insert(User{0, "Roberto", "Almeida", "Mexico"}); + storage.insert(User{0, "Julia", "Bernett", "USA"}); + storage.insert(User{0, "Camille", "Bernard", "Argentina"}); + storage.insert(User{0, "Michelle", "Brooks", "USA"}); + storage.insert(User{0, "Robet", "Brown", "USA"}); + + auto rows = storage.select( + columns(case_(&User::country).when("USA", then("Dosmetic")).else_("Foreign").end()), + multi_order_by(order_by(&User::lastName), order_by(&User::firstName))); + auto verifyRows = [&storage](auto& rows) { + REQUIRE(rows.size() == storage.count()); + REQUIRE(std::get<0>(rows[0]) == "Foreign"); + REQUIRE(std::get<0>(rows[1]) == "Foreign"); + REQUIRE(std::get<0>(rows[2]) == "Dosmetic"); + REQUIRE(std::get<0>(rows[3]) == "Dosmetic"); + REQUIRE(std::get<0>(rows[4]) == "Dosmetic"); + }; + verifyRows(rows); + + rows = storage.select( + columns(as( + case_(&User::country).when("USA", then("Dosmetic")).else_("Foreign").end())), + multi_order_by(order_by(&User::lastName), order_by(&User::firstName))); + + verifyRows(rows); + } + { + storage.insert(Track{0, "For Those About To Rock", 400000}); + storage.insert(Track{0, "Balls to the Wall", 500000}); + storage.insert(Track{0, "Fast as a Shark", 200000}); + storage.insert(Track{0, "Restless and Wild", 100000}); + storage.insert(Track{0, "Princess of the Dawn", 50000}); + + auto rows = storage.select( + case_() + .when(c(&Track::milliseconds) < 60000, then("short")) + .when(c(&Track::milliseconds) >= 60000 and c(&Track::milliseconds) < 300000, then("medium")) + .else_("long") + .end(), + order_by(&Track::name)); + auto verifyRows = [&storage](auto& rows) { + REQUIRE(rows.size() == storage.count()); + REQUIRE(rows[0] == "long"); + REQUIRE(rows[1] == "medium"); + REQUIRE(rows[2] == "long"); + REQUIRE(rows[3] == "short"); + REQUIRE(rows[4] == "medium"); + }; + verifyRows(rows); + + rows = storage.select( + as( + case_() + .when(c(&Track::milliseconds) < 60000, then("short")) + .when(c(&Track::milliseconds) >= 60000 and c(&Track::milliseconds) < 300000, then("medium")) + .else_("long") + .end()), + order_by(&Track::name)); + verifyRows(rows); + } +} + +TEST_CASE("Where") { + struct User { + int id = 0; + int age = 0; + std::string name; + }; + + auto storage = make_storage("", + make_table("users", + make_column("id", &User::id, primary_key()), + make_column("age", &User::age), + make_column("name", &User::name))); + storage.sync_schema(); + + storage.replace(User{1, 4, "Jeremy"}); + storage.replace(User{2, 18, "Nataly"}); + + auto users = storage.get_all(); + REQUIRE(users.size() == 2); + + auto users2 = storage.get_all(where(true)); + REQUIRE(users2.size() == 2); + + auto users3 = storage.get_all(where(false)); + REQUIRE(users3.size() == 0); + + auto users4 = storage.get_all(where(true and c(&User::id) == 1)); + REQUIRE(users4.size() == 1); + REQUIRE(users4.front().id == 1); + + auto users5 = storage.get_all(where(false and c(&User::id) == 1)); + REQUIRE(users5.size() == 0); + + auto users6 = storage.get_all(where((false or c(&User::id) == 4) and (false or c(&User::age) == 18))); + REQUIRE(users6.empty()); +} + +TEST_CASE("collate") { + struct User { + int id = 0; + std::string firstName; + + bool operator==(const User& user) const { + return this->id == user.id && this->firstName == user.firstName; + } + }; + auto storage = make_storage( + {}, + make_table("users", make_column("id", &User::id, primary_key()), make_column("first_name", &User::firstName))); + storage.sync_schema(); + User user1{1, "HELLO"}; + User user2{2, "Hello"}; + User user3{3, "HEllo"}; + + storage.replace(user1); + storage.replace(user2); + storage.replace(user3); + + auto rows = storage.get_all(where(is_equal(&User::firstName, "hello").collate_nocase())); + std::vector expected = {user1, user2, user3}; + REQUIRE(rows == expected); +} + +TEST_CASE("Dynamic order by") { + struct User { + int id = 0; + std::string firstName; + std::string lastName; + long registerTime = 0; + }; + + auto storage = make_storage({}, + make_table("users", + make_column("id", &User::id, primary_key()), + make_column("first_name", &User::firstName), + make_column("last_name", &User::lastName), + make_column("register_time", &User::registerTime))); + storage.sync_schema(); + + storage.replace(User{1, "Jack", "Johnson", 100}); + storage.replace(User{2, "John", "Jackson", 90}); + storage.replace(User{3, "Elena", "Alexandra", 80}); + storage.replace(User{4, "Kaye", "Styles", 70}); + + auto orderBy = dynamic_order_by(storage); + std::vector expectedIds; + + SECTION("id") { + auto ob = order_by(&User::id); + orderBy.push_back(ob); + expectedIds = { + 1, + 2, + 3, + 4, + }; + } + + SECTION("id desc") { + orderBy.push_back(order_by(&User::id).desc()); + expectedIds = { + 4, + 3, + 2, + 1, + }; + } + + SECTION("firstName") { + orderBy.push_back(order_by(&User::firstName)); + expectedIds = { + 3, + 1, + 2, + 4, + }; + } + + SECTION("firstName asc") { + orderBy.push_back(order_by(&User::firstName).asc()); + expectedIds = { + 3, + 1, + 2, + 4, + }; + } + + SECTION("firstName desc") { + orderBy.push_back(order_by(&User::firstName).desc()); + expectedIds = { + 4, + 2, + 1, + 3, + }; + } + + SECTION("firstName asc + id desc") { + orderBy.push_back(order_by(&User::firstName).asc()); + orderBy.push_back(order_by(&User::id).desc()); + expectedIds = { + 3, + 1, + 2, + 4, + }; + } + + SECTION("lastName + firstName + id") { + orderBy.push_back(order_by(&User::lastName)); + orderBy.push_back(order_by(&User::firstName)); + orderBy.push_back(order_by(&User::id)); + expectedIds = { + 3, + 2, + 1, + 4, + }; + } + + SECTION("lastName + firstName desc + id") { + orderBy.push_back(order_by(&User::lastName)); + orderBy.push_back(order_by(&User::firstName).desc()); + orderBy.push_back(order_by(&User::id)); + expectedIds = { + 3, + 2, + 1, + 4, + }; + } + + auto rows = storage.get_all(orderBy); + REQUIRE(rows.size() == 4); + for(auto i = 0; i < int(rows.size()); ++i) { + auto& row = rows[i]; + REQUIRE(row.id == expectedIds[i]); + } + orderBy.clear(); +} + +TEST_CASE("rows") { + // https://www.sqlite.org/rowvalue.html + auto storage = make_storage({}); + + auto rows = storage.select(is_equal(std::make_tuple(1, 2, 3), std::make_tuple(1, 2, 3))); + decltype(rows) expected; + expected.push_back(true); + REQUIRE(rows == expected); +} diff --git a/tests/statement_serializator_tests/arithmetic_operators.cpp b/tests/statement_serializator_tests/arithmetic_operators.cpp index 5c82413d5..8b615bfb8 100644 --- a/tests/statement_serializator_tests/arithmetic_operators.cpp +++ b/tests/statement_serializator_tests/arithmetic_operators.cpp @@ -5,54 +5,52 @@ using namespace sqlite_orm; TEST_CASE("statement_serializator arithmetic operators") { internal::serializator_context_base context; + std::string value; + decltype(value) expected; SECTION("add") { - std::string value; SECTION("func") { value = serialize(add(3, 5), context); } SECTION("operator") { value = serialize(c(3) + 5, context); } - REQUIRE(value == "(3 + 5)"); + expected = "(3 + 5)"; } SECTION("sub") { - std::string value; SECTION("func") { value = serialize(sub(5, -9), context); } SECTION("operator") { value = serialize(c(5) - -9, context); } - REQUIRE(value == "(5 - -9)"); + expected = "(5 - -9)"; } SECTION("mul") { - std::string value; SECTION("func") { value = serialize(mul(10, 0.5), context); } SECTION("operator") { value = serialize(c(10) * 0.5, context); } - REQUIRE(value == "(10 * 0.5)"); + expected = "(10 * 0.5)"; } SECTION("div") { - std::string value; SECTION("func") { value = serialize(sqlite_orm::div(10, 2), context); } SECTION("operator") { value = serialize(c(10) / 2, context); } - REQUIRE(value == "(10 / 2)"); + expected = "(10 / 2)"; } SECTION("mod") { - std::string value; SECTION("func") { value = serialize(mod(20, 3), context); } SECTION("operator") { value = serialize(c(20) % 3, context); } - REQUIRE(value == "(20 % 3)"); + expected = "(20 % 3)"; } + REQUIRE(value == expected); } diff --git a/tests/statement_serializator_tests/ast/excluded.cpp b/tests/statement_serializator_tests/ast/excluded.cpp new file mode 100644 index 000000000..63adb29f0 --- /dev/null +++ b/tests/statement_serializator_tests/ast/excluded.cpp @@ -0,0 +1,34 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("excluded") { + using internal::serialize; + struct Vocabulary { + std::string word; + int count = 0; + }; + auto table = make_table("vocabulary", + make_column("word", &Vocabulary::word, primary_key()), + make_column("count", &Vocabulary::count, default_value(1))); + using storage_impl_t = internal::storage_impl; + auto storageImpl = storage_impl_t{table}; + using context_t = internal::serializator_context; + context_t context{storageImpl}; + + std::string value; + decltype(value) expected; + SECTION("word") { + auto statement = excluded(&Vocabulary::word); + value = serialize(statement, context); + expected = "excluded.\"word\""; + } + SECTION("count") { + auto statement = excluded(&Vocabulary::count); + value = serialize(statement, context); + expected = "excluded.\"count\""; + } + + REQUIRE(value == expected); +} diff --git a/tests/statement_serializator_tests/ast/upsert_clause.cpp b/tests/statement_serializator_tests/ast/upsert_clause.cpp new file mode 100644 index 000000000..c44f65d13 --- /dev/null +++ b/tests/statement_serializator_tests/ast/upsert_clause.cpp @@ -0,0 +1,104 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("upsert_clause") { + using internal::serialize; + struct Vocabulary { + std::string word; + int count = 0; + }; + struct User { + int id = 0; + std::string firstname; + std::string lastname; + }; + auto vocabularyTable = make_table("vocabulary", + make_column("word", &Vocabulary::word, primary_key()), + make_column("count", &Vocabulary::count, default_value(1))); + auto usersTable = make_table("users", + make_column("id", &User::id, primary_key()), + make_column("firstname", &User::firstname), + make_column("lastname", &User::lastname)); + using storage_impl_t = internal::storage_impl; + auto storageImpl = storage_impl_t{vocabularyTable, usersTable}; + using context_t = internal::serializator_context; + context_t context{storageImpl}; + + std::string value; + decltype(value) expected; + SECTION("empty") { + auto expression = on_conflict().do_nothing(); + value = serialize(expression, context); + expected = "ON CONFLICT DO NOTHING"; + } + SECTION("one column") { + SECTION("1 set") { + SECTION("functions") { + auto expression = on_conflict(&Vocabulary::word) + .do_update(set(assign(&Vocabulary::count, add(&Vocabulary::count, 1)))); + value = serialize(expression, context); + } + SECTION("operators") { + auto expression = + on_conflict(&Vocabulary::word).do_update(set(c(&Vocabulary::count) = c(&Vocabulary::count) + 1)); + value = serialize(expression, context); + } + expected = "ON CONFLICT (\"word\") DO UPDATE SET \"count\" = \"count\" + 1"; + } + SECTION("2 sets") { + SECTION("fuctions") { + auto expression = on_conflict(&Vocabulary::word) + .do_update(set(assign(&Vocabulary::count, add(&Vocabulary::count, 1)), + assign(&Vocabulary::word, "abc"))); + value = serialize(expression, context); + } + SECTION("operators") { + auto expression = on_conflict(&Vocabulary::word) + .do_update(set(c(&Vocabulary::count) = c(&Vocabulary::count) + 1, + c(&Vocabulary::word) = "abc")); + value = serialize(expression, context); + } + expected = "ON CONFLICT (\"word\") DO UPDATE SET \"count\" = \"count\" + 1, \"word\" = 'abc'"; + } + } + SECTION("two columns") { + SECTION("1 set") { + SECTION("functions") { + auto expression = on_conflict(columns(&Vocabulary::word, &Vocabulary::count)) + .do_update(set(assign(&Vocabulary::count, add(&Vocabulary::count, 1)))); + value = serialize(expression, context); + } + SECTION("operators") { + auto expression = on_conflict(columns(&Vocabulary::word, &Vocabulary::count)) + .do_update(set(c(&Vocabulary::count) = c(&Vocabulary::count) + 1)); + value = serialize(expression, context); + } + expected = "ON CONFLICT (\"word\", \"count\") DO UPDATE SET \"count\" = \"count\" + 1"; + } + SECTION("2 sets") { + SECTION("fuctions") { + auto expression = on_conflict(columns(&Vocabulary::word, &Vocabulary::count)) + .do_update(set(assign(&Vocabulary::count, add(&Vocabulary::count, 1)), + assign(&Vocabulary::word, "abc"))); + value = serialize(expression, context); + } + SECTION("operators") { + auto expression = on_conflict(columns(&Vocabulary::word, &Vocabulary::count)) + .do_update(set(c(&Vocabulary::count) = c(&Vocabulary::count) + 1, + c(&Vocabulary::word) = "abc")); + value = serialize(expression, context); + } + expected = "ON CONFLICT (\"word\", \"count\") DO UPDATE SET \"count\" = \"count\" + 1, \"word\" = 'abc'"; + } + } + SECTION("with excluded") { + auto expression = on_conflict(&User::id).do_update( + set(c(&User::firstname) = excluded(&User::firstname), c(&User::lastname) = excluded(&User::lastname))); + value = serialize(expression, context); + expected = "ON CONFLICT (\"id\") DO UPDATE SET \"firstname\" = excluded.\"firstname\", \"lastname\" = " + "excluded.\"lastname\""; + } + REQUIRE(value == expected); +} diff --git a/tests/statement_serializator_tests/base_types.cpp b/tests/statement_serializator_tests/base_types.cpp index dbf9ff1db..a5edd9c35 100644 --- a/tests/statement_serializator_tests/base_types.cpp +++ b/tests/statement_serializator_tests/base_types.cpp @@ -5,28 +5,31 @@ using namespace sqlite_orm; TEST_CASE("statement_serializator base types") { internal::serializator_context_base context; + std::string stringValue; + decltype(stringValue) expected; SECTION("std::string") { std::string str("calma"); SECTION("no question") { - auto value = serialize(str, context); - REQUIRE(value == "\"calma\""); + stringValue = serialize(str, context); + expected = "\'calma\'"; } SECTION("question") { context.replace_bindable_with_question = true; - auto value = serialize(str, context); - REQUIRE(value == "?"); + stringValue = serialize(str, context); + expected = "?"; } } SECTION("const char *") { - const char *str = "baby"; + const char* str = "baby"; SECTION("no question") { - auto value = serialize(str, context); - REQUIRE(value == "\'baby\'"); + stringValue = serialize(str, context); + expected = "\'baby\'"; } SECTION("question") { context.replace_bindable_with_question = true; - auto value = serialize(str, context); - REQUIRE(value == "?"); + stringValue = serialize(str, context); + expected = "?"; } } + REQUIRE(stringValue == expected); } diff --git a/tests/statement_serializator_tests/column_names.cpp b/tests/statement_serializator_tests/column_names.cpp index 40fc3a7be..72e517e76 100644 --- a/tests/statement_serializator_tests/column_names.cpp +++ b/tests/statement_serializator_tests/column_names.cpp @@ -43,7 +43,7 @@ TEST_CASE("statement_serializator column names") { this->id = value; } - const std::string &getName() const { + const std::string& getName() const { return this->name; } diff --git a/tests/statement_serializator_tests/comparison_operators.cpp b/tests/statement_serializator_tests/comparison_operators.cpp index d4839046a..2d66437a3 100644 --- a/tests/statement_serializator_tests/comparison_operators.cpp +++ b/tests/statement_serializator_tests/comparison_operators.cpp @@ -5,8 +5,9 @@ using namespace sqlite_orm; TEST_CASE("statement_serializator comparison operators") { internal::serializator_context_base context; + std::string value; + std::string expected; SECTION("lesser_than") { - std::string value; SECTION("func") { value = serialize(lesser_than(4, 5), context); } @@ -16,10 +17,9 @@ TEST_CASE("statement_serializator comparison operators") { SECTION("operator") { value = serialize(c(4) < 5, context); } - REQUIRE(value == "(4 < 5)"); + expected = "(4 < 5)"; } SECTION("lesser_or_equal") { - std::string value; SECTION("func") { value = serialize(lesser_or_equal(10, 15), context); } @@ -29,10 +29,9 @@ TEST_CASE("statement_serializator comparison operators") { SECTION("operator") { value = serialize(c(10) <= 15, context); } - REQUIRE(value == "(10 <= 15)"); + expected = "(10 <= 15)"; } SECTION("greater_than") { - std::string value; SECTION("func") { value = serialize(greater_than(1, 0.5), context); } @@ -42,10 +41,9 @@ TEST_CASE("statement_serializator comparison operators") { SECTION("operator") { value = serialize(c(1) > 0.5, context); } - REQUIRE(value == "(1 > 0.5)"); + expected = "(1 > 0.5)"; } SECTION("greater_or_equal") { - std::string value; SECTION("func") { value = serialize(greater_or_equal(10, -5), context); } @@ -55,10 +53,9 @@ TEST_CASE("statement_serializator comparison operators") { SECTION("operator") { value = serialize(c(10) >= -5, context); } - REQUIRE(value == "(10 >= -5)"); + expected = "(10 >= -5)"; } SECTION("is_equal") { - std::string value; SECTION("func") { value = serialize(is_equal("ototo", "Hey"), context); } @@ -68,10 +65,9 @@ TEST_CASE("statement_serializator comparison operators") { SECTION("operator") { value = serialize(c("ototo") == "Hey", context); } - REQUIRE(value == "('ototo' = 'Hey')"); + expected = "('ototo' = 'Hey')"; } SECTION("is_not_equal") { - std::string value; SECTION("func") { value = serialize(is_not_equal("lala", 7), context); } @@ -81,6 +77,7 @@ TEST_CASE("statement_serializator comparison operators") { SECTION("operator") { value = serialize(c("lala") != 7, context); } - REQUIRE(value == "('lala' != 7)"); + expected = "('lala' != 7)"; } + REQUIRE(value == expected); } diff --git a/tests/statement_serializator_tests/core_functions.cpp b/tests/statement_serializator_tests/core_functions.cpp index 94c1fba17..834b133c3 100644 --- a/tests/statement_serializator_tests/core_functions.cpp +++ b/tests/statement_serializator_tests/core_functions.cpp @@ -21,6 +21,10 @@ TEST_CASE("statement_serializator core functions") { auto value = serialize(upper("call"), context); REQUIRE(value == "UPPER('call')"); } + { + auto value = serialize(total_changes(), context); + REQUIRE(value == "TOTAL_CHANGES()"); + } { auto value = serialize(changes(), context); REQUIRE(value == "CHANGES()"); diff --git a/tests/statement_serializator_tests/foreign_key.cpp b/tests/statement_serializator_tests/foreign_key.cpp index 564bcd9d1..9c937a4e8 100644 --- a/tests/statement_serializator_tests/foreign_key.cpp +++ b/tests/statement_serializator_tests/foreign_key.cpp @@ -1,6 +1,8 @@ #include #include +#include // std::is_same + using namespace sqlite_orm; #if SQLITE_VERSION_NUMBER >= 3006019 @@ -24,7 +26,10 @@ TEST_CASE("statement_serializator foreign key") { SECTION("simple") { auto fk = foreign_key(&Visit::userId).references(&User::id); - + { + using ForeignKey = decltype(fk); + static_assert(std::is_same::value, ""); + } auto visitsTable = make_table("visits", make_column("id", &Visit::id, primary_key(), autoincrement()), make_column("user_id", &Visit::userId), @@ -44,7 +49,10 @@ TEST_CASE("statement_serializator foreign key") { SECTION("on update") { SECTION("no_action") { auto fk = foreign_key(&Visit::userId).references(&User::id).on_update.no_action(); - + { + using ForeignKey = decltype(fk); + static_assert(std::is_same::value, ""); + } auto visitsTable = make_table("visits", make_column("id", &Visit::id, primary_key(), autoincrement()), make_column("user_id", &Visit::userId), @@ -63,7 +71,10 @@ TEST_CASE("statement_serializator foreign key") { } SECTION("restrict_") { auto fk = foreign_key(&Visit::userId).references(&User::id).on_update.restrict_(); - + { + using ForeignKey = decltype(fk); + static_assert(std::is_same::value, ""); + } auto visitsTable = make_table("visits", make_column("id", &Visit::id, primary_key(), autoincrement()), make_column("user_id", &Visit::userId), @@ -82,7 +93,10 @@ TEST_CASE("statement_serializator foreign key") { } SECTION("set_null") { auto fk = foreign_key(&Visit::userId).references(&User::id).on_update.set_null(); - + { + using ForeignKey = decltype(fk); + static_assert(std::is_same::value, ""); + } auto visitsTable = make_table("visits", make_column("id", &Visit::id, primary_key(), autoincrement()), make_column("user_id", &Visit::userId), @@ -101,7 +115,10 @@ TEST_CASE("statement_serializator foreign key") { } SECTION("set_default") { auto fk = foreign_key(&Visit::userId).references(&User::id).on_update.set_default(); - + { + using ForeignKey = decltype(fk); + static_assert(std::is_same::value, ""); + } auto visitsTable = make_table("visits", make_column("id", &Visit::id, primary_key(), autoincrement()), make_column("user_id", &Visit::userId), @@ -120,7 +137,10 @@ TEST_CASE("statement_serializator foreign key") { } SECTION("cascade") { auto fk = foreign_key(&Visit::userId).references(&User::id).on_update.cascade(); - + { + using ForeignKey = decltype(fk); + static_assert(std::is_same::value, ""); + } auto visitsTable = make_table("visits", make_column("id", &Visit::id, primary_key(), autoincrement()), make_column("user_id", &Visit::userId), @@ -141,7 +161,10 @@ TEST_CASE("statement_serializator foreign key") { SECTION("on delete") { SECTION("no_action") { auto fk = foreign_key(&Visit::userId).references(&User::id).on_delete.no_action(); - + { + using ForeignKey = decltype(fk); + static_assert(std::is_same::value, ""); + } auto visitsTable = make_table("visits", make_column("id", &Visit::id, primary_key(), autoincrement()), make_column("user_id", &Visit::userId), @@ -160,7 +183,10 @@ TEST_CASE("statement_serializator foreign key") { } SECTION("restrict_") { auto fk = foreign_key(&Visit::userId).references(&User::id).on_delete.restrict_(); - + { + using ForeignKey = decltype(fk); + static_assert(std::is_same::value, ""); + } auto visitsTable = make_table("visits", make_column("id", &Visit::id, primary_key(), autoincrement()), make_column("user_id", &Visit::userId), @@ -179,7 +205,10 @@ TEST_CASE("statement_serializator foreign key") { } SECTION("set_null") { auto fk = foreign_key(&Visit::userId).references(&User::id).on_delete.set_null(); - + { + using ForeignKey = decltype(fk); + static_assert(std::is_same::value, ""); + } auto visitsTable = make_table("visits", make_column("id", &Visit::id, primary_key(), autoincrement()), make_column("user_id", &Visit::userId), @@ -198,7 +227,10 @@ TEST_CASE("statement_serializator foreign key") { } SECTION("set_default") { auto fk = foreign_key(&Visit::userId).references(&User::id).on_delete.set_default(); - + { + using ForeignKey = decltype(fk); + static_assert(std::is_same::value, ""); + } auto visitsTable = make_table("visits", make_column("id", &Visit::id, primary_key(), autoincrement()), make_column("user_id", &Visit::userId), @@ -217,7 +249,10 @@ TEST_CASE("statement_serializator foreign key") { } SECTION("cascade") { auto fk = foreign_key(&Visit::userId).references(&User::id).on_delete.cascade(); - + { + using ForeignKey = decltype(fk); + static_assert(std::is_same::value, ""); + } auto visitsTable = make_table("visits", make_column("id", &Visit::id, primary_key(), autoincrement()), make_column("user_id", &Visit::userId), @@ -255,6 +290,10 @@ TEST_CASE("statement_serializator foreign key") { Object{id_}, token(std::move(token_)), usedId(usedId_) {} }; auto fk = foreign_key(&Token::usedId).references(column(&User::id)); + { + using ForeignKey = decltype(fk); + static_assert(std::is_same::value, ""); + } auto usersTable = make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)); auto tokensTable = make_table("tokens", @@ -287,17 +326,34 @@ TEST_CASE("statement_serializator foreign key") { }; auto fk = foreign_key(&UserVisit::userId, &UserVisit::userFirstName).references(&User::id, &User::firstName); + { + using ForeignKey = decltype(fk); + static_assert(std::is_same::value, ""); + } auto usersTable = make_table("users", make_column("id", &User::id), make_column("first_name", &User::firstName), make_column("last_name", &User::lastName), primary_key(&User::id, &User::firstName)); + { + static_assert(internal::storage_traits::table_foreign_keys_count::value == 0, + ""); + static_assert(internal::storage_traits::table_foreign_keys_count::value == + 0, + ""); + } auto visitsTable = make_table("visits", make_column("user_id", &UserVisit::userId), make_column("user_first_name", &UserVisit::userFirstName), make_column("time", &UserVisit::time), fk); - + { + static_assert(internal::storage_traits::table_foreign_keys_count::value == 1, + ""); + static_assert(internal::storage_traits::table_foreign_keys_count::value == + 0, + ""); + } using storage_impl_t = internal::storage_impl; storage_impl_t storageImpl{usersTable, visitsTable}; diff --git a/tests/statement_serializator_tests/index.cpp b/tests/statement_serializator_tests/index.cpp index 64e247a9a..8b1347e3e 100644 --- a/tests/statement_serializator_tests/index.cpp +++ b/tests/statement_serializator_tests/index.cpp @@ -13,7 +13,32 @@ TEST_CASE("statement_serializator index") { auto storageImpl = storage_impl_t{table}; using context_t = internal::serializator_context; context_t context{storageImpl}; - auto index = make_index("id_index", &User::id); - auto value = internal::serialize(index, context); - REQUIRE(value == "CREATE INDEX IF NOT EXISTS 'id_index' ON 'users' ('id')"); + std::string value; + decltype(value) expected; + SECTION("simple") { + auto index = make_index("id_index", &User::id); + value = internal::serialize(index, context); + expected = "CREATE INDEX IF NOT EXISTS 'id_index' ON 'users' (\"id\")"; + } + SECTION("desc") { + auto index = make_index("idx_users_id", indexed_column(&User::id).desc()); + value = internal::serialize(index, context); + expected = "CREATE INDEX IF NOT EXISTS 'idx_users_id' ON 'users' (\"id\" DESC)"; + } + SECTION("asc") { + auto index = make_index("idx_users_id", indexed_column(&User::id).asc()); + value = internal::serialize(index, context); + expected = "CREATE INDEX IF NOT EXISTS 'idx_users_id' ON 'users' (\"id\" ASC)"; + } + SECTION("collate") { + auto index = make_index("idx_users_id", indexed_column(&User::id).collate("compare")); + value = internal::serialize(index, context); + expected = "CREATE INDEX IF NOT EXISTS 'idx_users_id' ON 'users' (\"id\" COLLATE compare)"; + } + SECTION("collate asc") { + auto index = make_index("my_index", indexed_column(&User::id).collate("compare").asc()); + value = internal::serialize(index, context); + expected = "CREATE INDEX IF NOT EXISTS 'my_index' ON 'users' (\"id\" COLLATE compare ASC)"; + } + REQUIRE(value == expected); } diff --git a/tests/statement_serializator_tests/insert_replace.cpp b/tests/statement_serializator_tests/insert_replace.cpp new file mode 100644 index 000000000..e2fd375a4 --- /dev/null +++ b/tests/statement_serializator_tests/insert_replace.cpp @@ -0,0 +1,234 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator insert/replace") { + using internal::serialize; + struct User { + int id = 0; + std::string name; + }; + struct UserBackup { + int id = 0; + std::string name; + }; + auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); + auto table2 = + make_table("users_backup", make_column("id", &UserBackup::id), make_column("name", &UserBackup::name)); + using storage_impl_t = internal::storage_impl; + auto storageImpl = storage_impl_t{table, table2}; + using context_t = internal::serializator_context; + context_t context{storageImpl}; + std::string value; + decltype(value) expected; + SECTION("replace") { + SECTION("values") { + SECTION("1 row") { + auto statement = + replace(into(), columns(&User::id, &User::name), values(std::make_tuple(1, "The Weeknd"))); + value = serialize(statement, context); + expected = "REPLACE INTO users (\"id\", \"name\") VALUES (1, 'The Weeknd')"; + } + SECTION("2 rows") { + auto statement = replace(into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "The Weeknd"), std::make_tuple(4, "Jonas Blue"))); + value = serialize(statement, context); + expected = "REPLACE INTO users (\"id\", \"name\") VALUES (1, 'The Weeknd'), (4, 'Jonas Blue')"; + } + } + SECTION("default values") { + auto statement = replace(into(), default_values()); + value = serialize(statement, context); + expected = "REPLACE INTO users DEFAULT VALUES"; + } + SECTION("select") { + auto statement = replace(into(), select(columns(&UserBackup::id, &UserBackup::name))); + value = serialize(statement, context); + expected = + "REPLACE INTO users SELECT \"users_backup\".\"id\", \"users_backup\".\"name\" FROM 'users_backup'"; + } + } + SECTION("insert") { + SECTION("values") { + SECTION("1 row") { + SECTION("no constraint") { + auto statement = + insert(into(), columns(&User::id, &User::name), values(std::make_tuple(1, "The Weeknd"))); + value = serialize(statement, context); + expected = "INSERT INTO users (\"id\", \"name\") VALUES (1, 'The Weeknd')"; + } + SECTION("or abort") { + auto statement = insert(or_abort(), + into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "The Weeknd"))); + value = serialize(statement, context); + expected = "INSERT OR ABORT INTO users (\"id\", \"name\") VALUES (1, 'The Weeknd')"; + } + SECTION("or fail") { + auto statement = insert(or_fail(), + into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "The Weeknd"))); + value = serialize(statement, context); + expected = "INSERT OR FAIL INTO users (\"id\", \"name\") VALUES (1, 'The Weeknd')"; + } + SECTION("or ignore") { + auto statement = insert(or_ignore(), + into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "The Weeknd"))); + value = serialize(statement, context); + expected = "INSERT OR IGNORE INTO users (\"id\", \"name\") VALUES (1, 'The Weeknd')"; + } + SECTION("or replace") { + auto statement = insert(or_replace(), + into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "The Weeknd"))); + value = serialize(statement, context); + expected = "INSERT OR REPLACE INTO users (\"id\", \"name\") VALUES (1, 'The Weeknd')"; + } + SECTION("or rollback") { + auto statement = insert(or_rollback(), + into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "The Weeknd"))); + value = serialize(statement, context); + expected = "INSERT OR ROLLBACK INTO users (\"id\", \"name\") VALUES (1, 'The Weeknd')"; + } + } + SECTION("2 rows") { + SECTION("no constraint") { + auto statement = insert(into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "The Weeknd"), std::make_tuple(4, "Jonas Blue"))); + value = serialize(statement, context); + expected = "INSERT INTO users (\"id\", \"name\") VALUES (1, 'The Weeknd'), (4, 'Jonas Blue')"; + } + SECTION("or abort") { + auto statement = insert(or_abort(), + into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "The Weeknd"), std::make_tuple(4, "Jonas Blue"))); + value = serialize(statement, context); + expected = + "INSERT OR ABORT INTO users (\"id\", \"name\") VALUES (1, 'The Weeknd'), (4, 'Jonas Blue')"; + } + SECTION("or fail") { + auto statement = insert(or_fail(), + into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "The Weeknd"), std::make_tuple(4, "Jonas Blue"))); + value = serialize(statement, context); + expected = + "INSERT OR FAIL INTO users (\"id\", \"name\") VALUES (1, 'The Weeknd'), (4, 'Jonas Blue')"; + } + SECTION("or ignore") { + auto statement = insert(or_ignore(), + into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "The Weeknd"), std::make_tuple(4, "Jonas Blue"))); + value = serialize(statement, context); + expected = + "INSERT OR IGNORE INTO users (\"id\", \"name\") VALUES (1, 'The Weeknd'), (4, 'Jonas Blue')"; + } + SECTION("or replace") { + auto statement = insert(or_replace(), + into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "The Weeknd"), std::make_tuple(4, "Jonas Blue"))); + value = serialize(statement, context); + expected = + "INSERT OR REPLACE INTO users (\"id\", \"name\") VALUES (1, 'The Weeknd'), (4, 'Jonas Blue')"; + } + SECTION("or rollback") { + auto statement = insert(or_rollback(), + into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "The Weeknd"), std::make_tuple(4, "Jonas Blue"))); + value = serialize(statement, context); + expected = + "INSERT OR ROLLBACK INTO users (\"id\", \"name\") VALUES (1, 'The Weeknd'), (4, 'Jonas Blue')"; + } + } + } + SECTION("default values") { + SECTION("no constraint") { + auto statement = insert(into(), default_values()); + value = serialize(statement, context); + expected = "INSERT INTO users DEFAULT VALUES"; + } + SECTION("or abort") { + auto statement = insert(or_abort(), into(), default_values()); + value = serialize(statement, context); + expected = "INSERT OR ABORT INTO users DEFAULT VALUES"; + } + SECTION("or fail") { + auto statement = insert(or_fail(), into(), default_values()); + value = serialize(statement, context); + expected = "INSERT OR FAIL INTO users DEFAULT VALUES"; + } + SECTION("or ignore") { + auto statement = insert(or_ignore(), into(), default_values()); + value = serialize(statement, context); + expected = "INSERT OR IGNORE INTO users DEFAULT VALUES"; + } + SECTION("or replace") { + auto statement = insert(or_replace(), into(), default_values()); + value = serialize(statement, context); + expected = "INSERT OR REPLACE INTO users DEFAULT VALUES"; + } + SECTION("or rollback") { + auto statement = insert(or_rollback(), into(), default_values()); + value = serialize(statement, context); + expected = "INSERT OR ROLLBACK INTO users DEFAULT VALUES"; + } + } + SECTION("select") { + SECTION("no constraint") { + auto statement = insert(into(), select(columns(&UserBackup::id, &UserBackup::name))); + value = serialize(statement, context); + expected = + "INSERT INTO users SELECT \"users_backup\".\"id\", \"users_backup\".\"name\" FROM 'users_backup'"; + } + SECTION("or abort") { + auto statement = insert(or_abort(), into(), select(columns(&UserBackup::id, &UserBackup::name))); + value = serialize(statement, context); + expected = "INSERT OR ABORT INTO users SELECT \"users_backup\".\"id\", \"users_backup\".\"name\" FROM " + "'users_backup'"; + } + SECTION("or fail") { + auto statement = insert(or_fail(), into(), select(columns(&UserBackup::id, &UserBackup::name))); + value = serialize(statement, context); + expected = "INSERT OR FAIL INTO users SELECT \"users_backup\".\"id\", \"users_backup\".\"name\" FROM " + "'users_backup'"; + } + SECTION("or ignore") { + auto statement = insert(or_ignore(), into(), select(columns(&UserBackup::id, &UserBackup::name))); + value = serialize(statement, context); + expected = "INSERT OR IGNORE INTO users SELECT \"users_backup\".\"id\", \"users_backup\".\"name\" FROM " + "'users_backup'"; + } + SECTION("or replace") { + auto statement = + insert(or_replace(), into(), select(columns(&UserBackup::id, &UserBackup::name))); + value = serialize(statement, context); + expected = + "INSERT OR REPLACE INTO users SELECT \"users_backup\".\"id\", \"users_backup\".\"name\" FROM " + "'users_backup'"; + } + SECTION("or rollback") { + auto statement = + insert(or_rollback(), into(), select(columns(&UserBackup::id, &UserBackup::name))); + value = serialize(statement, context); + expected = + "INSERT OR ROLLBACK INTO users SELECT \"users_backup\".\"id\", \"users_backup\".\"name\" FROM " + "'users_backup'"; + } + } + } + REQUIRE(value == expected); +} diff --git a/tests/statement_serializator_tests/logical_operators.cpp b/tests/statement_serializator_tests/logical_operators.cpp new file mode 100644 index 000000000..25c12f1a0 --- /dev/null +++ b/tests/statement_serializator_tests/logical_operators.cpp @@ -0,0 +1,89 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator logical operators") { + struct User { + int id = 0; + std::string name; + }; + auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); + using storage_impl_t = internal::storage_impl; + auto storageImpl = storage_impl_t{table}; + using context_t = internal::serializator_context; + context_t context{storageImpl}; + std::string stringValue; + decltype(stringValue) expected; + SECTION("and") { + SECTION("simple") { + SECTION("operator") { + SECTION("c + 0") { + stringValue = internal::serialize(c(0) and 0, context); + } + SECTION("0 + c") { + stringValue = internal::serialize(0 and c(0), context); + } + } + SECTION("function") { + stringValue = internal::serialize(and_(0, 0), context); + } + SECTION("member function") { + stringValue = internal::serialize(c(0).and_(0), context); + } + expected = "(0 AND 0)"; + } + SECTION("complex") { + SECTION("operators") { + stringValue = internal::serialize(c(&User::id) == 5 and c(&User::name) == "Ariana", context); + } + SECTION("functions") { + stringValue = internal::serialize(is_equal(&User::id, 5) and is_equal(&User::name, "Ariana"), context); + } + expected = "((\"id\" = 5) AND (\"name\" = 'Ariana'))"; + } + } + SECTION("or") { + SECTION("simple") { + SECTION("function") { + stringValue = internal::serialize(or_(0, 0), context); + } + SECTION("member function") { + stringValue = internal::serialize(c(0).or_(0), context); + } + expected = "(0 OR 0)"; + } + SECTION("complex") { + SECTION("operators") { + stringValue = internal::serialize(c(&User::id) == 5 or c(&User::name) == "Ariana", context); + } + SECTION("functions") { + stringValue = internal::serialize(is_equal(&User::id, 5) or is_equal(&User::name, "Ariana"), context); + } + expected = "((\"id\" = 5) OR (\"name\" = 'Ariana'))"; + } + } + SECTION("in") { + SECTION("static in") { + auto inValue = c(&User::id).in(1, 2, 3); + stringValue = internal::serialize(inValue, context); + expected = "\"id\" IN (1, 2, 3)"; + } + SECTION("static not in") { + auto inValue = c(&User::id).not_in(1, 2, 3); + stringValue = internal::serialize(inValue, context); + expected = "\"id\" NOT IN (1, 2, 3)"; + } + SECTION("dynamic in") { + auto inValue = in(&User::id, {1, 2, 3}); + stringValue = internal::serialize(inValue, context); + expected = "\"id\" IN (1, 2, 3)"; + } + SECTION("dynamic not in") { + auto inValue = not_in(&User::id, {1, 2, 3}); + stringValue = internal::serialize(inValue, context); + expected = "\"id\" NOT IN (1, 2, 3)"; + } + } + REQUIRE(stringValue == expected); +} diff --git a/tests/statement_serializator_tests/select.cpp b/tests/statement_serializator_tests/select.cpp new file mode 100644 index 000000000..03c17ee83 --- /dev/null +++ b/tests/statement_serializator_tests/select.cpp @@ -0,0 +1,78 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator select_t") { + using internal::serialize; + struct User { + int id = 0; + std::string name; + }; + auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); + using storage_impl_t = internal::storage_impl; + storage_impl_t storageImpl{table}; + internal::serializator_context context{storageImpl}; + std::string stringValue; + decltype(stringValue) expected; + SECTION("simple") { + auto statement = select(1); + SECTION("!highest_level") { + statement.highest_level = false; + stringValue = serialize(statement, context); + expected = "(SELECT 1)"; + } + SECTION("highest_level") { + statement.highest_level = true; + stringValue = serialize(statement, context); + expected = "SELECT 1"; + } + } + SECTION("row") { + auto statement = select(is_equal(std::make_tuple(1, 2, 3), std::make_tuple(4, 5, 6))); + SECTION("!highest_level") { + statement.highest_level = false; + stringValue = serialize(statement, context); + expected = "(SELECT ((1, 2, 3) = (4, 5, 6)))"; + } + SECTION("highest_level") { + statement.highest_level = true; + stringValue = serialize(statement, context); + expected = "SELECT ((1, 2, 3) = (4, 5, 6))"; + } + } + SECTION("compound operator") { + auto statement = select(union_(select(1), select(2))); + stringValue = serialize(statement, context); + expected = "SELECT 1 UNION SELECT 2"; + } + SECTION("columns") { + SECTION("literals") { + auto statement = select(columns(1, 2)); + SECTION("!highest_level") { + statement.highest_level = false; + stringValue = serialize(statement, context); + expected = "(SELECT 1, 2)"; + } + SECTION("highest_level") { + statement.highest_level = true; + stringValue = serialize(statement, context); + expected = "SELECT 1, 2"; + } + } + SECTION("from table") { + auto statement = select(&User::id); + SECTION("!highest_level") { + statement.highest_level = false; + stringValue = serialize(statement, context); + expected = "(SELECT \"users\".\"id\" FROM 'users')"; + } + SECTION("highest_level") { + statement.highest_level = true; + stringValue = serialize(statement, context); + expected = "SELECT \"users\".\"id\" FROM 'users'"; + } + } + } + REQUIRE(stringValue == expected); +} diff --git a/tests/statement_serializator_tests/select_constraints.cpp b/tests/statement_serializator_tests/select_constraints.cpp new file mode 100644 index 000000000..81bd4b1fd --- /dev/null +++ b/tests/statement_serializator_tests/select_constraints.cpp @@ -0,0 +1,91 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator select constraints") { + using internal::serialize; + struct User { + int id = 0; + std::string name; + }; + auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); + using storage_impl_t = internal::storage_impl; + auto storageImpl = storage_impl_t{table}; + using context_t = internal::serializator_context; + context_t context{storageImpl}; + + std::string value; + decltype(value) expected; + SECTION("columns") { + auto expression = columns(&User::id, &User::name); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(\"id\", \"name\")"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "\"id\", \"name\""; + } + value = serialize(expression, context); + } + SECTION("into") { + auto expression = into(); + value = serialize(expression, context); + expected = "INTO users"; + } + SECTION("insert constraint") { + SECTION("abort") { + auto expression = or_abort(); + value = serialize(expression, context); + expected = "OR ABORT"; + } + SECTION("fail") { + auto expression = or_fail(); + value = serialize(expression, context); + expected = "OR FAIL"; + } + SECTION("ignore") { + auto expression = or_ignore(); + value = serialize(expression, context); + expected = "OR IGNORE"; + } + SECTION("replace") { + auto expression = or_replace(); + value = serialize(expression, context); + expected = "OR REPLACE"; + } + SECTION("rollback") { + auto expression = or_rollback(); + value = serialize(expression, context); + expected = "OR ROLLBACK"; + } + } + SECTION("from") { + SECTION("without alias") { + auto expression = from(); + value = serialize(expression, context); + expected = "FROM 'users'"; + } + SECTION("with alias") { + auto expression = from>(); + value = serialize(expression, context); + expected = "FROM 'users' 'u'"; + } + } + SECTION("function_call") { + struct Func { + bool operator()(int arg) const { + return arg % 2; + } + + static const char* name() { + return "EVEN"; + } + }; + auto expression = func(&User::id); + value = serialize(expression, context); + expected = "EVEN(\"id\")"; + } + REQUIRE(value == expected); +} diff --git a/tests/static_tests.cpp b/tests/static_tests.cpp deleted file mode 100644 index 6ae6bd1a9..000000000 --- a/tests/static_tests.cpp +++ /dev/null @@ -1,240 +0,0 @@ -#include -#include -#include -#include - -#include "static_tests/static_tests_common.h" - -using namespace sqlite_orm; - -TEST_CASE("Column") { - { - using column_type = decltype(make_column("id", &User::id)); - static_assert(std::tuple_size::value == 0, "Incorrect constraints_type size"); - static_assert(std::is_same::value, "Incorrect object_type"); - static_assert(std::is_same::value, "Incorrect field_type"); - static_assert(std::is_same::value, "Incorrect member pointer type"); - static_assert(std::is_same::value, - "Incorrect getter_type"); - static_assert(std::is_same::value, "Incorrect setter_type"); - } - { - using column_type = decltype(make_column("id", &User::getIdByRefConst, &User::setIdByVal)); - static_assert(std::tuple_size::value == 0, "Incorrect constraints_type size"); - static_assert(std::is_same::value, "Incorrect object_type"); - static_assert(std::is_same::value, "Incorrect field_type"); - static_assert(std::is_same::value, "Incorrect member pointer type"); - static_assert(std::is_same::value, - "Incorrect getter_type"); - static_assert(std::is_same::value, "Incorrect setter_type"); - } - { - using column_type = decltype(make_column("id", &User::setIdByVal, &User::getIdByRefConst)); - static_assert(std::tuple_size::value == 0, "Incorrect constraints_type size"); - static_assert(std::is_same::value, "Incorrect object_type"); - static_assert(std::is_same::value, "Incorrect field_type"); - static_assert(std::is_same::value, "Incorrect member pointer type"); - static_assert(std::is_same::value, - "Incorrect getter_type"); - static_assert(std::is_same::value, "Incorrect setter_type"); - } - { - using column_type = decltype(make_column("id", &User::getIdByRef, &User::setIdByConstRef)); - static_assert(std::tuple_size::value == 0, "Incorrect constraints_type size"); - static_assert(std::is_same::value, "Incorrect object_type"); - static_assert(std::is_same::value, "Incorrect field_type"); - static_assert(std::is_same::value, "Incorrect member pointer type"); - static_assert(std::is_same::value, "Incorrect getter_type"); - static_assert(std::is_same::value, - "Incorrect setter_type"); - } - { - using column_type = decltype(make_column("id", &User::setIdByConstRef, &User::getIdByRef)); - static_assert(std::tuple_size::value == 0, "Incorrect constraints_type size"); - static_assert(std::is_same::value, "Incorrect object_type"); - static_assert(std::is_same::value, "Incorrect field_type"); - static_assert(std::is_same::value, "Incorrect member pointer type"); - static_assert(std::is_same::value, "Incorrect getter_type"); - static_assert(std::is_same::value, - "Incorrect setter_type"); - } - { - using column_type = decltype(make_column("id", &User::getIdByValConst, &User::setIdByRef)); - static_assert(std::tuple_size::value == 0, "Incorrect constraints_type size"); - static_assert(std::is_same::value, "Incorrect object_type"); - static_assert(std::is_same::value, "Incorrect field_type"); - static_assert(std::is_same::value, "Incorrect member pointer type"); - static_assert(std::is_same::value, "Incorrect getter_type"); - static_assert(std::is_same::value, "Incorrect setter_type"); - } - { - using column_type = decltype(make_column("id", &User::setIdByRef, &User::getIdByValConst)); - static_assert(std::tuple_size::value == 0, "Incorrect constraints_type size"); - static_assert(std::is_same::value, "Incorrect object_type"); - static_assert(std::is_same::value, "Incorrect field_type"); - static_assert(std::is_same::value, "Incorrect member pointer type"); - static_assert(std::is_same::value, "Incorrect getter_type"); - static_assert(std::is_same::value, "Incorrect setter_type"); - } - { - using column_type = decltype(column(&Token::id)); - static_assert(std::is_same::value, "Incorrect column type"); - using field_type = column_type::field_type; - static_assert(std::is_same::value, "Incorrect field type"); - static_assert(std::is_same::type, Object>::value, "Incorrect mapped type"); - static_assert(std::is_same, field_type>::type, int>::value, - "Incorrect field type"); - static_assert(std::is_member_pointer::value, "Field type is not a member pointer"); - static_assert(!std::is_member_function_pointer::value, "Field type is not a member pointer"); - } -} - -TEST_CASE("Aggregate function return types") { - struct User { - int id; - std::string name; - - int getIdByValConst() const { - return this->id; - } - - void setIdByVal(int id_) { - this->id = id_; - } - - std::string getNameByVal() { - return this->name; - } - - void setNameByConstRef(const std::string &name_) { - this->name = name_; - } - - const int &getConstIdByRefConst() const { - return this->id; - } - - void setIdByRef(int &id_) { - this->id = id_; - } - - const std::string &getConstNameByRefConst() const { - return this->name; - } - - void setNameByRef(std::string &name_) { - this->name = std::move(name_); - } - }; - const std::string filename = "static_tests.sqlite"; - auto storage0 = make_storage( - filename, - make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name))); - auto storage1 = make_storage(filename, - make_table("users", - make_column("id", &User::getIdByValConst, &User::setIdByVal, primary_key()), - make_column("name", &User::setNameByConstRef, &User::getNameByVal))); - auto storage2 = - make_storage(filename, - make_table("users", - make_column("id", &User::getConstIdByRefConst, &User::setIdByRef, primary_key()), - make_column("name", &User::getConstNameByRefConst, &User::setNameByRef))); - static_assert(std::is_same::value, "Incorrect max value"); - static_assert(std::is_same::value, - "Incorrect max value"); - static_assert(std::is_same::value, - "Incorrect max value"); - static_assert(std::is_same::value, - "Incorrect max value"); - static_assert(std::is_same::value, - "Incorrect max value"); - - static_assert( - std::is_same::value, - "Incorrect max value"); - static_assert( - std::is_same::value, - "Incorrect max value"); - static_assert( - std::is_same::value, - "Incorrect max value"); - static_assert(std::is_same::value, - "Incorrect max value"); - static_assert( - std::is_same::value, - "Incorrect max value"); - - static_assert(std::is_same::value, "Incorrect min value"); - static_assert(std::is_same::value, - "Incorrect min value"); - static_assert(std::is_same::value, - "Incorrect min value"); - static_assert(std::is_same::value, - "Incorrect min value"); - static_assert(std::is_same::value, - "Incorrect min value"); - - static_assert( - std::is_same::value, - "Incorrect min value"); - static_assert( - std::is_same::value, - "Incorrect min value"); - static_assert( - std::is_same::value, - "Incorrect min value"); - static_assert(std::is_same::value, - "Incorrect min value"); - static_assert( - std::is_same::value, - "Incorrect min value"); - - static_assert(std::is_same::value, "Incorrect sum value"); - static_assert(std::is_same::value, - "Incorrect sum value"); - static_assert(std::is_same::value, - "Incorrect sum value"); - static_assert(std::is_same::value, - "Incorrect sum value"); - static_assert(std::is_same::value, - "Incorrect sum value"); - - static_assert( - std::is_same::value, - "Incorrect sum value"); - static_assert( - std::is_same::value, - "Incorrect sum value"); - static_assert( - std::is_same::value, - "Incorrect sum value"); - static_assert(std::is_same::value, - "Incorrect sum value"); - static_assert( - std::is_same::value, - "Incorrect sum value"); -} - -TEST_CASE("Compound operators") { - auto unionValue = union_(select(&User::id), select(&Token::id)); - static_assert(internal::is_base_of_template::value, - "union must be base of compound_operator"); - auto exceptValue = except(select(&User::id), select(&Token::id)); - static_assert(internal::is_base_of_template::value, - "except must be base of compound_operator"); -} diff --git a/tests/static_tests/aggregate_function_return_types.cpp b/tests/static_tests/aggregate_function_return_types.cpp new file mode 100644 index 000000000..2712b3278 --- /dev/null +++ b/tests/static_tests/aggregate_function_return_types.cpp @@ -0,0 +1,146 @@ +#include +#include +#include // std::is_same + +using namespace sqlite_orm; + +TEST_CASE("Aggregate function return types") { + struct User { + int id; + std::string name; + + int getIdByValConst() const { + return this->id; + } + + void setIdByVal(int id_) { + this->id = id_; + } + + std::string getNameByVal() { + return this->name; + } + + void setNameByConstRef(const std::string& name_) { + this->name = name_; + } + + const int& getConstIdByRefConst() const { + return this->id; + } + + void setIdByRef(int& id_) { + this->id = id_; + } + + const std::string& getConstNameByRefConst() const { + return this->name; + } + + void setNameByRef(std::string& name_) { + this->name = std::move(name_); + } + }; + const std::string filename = "static_tests.sqlite"; + auto storage0 = make_storage( + filename, + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name))); + auto storage1 = make_storage(filename, + make_table("users", + make_column("id", &User::getIdByValConst, &User::setIdByVal, primary_key()), + make_column("name", &User::setNameByConstRef, &User::getNameByVal))); + auto storage2 = + make_storage(filename, + make_table("users", + make_column("id", &User::getConstIdByRefConst, &User::setIdByRef, primary_key()), + make_column("name", &User::getConstNameByRefConst, &User::setNameByRef))); + static_assert(std::is_same::value, "Incorrect max value"); + static_assert(std::is_same::value, + "Incorrect max value"); + static_assert(std::is_same::value, + "Incorrect max value"); + static_assert(std::is_same::value, + "Incorrect max value"); + static_assert(std::is_same::value, + "Incorrect max value"); + + static_assert( + std::is_same::value, + "Incorrect max value"); + static_assert( + std::is_same::value, + "Incorrect max value"); + static_assert( + std::is_same::value, + "Incorrect max value"); + static_assert(std::is_same::value, + "Incorrect max value"); + static_assert( + std::is_same::value, + "Incorrect max value"); + + static_assert(std::is_same::value, "Incorrect min value"); + static_assert(std::is_same::value, + "Incorrect min value"); + static_assert(std::is_same::value, + "Incorrect min value"); + static_assert(std::is_same::value, + "Incorrect min value"); + static_assert(std::is_same::value, + "Incorrect min value"); + + static_assert( + std::is_same::value, + "Incorrect min value"); + static_assert( + std::is_same::value, + "Incorrect min value"); + static_assert( + std::is_same::value, + "Incorrect min value"); + static_assert(std::is_same::value, + "Incorrect min value"); + static_assert( + std::is_same::value, + "Incorrect min value"); + + static_assert(std::is_same::value, "Incorrect sum value"); + static_assert(std::is_same::value, + "Incorrect sum value"); + static_assert(std::is_same::value, + "Incorrect sum value"); + static_assert(std::is_same::value, + "Incorrect sum value"); + static_assert(std::is_same::value, + "Incorrect sum value"); + + static_assert( + std::is_same::value, + "Incorrect sum value"); + static_assert( + std::is_same::value, + "Incorrect sum value"); + static_assert( + std::is_same::value, + "Incorrect sum value"); + static_assert(std::is_same::value, + "Incorrect sum value"); + static_assert( + std::is_same::value, + "Incorrect sum value"); +} diff --git a/tests/static_tests/column.cpp b/tests/static_tests/column.cpp new file mode 100644 index 000000000..0cf5a63b7 --- /dev/null +++ b/tests/static_tests/column.cpp @@ -0,0 +1,88 @@ +#include +#include + +#include "static_tests_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Column") { + { + using column_type = decltype(make_column("id", &User::id)); + static_assert(std::tuple_size::value == 0, "Incorrect constraints_type size"); + static_assert(std::is_same::value, "Incorrect object_type"); + static_assert(std::is_same::value, "Incorrect field_type"); + static_assert(std::is_same::value, "Incorrect member pointer type"); + static_assert(std::is_same::value, + "Incorrect getter_type"); + static_assert(std::is_same::value, "Incorrect setter_type"); + } + { + using column_type = decltype(make_column("id", &User::getIdByRefConst, &User::setIdByVal)); + static_assert(std::tuple_size::value == 0, "Incorrect constraints_type size"); + static_assert(std::is_same::value, "Incorrect object_type"); + static_assert(std::is_same::value, "Incorrect field_type"); + static_assert(std::is_same::value, "Incorrect member pointer type"); + static_assert(std::is_same::value, + "Incorrect getter_type"); + static_assert(std::is_same::value, "Incorrect setter_type"); + } + { + using column_type = decltype(make_column("id", &User::setIdByVal, &User::getIdByRefConst)); + static_assert(std::tuple_size::value == 0, "Incorrect constraints_type size"); + static_assert(std::is_same::value, "Incorrect object_type"); + static_assert(std::is_same::value, "Incorrect field_type"); + static_assert(std::is_same::value, "Incorrect member pointer type"); + static_assert(std::is_same::value, + "Incorrect getter_type"); + static_assert(std::is_same::value, "Incorrect setter_type"); + } + { + using column_type = decltype(make_column("id", &User::getIdByRef, &User::setIdByConstRef)); + static_assert(std::tuple_size::value == 0, "Incorrect constraints_type size"); + static_assert(std::is_same::value, "Incorrect object_type"); + static_assert(std::is_same::value, "Incorrect field_type"); + static_assert(std::is_same::value, "Incorrect member pointer type"); + static_assert(std::is_same::value, "Incorrect getter_type"); + static_assert(std::is_same::value, + "Incorrect setter_type"); + } + { + using column_type = decltype(make_column("id", &User::setIdByConstRef, &User::getIdByRef)); + static_assert(std::tuple_size::value == 0, "Incorrect constraints_type size"); + static_assert(std::is_same::value, "Incorrect object_type"); + static_assert(std::is_same::value, "Incorrect field_type"); + static_assert(std::is_same::value, "Incorrect member pointer type"); + static_assert(std::is_same::value, "Incorrect getter_type"); + static_assert(std::is_same::value, + "Incorrect setter_type"); + } + { + using column_type = decltype(make_column("id", &User::getIdByValConst, &User::setIdByRef)); + static_assert(std::tuple_size::value == 0, "Incorrect constraints_type size"); + static_assert(std::is_same::value, "Incorrect object_type"); + static_assert(std::is_same::value, "Incorrect field_type"); + static_assert(std::is_same::value, "Incorrect member pointer type"); + static_assert(std::is_same::value, "Incorrect getter_type"); + static_assert(std::is_same::value, "Incorrect setter_type"); + } + { + using column_type = decltype(make_column("id", &User::setIdByRef, &User::getIdByValConst)); + static_assert(std::tuple_size::value == 0, "Incorrect constraints_type size"); + static_assert(std::is_same::value, "Incorrect object_type"); + static_assert(std::is_same::value, "Incorrect field_type"); + static_assert(std::is_same::value, "Incorrect member pointer type"); + static_assert(std::is_same::value, "Incorrect getter_type"); + static_assert(std::is_same::value, "Incorrect setter_type"); + } + { + using column_type = decltype(column(&Token::id)); + static_assert(std::is_same::value, "Incorrect column type"); + using field_type = column_type::field_type; + static_assert(std::is_same::value, "Incorrect field type"); + static_assert(std::is_same::type, Object>::value, "Incorrect mapped type"); + static_assert(std::is_same, field_type>::type, int>::value, + "Incorrect field type"); + static_assert(std::is_member_pointer::value, "Field type is not a member pointer"); + static_assert(!std::is_member_function_pointer::value, "Field type is not a member pointer"); + } +} diff --git a/tests/static_tests/column_result_t.cpp b/tests/static_tests/column_result_t.cpp new file mode 100644 index 000000000..946eeb61c --- /dev/null +++ b/tests/static_tests/column_result_t.cpp @@ -0,0 +1,104 @@ +#include +#include +#include // std::is_same + +using namespace sqlite_orm; + +template +void runTest(V value) { + using Type = typename internal::column_result_t::type; + static_assert(std::is_same::value, ""); +} + +TEST_CASE("column_result_t") { + struct User { + int id = 0; + std::string name; + }; + + struct Visit { + void setId(int value) { + this->id = value; + } + + int getId() const { + return this->id; + } + + void setComment(std::string comment) { + this->comment = move(comment); + } + + const std::string &getComment() const { + return this->comment; + } + + private: + int id = 0; + std::string comment; + }; + auto storage = make_storage({}); + + using Storage = decltype(storage); + runTest(&User::id); + runTest(&User::name); + runTest(in(&User::id, {1, 2, 3})); + { + std::vector vector; + vector.push_back(1); + vector.push_back(2); + vector.push_back(3); + runTest(in(&User::id, vector)); + } + runTest(in(&User::id, select(&User::id))); + runTest(c(&User::id).in(1, 2, 3)); + runTest(&Visit::getId); + runTest(&Visit::getComment); + runTest(&Visit::setId); + runTest(&Visit::setComment); + runTest>(sqlite_orm::abs(&User::id)); + runTest(sqlite_orm::length(&User::id)); + runTest(sqlite_orm::unicode(&User::id)); + runTest(sqlite_orm::typeof_(&User::id)); + runTest(sqlite_orm::lower(&User::id)); + runTest(sqlite_orm::upper(&User::id)); + runTest>(max(&User::id)); + runTest>(max(&User::name)); + runTest(count()); + runTest(count()); + { + struct RandomFunc { + int operator()() const { + return 4; + } + }; + runTest(func()); + } + runTest(distinct(&User::id)); + runTest(distinct(&User::name)); + runTest(all(&User::id)); + runTest(all(&User::name)); + runTest(conc(&User::name, &User::id)); + runTest(c(&User::name) || &User::id); + runTest(add(&User::id, 5)); + runTest(c(&User::id) + 5); + runTest(sub(&User::id, 5)); + runTest(c(&User::id) - 5); + runTest(mul(&User::id, 5)); + runTest(c(&User::id) * 5); + runTest(sqlite_orm::div(&User::id, 5)); + runTest(c(&User::id) / 5); + runTest(mod(&User::id, 5)); + runTest(c(&User::id) % 5); + runTest(bitwise_shift_left(&User::id, 4)); + runTest(bitwise_shift_right(&User::id, 4)); + runTest(bitwise_and(&User::id, 4)); + runTest(bitwise_or(&User::id, 4)); + runTest(bitwise_not(&User::id)); + runTest(rowid()); + runTest(oid()); + runTest(_rowid_()); + runTest(rowid()); + runTest(oid()); + runTest(_rowid_()); +} diff --git a/tests/static_tests/compound_operators.cpp b/tests/static_tests/compound_operators.cpp new file mode 100644 index 000000000..78d9c9f92 --- /dev/null +++ b/tests/static_tests/compound_operators.cpp @@ -0,0 +1,15 @@ +#include +#include + +#include "static_tests_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Compound operators") { + auto unionValue = union_(select(&User::id), select(&Token::id)); + static_assert(internal::is_base_of_template::value, + "union must be base of compound_operator"); + auto exceptValue = except(select(&User::id), select(&Token::id)); + static_assert(internal::is_base_of_template::value, + "except must be base of compound_operator"); +} diff --git a/tests/static_tests/find_in_tuple.cpp b/tests/static_tests/find_in_tuple.cpp new file mode 100644 index 000000000..13362020e --- /dev/null +++ b/tests/static_tests/find_in_tuple.cpp @@ -0,0 +1,28 @@ +#include +#include +#include // std::is_same + +#include "static_tests_common.h" + +using namespace sqlite_orm; + +TEST_CASE("find_in_tuple") { + using namespace internal; + using tuple = std::tuple, columns_t>; + { + using found = find_in_tuple::type; + static_assert(std::is_same>::value, ""); + } + { + using found = find_in_tuple::type; + static_assert(std::is_same>::value, ""); + } + { + using found = find_in_tuple::type; + static_assert(std::is_same::value, ""); + } + { + using found = find_in_tuple::type; + static_assert(std::is_same::value, ""); + } +} diff --git a/tests/static_tests/foreign_key.cpp b/tests/static_tests/foreign_key.cpp new file mode 100644 index 000000000..fee5d6742 --- /dev/null +++ b/tests/static_tests/foreign_key.cpp @@ -0,0 +1,135 @@ +#include +#include + +#include // std::is_same +#include // std::string +#include // std::is_same + +using namespace sqlite_orm; + +TEST_CASE("foreign key static") { + struct FunctionDecl { + std::string function_name; + std::string file_path; + std::string return_type; + }; + struct FunctionDef { + std::string function_name; + std::string file_path; + std::string return_type; + }; + struct FunctionCall { + unsigned int line_num; + std::string called_function_name; + std::string parent_function_name; + }; + struct File { + std::string path; + }; + struct CppInclusion { + std::string includer_path; + std::string includee_path; + }; + struct VarDecl { + int id; + std::string name; + std::string type; + bool is_global; + std::string file_path; + }; + auto cppInclusionIncluderPathFk = foreign_key(&CppInclusion::includer_path).references(&File::path); + static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); + + auto cppInclusionIncludeePathFk = foreign_key(&CppInclusion::includee_path).references(&File::path); + static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); + + auto functionDeclFilePathFk = foreign_key(&FunctionDecl::file_path).references(&File::path); + static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); + + auto varDeclFilePathFk = foreign_key(&VarDecl::file_path).references(&File::path); + static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); + + auto functionDefFilePathFk = foreign_key(&FunctionDef::file_path).references(&File::path); + static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); + + auto functionCallParentFunctionName = + foreign_key(&FunctionCall::parent_function_name).references(&FunctionDef::function_name); + static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); + + auto storage = make_storage({}, + make_table("files", make_column("path", &File::path, primary_key())), + make_table("c_inclusions", + make_column("includer_path", &CppInclusion::includer_path), + make_column("includee_path", &CppInclusion::includee_path), + primary_key(&CppInclusion::includer_path, &CppInclusion::includee_path), + cppInclusionIncluderPathFk, + cppInclusionIncludeePathFk), + make_table("func_decls", + make_column("name", &FunctionDecl::function_name), + make_column("file_path", &FunctionDecl::file_path), + make_column("return_type", &FunctionDecl::return_type), + functionDeclFilePathFk, + primary_key(&FunctionDecl::function_name, &FunctionDecl::file_path)), + make_table("var_decls", + make_column("id", &VarDecl::id, autoincrement(), primary_key()), + make_column("name", &VarDecl::name), + make_column("type", &VarDecl::type), + make_column("is_global", &VarDecl::is_global), + make_column("file_path", &VarDecl::file_path), + varDeclFilePathFk), + make_table("func_defs", + make_column("name", &FunctionDef::function_name), + make_column("file_path", &FunctionDef::file_path), + make_column("return_type", &FunctionDef::return_type), + functionDefFilePathFk, + primary_key(&FunctionDef::function_name)), + make_table("func_calls", + make_column("line_num", &FunctionCall::line_num), + make_column("called_func_name", &FunctionCall::called_function_name), + make_column("parent_func_name", &FunctionCall::parent_function_name), + functionCallParentFunctionName, + primary_key(&FunctionCall::called_function_name, + &FunctionCall::parent_function_name, + &FunctionCall::line_num))); + storage.sync_schema(); + + using Storage = decltype(storage); + + using namespace sqlite_orm::internal::storage_traits; + { + using FkTuple = storage_fk_references::type; + using Expected = std::tuple<>; + static_assert(std::is_same::value, ""); + } + { + using FkTuple = storage_fk_references::type; + using Expected = std::tuple; + static_assert(std::is_same::value, ""); + } + { + using FkTuple = storage_fk_references::type; + using Expected = std::tuple<>; + static_assert(std::is_same::value, ""); + } + { + using FkTuple = storage_fk_references::type; + using Expected = std::tuple<>; + static_assert(std::is_same::value, ""); + } + { + using FkTuple = storage_fk_references::type; + using Expected = std::tuple<>; + static_assert(std::is_same::value, ""); + } + { + using FkTuple = storage_fk_references::type; + using Expected = std::tuple; + static_assert(std::is_same::value, ""); + } +} diff --git a/tests/static_tests/function_static_tests.cpp b/tests/static_tests/function_static_tests.cpp new file mode 100644 index 000000000..73e0f70c0 --- /dev/null +++ b/tests/static_tests/function_static_tests.cpp @@ -0,0 +1,231 @@ +#include +#include +#include // std::is_same + +using namespace sqlite_orm; + +TEST_CASE("function static") { + SECTION("scalar") { + SECTION("variadic") { + struct FirstFunction { + std::string operator()(const arg_values &args) const { + std::string res; + res.reserve(args.size()); + for(auto value: args) { + auto stringValue = value.get(); + if(!stringValue.empty()) { + res += stringValue.front(); + } + } + return res; + } + + static const char *name() { + return "FIRST"; + } + }; + } + SECTION("non variadic") { + SECTION("double(double) const") { + struct Function { + double operator()(double arg) const { + return std::sqrt(arg); + } + }; + + static_assert(internal::is_scalar_function::value, ""); + static_assert(!internal::is_aggregate_function::value, ""); + + using RunMemberFunctionPointer = internal::scalar_run_member_pointer::type; + using ExpectedType = double (Function::*)(double) const; + static_assert(std::is_same::value, ""); + + using ArgumentsTuple = internal::member_function_arguments::tuple_type; + using ExpectedArgumentsTuple = std::tuple; + static_assert(std::is_same::value, ""); + + static_assert(std::is_same::return_type, double>::value, ""); + static_assert( + std::is_same::args_tuple, std::tuple>::value, + ""); + } + SECTION("double(double)") { + struct Function { + double operator()(double arg) { + return std::sqrt(arg); + } + }; + + static_assert(internal::is_scalar_function::value, ""); + static_assert(!internal::is_aggregate_function::value, ""); + + using RunMemberFunctionPointer = internal::scalar_run_member_pointer::type; + using ExpectedType = double (Function::*)(double); + static_assert(std::is_same::value, ""); + + using ArgumentsTuple = internal::member_function_arguments::tuple_type; + using ExpectedArgumentsTuple = std::tuple; + static_assert(std::is_same::value, ""); + + static_assert(std::is_same::return_type, double>::value, ""); + static_assert( + std::is_same::args_tuple, std::tuple>::value, + ""); + } + SECTION("int(std::string) const") { + struct Function { + int operator()(std::string arg) const { + return int(arg.length()); + } + }; + + static_assert(internal::is_scalar_function::value, ""); + static_assert(!internal::is_aggregate_function::value, ""); + + using RunMemberFunctionPointer = internal::scalar_run_member_pointer::type; + using ExpectedType = int (Function::*)(std::string) const; + static_assert(std::is_same::value, ""); + + using ArgumentsTuple = internal::member_function_arguments::tuple_type; + using ExpectedArgumentsTuple = std::tuple; + static_assert(std::is_same::value, ""); + + static_assert(std::is_same::return_type, int>::value, ""); + static_assert( + std::is_same::args_tuple, std::tuple>::value, + ""); + } + SECTION("int(std::string)") { + struct Function { + int operator()(std::string arg) { + return int(arg.length()); + } + }; + + static_assert(internal::is_scalar_function::value, ""); + static_assert(!internal::is_aggregate_function::value, ""); + + using RunMemberFunctionPointer = internal::scalar_run_member_pointer::type; + using ExpectedType = int (Function::*)(std::string); + static_assert(std::is_same::value, ""); + + using ArgumentsTuple = internal::member_function_arguments::tuple_type; + using ExpectedArgumentsTuple = std::tuple; + static_assert(std::is_same::value, ""); + + static_assert(std::is_same::return_type, int>::value, ""); + static_assert( + std::is_same::args_tuple, std::tuple>::value, + ""); + } + SECTION("std::string(const std::string &, const std::string &) const") { + struct Function { + std::string operator()(const std::string &arg1, const std::string &arg2) const { + return arg1 + arg2; + } + }; + + static_assert(internal::is_scalar_function::value, ""); + static_assert(!internal::is_aggregate_function::value, ""); + + using RunMemberFunctionPointer = internal::scalar_run_member_pointer::type; + using ExpectedType = std::string (Function::*)(const std::string &, const std::string &) const; + static_assert(std::is_same::value, ""); + + using ArgumentsTuple = internal::member_function_arguments::tuple_type; + using ExpectedArgumentsTuple = std::tuple; + static_assert(std::is_same::value, ""); + + static_assert(std::is_same::return_type, std::string>::value, + ""); + static_assert(std::is_same::args_tuple, + std::tuple>::value, + ""); + } + SECTION("std::string(const std::string &, const std::string &)") { + struct Function { + std::string operator()(const std::string &arg1, const std::string &arg2) { + return arg1 + arg2; + } + }; + + static_assert(internal::is_scalar_function::value, ""); + static_assert(!internal::is_aggregate_function::value, ""); + + using RunMemberFunctionPointer = internal::scalar_run_member_pointer::type; + using ExpectedType = std::string (Function::*)(const std::string &, const std::string &); + static_assert(std::is_same::value, ""); + + using ArgumentsTuple = internal::member_function_arguments::tuple_type; + using ExpectedArgumentsTuple = std::tuple; + static_assert(std::is_same::value, ""); + + static_assert(std::is_same::return_type, std::string>::value, + ""); + static_assert(std::is_same::args_tuple, + std::tuple>::value, + ""); + } + } + } + SECTION("aggregate") { + SECTION("void(int) & int() const") { + struct Function { + int total = 0; + int count = 0; + + void step(int value) { + ++count; + total += value; + } + + int fin() const { + return total; + } + }; + + static_assert(internal::is_aggregate_function::value, ""); + static_assert(!internal::is_scalar_function::value, ""); + + using StepMemberFunctionPointer = internal::aggregate_run_member_pointer::step_type; + using ExpectedStepType = void (Function::*)(int); + static_assert(std::is_same::value, ""); + + using FinMemberFunctionPointer = internal::aggregate_run_member_pointer::fin_type; + using ExpectedFinType = int (Function::*)() const; + static_assert(std::is_same::value, ""); + + static_assert(std::is_same::return_type, int>::value, ""); + static_assert(std::is_same::args_tuple, std::tuple>::value, ""); + } + SECTION("void(std::string) const & std::string()") { + struct Function { + mutable std::string result; + + void step(std::string value) const { + result += value[0]; + } + + std::string fin() { + return move(result); + } + }; + + static_assert(internal::is_aggregate_function::value, ""); + static_assert(!internal::is_scalar_function::value, ""); + + using StepMemberFunctionPointer = internal::aggregate_run_member_pointer::step_type; + using ExpectedStepType = void (Function::*)(std::string) const; + static_assert(std::is_same::value, ""); + + using FinMemberFunctionPointer = internal::aggregate_run_member_pointer::fin_type; + using ExpectedFinType = std::string (Function::*)(); + static_assert(std::is_same::value, ""); + + static_assert(std::is_same::return_type, std::string>::value, ""); + static_assert( + std::is_same::args_tuple, std::tuple>::value, + ""); + } + } +} diff --git a/tests/static_tests/has_some_type.cpp b/tests/static_tests/has_some_type.cpp new file mode 100644 index 000000000..1933d3490 --- /dev/null +++ b/tests/static_tests/has_some_type.cpp @@ -0,0 +1,23 @@ +#include +#include + +using namespace sqlite_orm; + +namespace { + template + class my_vector : public std::vector { + using super = std::vector; + + public: + using super::super; + }; +} // end of anonymous namespace + +TEST_CASE("has_some_type") { + using empty_tuple_type = std::tuple<>; + using tuple_type = std::tuple, std::string>; + + static_assert(tuple_helper::has_some_type::value, ""); + static_assert(!tuple_helper::has_some_type::value, ""); + static_assert(!tuple_helper::has_some_type::value, ""); +} \ No newline at end of file diff --git a/tests/static_tests/is_bindable.cpp b/tests/static_tests/is_bindable.cpp index fe9169f81..b1c5c74ca 100644 --- a/tests/static_tests/is_bindable.cpp +++ b/tests/static_tests/is_bindable.cpp @@ -58,10 +58,10 @@ TEST_CASE("is_bindable") { bool falseCalled = false; auto dummy = 5; // for gcc compilation internal::static_if{}>( - [&trueCalled](int &) { + [&trueCalled](int&) { trueCalled = true; }, - [&falseCalled](int &) { + [&falseCalled](int&) { falseCalled = true; })(dummy); REQUIRE(!trueCalled); diff --git a/tests/static_tests/is_column_with_insertable_primary_key.cpp b/tests/static_tests/is_column_with_insertable_primary_key.cpp new file mode 100644 index 000000000..f519266dc --- /dev/null +++ b/tests/static_tests/is_column_with_insertable_primary_key.cpp @@ -0,0 +1,46 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("is_column_with_insertable_primary_key") { + struct User { + int id; + std::string username; + std::string password; + bool isActive; + }; + + auto insertable = std::make_tuple( /// + make_column("", &User::id, primary_key()), + make_column("", &User::username, primary_key(), default_value("Clint Eastwood")), + make_column("", &User::username, primary_key(), default_value(std::vector{})), + make_column("", &User::username, primary_key(), autoincrement())); + + auto noninsertable = std::make_tuple( /// + make_column("", &User::username, primary_key()), + make_column("", &User::password, primary_key())); + + auto outside = std::make_tuple( /// + make_column("", &User::id), ///< not a primary key + std::make_shared() ///< not a column + ); + + iterate_tuple(insertable, [](auto& v) { + static_assert(internal::is_column_with_insertable_primary_key::type>::value, + ""); + }); + + iterate_tuple(noninsertable, [](auto& v) { + static_assert(internal::is_column_with_noninsertable_primary_key::type>::value, + ""); + }); + + iterate_tuple(outside, [](auto& v) { + static_assert(!internal::is_column_with_insertable_primary_key::type>::value, + ""); + static_assert( + !internal::is_column_with_noninsertable_primary_key::type>::value, + ""); + }); +} \ No newline at end of file diff --git a/tests/static_tests/is_primary_key_insertable.cpp b/tests/static_tests/is_primary_key_insertable.cpp new file mode 100644 index 000000000..fee3766dd --- /dev/null +++ b/tests/static_tests/is_primary_key_insertable.cpp @@ -0,0 +1,31 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("is_primary_key_insertable") { + struct User { + int id; + std::string username; + std::string password; + bool isActive; + }; + + auto insertable = std::make_tuple( /// + make_column("", &User::id, primary_key()), + make_column("", &User::username, primary_key(), default_value("Clint Eastwood")), + make_column("", &User::username, primary_key(), default_value(std::vector{})), + make_column("", &User::username, primary_key(), autoincrement())); + + auto noninsertable = std::make_tuple( /// + make_column("", &User::username, primary_key()), + make_column("", &User::password, primary_key())); + + iterate_tuple(insertable, [](auto& v) { + static_assert(internal::is_primary_key_insertable::type>::value, ""); + }); + + iterate_tuple(noninsertable, [](auto& v) { + static_assert(!internal::is_primary_key_insertable::type>::value, ""); + }); +} \ No newline at end of file diff --git a/tests/static_tests/iterator_t.cpp b/tests/static_tests/iterator_t.cpp new file mode 100644 index 000000000..7add69436 --- /dev/null +++ b/tests/static_tests/iterator_t.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +using namespace sqlite_orm; + +struct User { + int id = 0; + std::string name; +}; + +TEST_CASE("iterator_t") { + using storage = decltype(make_storage( + "aPath", + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)))); + using iter = decltype(std::declval().iterate().begin()); + + // weakly_incrementable + static_assert(std::is_default_constructible::value, "needs to be default constructible"); + static_assert(std::is_same::value, "needs to have difference_type"); + static_assert(std::is_same()), iter&>::value, "needs to be incrementable"); + using check = decltype(std::declval()++); + + // indirectly_readable + static_assert(std::is_same()), const User&>::value, + "needs to be const dereferencable"); + + // input_iterator + static_assert(std::is_same::value, + "needs to have iterator_category"); + + // sentinel + static_assert(std::is_same() == std::declval()), bool>::value, + "supports equality checking"); +} diff --git a/tests/static_tests/member_traits_tests.cpp b/tests/static_tests/member_traits_tests.cpp index 4a5a335c5..9ff66f11f 100644 --- a/tests/static_tests/member_traits_tests.cpp +++ b/tests/static_tests/member_traits_tests.cpp @@ -14,56 +14,157 @@ TEST_CASE("member_traits_tests") { using std::is_same; struct User { - int id; + mutable int id = 0; - const int &getIdByRefConst() const { + // getter_by_value + int getIdByValConst() const { return this->id; } - const int &getIdByRef() { + // getter_by_ref_const + int& getIdByRefConst() const { return this->id; } - int getIdByValConst() const { + // getter_by_ref_const + int& getIdByRef() { + return this->id; + } + + // getter_by_const_ref_const + const int& getIdByConstRefConst() const { + return this->id; + } + + // getter_by_const_ref + const int& getIdByConstRef() { + return this->id; + } + + // getter_by_value_const_noexcept + int getIdByValConstNoexcept() const noexcept { + return this->id; + } + + // getter_by_value_noexcept + int getIdByValNoexcept() noexcept { + return this->id; + } + + // getter_by_ref_const_noexcept + int& getIdByRefConstNoexcept() const noexcept { + return this->id; + } + + // getter_by_ref_noexcept + int& getIdByRefNoexcept() noexcept { return this->id; } + // getter_by_const_ref_const_noexcept + const int& getIdByConstRefConstNoexcept() const noexcept { + return this->id; + } + + // getter_by_const_ref_noexcept + const int& getIdByConstRefNoExcept() noexcept { + return this->id; + } + + // setter_by_value void setIdByVal(int id) { this->id = id; } - void setIdByConstRef(const int &id) { + // setter_by_ref + void setIdByRef(int& id) { this->id = id; } - void setIdByRef(int &id) { + // setter_by_const_ref + void setIdByConstRef(const int& id) { + this->id = id; + } + + // setter_by_value_noexcept + void setIdByValueNoexcept(int id) noexcept { + this->id = id; + } + + // setter_by_ref_noexcept + void setIdByRefNoExcept(int& id) noexcept { + this->id = id; + } + + // setter_by_const_ref_noexcept + void setIdByConstRefNoexcept(const int& id) noexcept { this->id = id; } }; static_assert(is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); static_assert(!is_field_member_pointer::value, ""); static_assert(!is_field_member_pointer::value, ""); - static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); static_assert(!is_field_member_pointer::value, ""); - static_assert(!is_field_member_pointer::value, ""); static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); static_assert(!is_getter::value, ""); + static_assert(is_getter::value, ""); static_assert(is_getter::value, ""); static_assert(is_getter::value, ""); - static_assert(is_getter::value, ""); + static_assert(is_getter::value, ""); + static_assert(is_getter::value, ""); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + static_assert(is_getter::value, ""); + static_assert(is_getter::value, ""); + static_assert(is_getter::value, ""); + static_assert(is_getter::value, ""); + static_assert(is_getter::value, ""); + static_assert(is_getter::value, ""); +#endif static_assert(!is_getter::value, ""); - static_assert(!is_getter::value, ""); static_assert(!is_getter::value, ""); + static_assert(!is_getter::value, ""); + static_assert(!is_getter::value, ""); + static_assert(!is_getter::value, ""); + static_assert(!is_getter::value, ""); static_assert(!is_setter::value, ""); + static_assert(!is_setter::value, ""); static_assert(!is_setter::value, ""); static_assert(!is_setter::value, ""); - static_assert(!is_setter::value, ""); + static_assert(!is_setter::value, ""); + static_assert(!is_setter::value, ""); + static_assert(!is_setter::value, ""); + static_assert(!is_setter::value, ""); + static_assert(!is_setter::value, ""); + static_assert(!is_setter::value, ""); + static_assert(!is_setter::value, ""); + static_assert(!is_setter::value, ""); static_assert(is_setter::value, ""); - static_assert(is_setter::value, ""); static_assert(is_setter::value, ""); + static_assert(is_setter::value, ""); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + static_assert(is_setter::value, ""); + static_assert(is_setter::value, ""); + static_assert(is_setter::value, ""); +#endif + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); static_assert(is_same::object_type, User>::value, ""); static_assert(is_same::field_type, int>::value, ""); @@ -71,39 +172,127 @@ TEST_CASE("member_traits_tests") { static_assert(is_same::object_type, User>::value, ""); static_assert(is_same::field_type, int>::value, ""); - static_assert(is_same::object_type, User>::value, ""); - static_assert(is_same::field_type, int>::value, ""); + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + static_assert(is_same::object_type, User>::value, + ""); + static_assert(is_same::field_type, int>::value, + ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + static_assert(is_same::object_type, User>::value, + ""); + static_assert(is_same::field_type, int>::value, + ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert( + is_same::object_type, User>::value, + ""); + static_assert( + is_same::field_type, int>::value, + ""); + + static_assert(is_same::object_type, User>::value, + ""); + static_assert(is_same::field_type, int>::value, + ""); +#endif static_assert(is_same::object_type, User>::value, ""); static_assert(is_same::field_type, int>::value, ""); + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + static_assert(is_same::object_type, User>::value, ""); static_assert(is_same::field_type, int>::value, ""); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); - static_assert(is_same::object_type, User>::value, ""); - static_assert(is_same::field_type, int>::value, ""); + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + static_assert(is_same::object_type, User>::value, + ""); + static_assert(is_same::field_type, int>::value, + ""); +#endif static_assert(is_same::object_type, User>::value, ""); static_assert(is_same::field_type, int>::value, ""); static_assert(is_same::object_type, User>::value, ""); static_assert(is_same::field_type, int>::value, ""); + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + static_assert(is_same::object_type, User>::value, ""); static_assert(is_same::field_type, int>::value, ""); static_assert(is_same::object_type, User>::value, ""); static_assert(is_same::field_type, int>::value, ""); - static_assert(is_same::object_type, User>::value, ""); - static_assert(is_same::field_type, int>::value, ""); + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + static_assert(is_same::object_type, User>::value, + ""); + static_assert(is_same::field_type, int>::value, + ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + static_assert(is_same::object_type, User>::value, + ""); + static_assert(is_same::field_type, int>::value, + ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert( + is_same::object_type, User>::value, + ""); + static_assert( + is_same::field_type, int>::value, + ""); + + static_assert(is_same::object_type, User>::value, + ""); + static_assert(is_same::field_type, int>::value, + ""); +#endif static_assert(is_same::object_type, User>::value, ""); static_assert(is_same::field_type, int>::value, ""); + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + static_assert(is_same::object_type, User>::value, ""); static_assert(is_same::field_type, int>::value, ""); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); - static_assert(is_same::object_type, User>::value, ""); - static_assert(is_same::field_type, int>::value, ""); + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, + ""); + static_assert(is_same::field_type, int>::value, + ""); +#endif } diff --git a/tests/static_tests/node_tuple.cpp b/tests/static_tests/node_tuple.cpp index ca76b65ba..5a3680d72 100644 --- a/tests/static_tests/node_tuple.cpp +++ b/tests/static_tests/node_tuple.cpp @@ -26,23 +26,23 @@ TEST_CASE("Node tuple") { std::string name; }; - // simple - { - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "int"); - static_assert(is_same::type, std::tuple>::value, ""); - } - { - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "float"); - static_assert(is_same::type, std::tuple>::value, ""); + SECTION("simple") { + SECTION("int") { + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "int"); + static_assert(is_same::type, std::tuple>::value, ""); + } + SECTION("float") { + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "float"); + static_assert(is_same::type, std::tuple>::value, ""); + } } - - { // binary_condition + SECTION("binary_condition") { using namespace internal; - { // 5 < 6.0f + SECTION("5 < 6.0f") { auto c = lesser_than(5, 6.0f); using C = decltype(c); using Tuple = node_tuple::type; @@ -50,7 +50,7 @@ TEST_CASE("Node tuple") { static_assert(is_same::value, "lesser_than_t"); static_assert(is_same::type, std::tuple>::value, ""); } - { // id < 10 + SECTION("id < 10") { auto c = lesser_than(&User::id, 10); using C = decltype(c); using Tuple = node_tuple::type; @@ -58,7 +58,7 @@ TEST_CASE("Node tuple") { static_assert(is_same::value, "lesser_than_t"); static_assert(is_same::type, std::tuple>::value, ""); } - { // 5 <= 6.0f + SECTION("5 <= 6.0f") { auto c = lesser_or_equal(5, 6.0f); using C = decltype(c); using Tuple = node_tuple::type; @@ -66,7 +66,7 @@ TEST_CASE("Node tuple") { static_assert(is_same::value, "lesser_or_equal_t"); static_assert(is_same::type, std::tuple>::value, ""); } - { // id <= 10.0 + SECTION("id <= 10.0") { auto c = lesser_or_equal(&User::id, 10.0); using C = decltype(c); using Tuple = node_tuple::type; @@ -74,7 +74,7 @@ TEST_CASE("Node tuple") { static_assert(is_same::value, "lesser_or_equal_t"); static_assert(is_same::type, std::tuple>::value, ""); } - { // 5 > 6.0f + SECTION("5 > 6.0f") { auto c = greater_than(5, 6.0f); using C = decltype(c); using Tuple = node_tuple::type; @@ -82,7 +82,7 @@ TEST_CASE("Node tuple") { static_assert(is_same::value, "greater_than_t"); static_assert(is_same::type, std::tuple>::value, ""); } - { // id > 20 + SECTION("id > 20") { auto c = greater_than(&User::id, 20); using C = decltype(c); using Tuple = node_tuple::type; @@ -90,7 +90,7 @@ TEST_CASE("Node tuple") { static_assert(is_same::value, "greater_than_t"); static_assert(is_same::type, std::tuple>::value, ""); } - { // 5 >= 6.0f + SECTION("5 >= 6.0f") { auto c = greater_or_equal(5, 6.0f); using C = decltype(c); using Tuple = node_tuple::type; @@ -98,7 +98,7 @@ TEST_CASE("Node tuple") { static_assert(is_same::value, "greater_or_equal_t"); static_assert(is_same::type, std::tuple>::value, ""); } - { // 5 >= id + SECTION("5 >= id") { auto c = greater_or_equal(5, &User::id); using C = decltype(c); using Tuple = node_tuple::type; @@ -106,7 +106,7 @@ TEST_CASE("Node tuple") { static_assert(is_same::value, "greater_or_equal_t"); static_assert(is_same::type, std::tuple>::value, ""); } - { // 5 == 6.0f + SECTION("5 == 6.0f") { auto c = is_equal(5, 6.0f); using C = decltype(c); using Tuple = node_tuple::type; @@ -114,15 +114,15 @@ TEST_CASE("Node tuple") { static_assert(is_same::value, "is_equal_t"); static_assert(is_same::type, std::tuple>::value, ""); } - { // "ototo" == name + SECTION("'ototo' == name") { auto c = is_equal("ototo", &User::name); using C = decltype(c); using Tuple = node_tuple::type; - using Expected = std::tuple; + using Expected = std::tuple; static_assert(is_same::value, "is_equal_t"); - static_assert(is_same::type, std::tuple>::value, ""); + static_assert(is_same::type, std::tuple>::value, ""); } - { // 5 != 6.0f + SECTION("5 != 6.0f") { auto c = is_not_equal(5, 6.0f); using C = decltype(c); using Tuple = node_tuple::type; @@ -130,7 +130,7 @@ TEST_CASE("Node tuple") { static_assert(is_same::value, "is_not_equal_t"); static_assert(is_same::type, std::tuple>::value, ""); } - { // name != std::string("ototo") + SECTION("name != std::string('ototo')") { auto c = is_not_equal(&User::name, std::string("ototo")); using C = decltype(c); using Tuple = node_tuple::type; @@ -138,20 +138,20 @@ TEST_CASE("Node tuple") { static_assert(is_same::value, "is_not_equal_t"); static_assert(is_same::type, std::tuple>::value, ""); } - { // bool and int + SECTION("bool and int") { using Tuple = node_tuple>::type; using Expected = std::tuple; static_assert(is_same::value, "and_condition_t"); static_assert(is_same::type, std::tuple>::value, ""); } - { // bool or int + SECTION("bool or int") { using Tuple = node_tuple>::type; using Expected = std::tuple; static_assert(is_same::value, "or_condition_t"); static_assert(is_same::type, std::tuple>::value, ""); } } - { // binary_operator + SECTION("binary_operator") { using namespace internal; using CondTuple = node_tuple>::type; @@ -175,494 +175,570 @@ TEST_CASE("Node tuple") { using AssignTuple = node_tuple>::type; static_assert(is_same>::value, "assign_t"); } - { // columns + SECTION("columns") { auto cols = columns(&User::id, &User::name); using Cols = decltype(cols); using ColsTuple = node_tuple::type; static_assert(is_same>::value, "columns_t"); } - { // in + SECTION("in") { auto inValue = in(&User::id, {1, 2, 3}); using In = decltype(inValue); using InTuple = node_tuple::type; static_assert(is_same>>::value, "in_t"); } - {// compound operator... - {// union_(select(1), select(2)) - auto un = union_(select(1), select(2)); - using Union = decltype(un); - using Tuple = node_tuple::type; - static_assert(is_same>::value, "union_(select(1), select(2))"); -} -{ // union_all - auto un = union_all(select(&User::id, where(is_equal(&User::name, "Alice"))), - select(&User::id, where(is_equal(std::string("Bob"), &User::name)))); - using Union = decltype(un); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "union_all"); -} -{ // except - auto un = except(select(columns(&User::id, &User::name), where(is_equal(&User::id, 10L))), - select(columns(&User::id, &User::name), where(is_equal(&User::id, 15L)))); - using Union = decltype(un); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "except"); -} -{ // intersect - auto un = intersect(select(&User::name), select(&User::name, where(is_equal(&User::name, "Anny")))); - using Union = decltype(un); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "intersect"); -} -} -{// select - {// select(&User::id) - auto sel = select(&User::id); -using Sel = decltype(sel); -using Tuple = node_tuple::type; -static_assert(is_same>::value, "select(&User::id)"); -} -{ // select(&User::name) - auto sel = select(&User::name); - using Sel = decltype(sel); - using Tuple = node_tuple::type; - static_assert(is_same>::value, "select(&User::name)"); -} -{ // select(&User::id, where(is_equal(&User::id, 5))) - auto sel = select(&User::id, where(is_equal(&User::id, 5))); - using Sel = decltype(sel); - using Tuple = node_tuple::type; - static_assert(is_same>::value, - "select(&User::id, where(is_equal(&User::id, 5)))"); -} -{ // select(&User::name, where(lesser_than(&User::id, 10))) - auto sel = select(&User::name, where(lesser_than(&User::id, 10))); - using Sel = decltype(sel); - using Tuple = node_tuple::type; - static_assert(is_same>::value, - "select(&User::name, where(lesser_than(&User::id, 10)))"); -} -{ // select(columns(&User::id, &User::name), where(greater_or_equal(&User::id, 10) and lesser_or_equal(&User::id, 20))) - auto sel = select(columns(&User::id, &User::name), - where(greater_or_equal(&User::id, 10) and lesser_or_equal(&User::id, 20))); - using Sel = decltype(sel); - using Tuple = node_tuple::type; - using Expected = - std::tuple; - static_assert(is_same::value, - "select(columns(&User::id, &User::name), where(greater_or_equal(&User::id, 10) and " - "lesser_or_equal(&User::id, 20)))"); -} -} -{// get_all_t - {// get_all() - auto getAll = get_all(); -using GetAll = decltype(getAll); -using Tuple = node_tuple::type; -static_assert(is_same>::value, "get_all()"); -} -{ // get_all(where(is_equal(5.0, &User::id))) - auto getAll = get_all(where(is_equal(5.0, &User::id))); - using GetAll = decltype(getAll); - using Tuple = node_tuple::type; - static_assert(is_same>::value, - "get_all(where(is_equal(5.0, &User::id)))"); -} -{ // get_all(where(is_equal(5.0, &User::id))) - auto getAll = get_all(where(is_equal(&User::id, 1) or is_equal(std::string("Alex"), &User::name))); - using GetAll = decltype(getAll); - using Tuple = node_tuple::type; - static_assert(is_same>::value, - "get_all(where(is_equal(5.0, &User::id)))"); -} -} -{ // having_t - using namespace internal; - auto hav = having(greater_or_equal(&User::id, 10)); - using Having = decltype(hav); - using Tuple = node_tuple::type; - static_assert(is_same>::value, - "having(greater_or_equal(&User::id, 10))"); -} -{ // cast_t - auto sel = select(columns(cast(&User::id), cast(&User::name))); - using Select = decltype(sel); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "named_collate"); -} -{// negated_condition_t - {// not is_equal(20, "20"); - auto c = not is_equal(20, "20"); -using Con = decltype(c); -using Tuple = node_tuple::type; -using Expected = std::tuple; -static_assert(is_same::value, "not is_equal(20, \"20\")"); -} -{ // not is_not_equal(&User::id, 15.0) - auto c = not is_not_equal(&User::id, 15.0); - using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "not is_not_equal(&User::id, 15.0)"); -} -{ // not greater_than(20.0f, &User::id) - auto c = not greater_than(20.0f, &User::id); - using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "not greater_than(20.0f, &User::id)"); -} -{ // not greater_or_equal(&User::id, 5) - auto c = not greater_or_equal(&User::id, 5); - using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "not greater_or_equal(&User::id, 5)"); -} -{ // not lesser_than(&User::id, std::string("6")) - auto c = not lesser_than(&User::id, std::string("6")); - using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "not lesser_than(&User::id, std::string(\"6\"))"); -} -{ // not lesser_or_equal(&User::id, 10) - auto c = not lesser_or_equal(&User::id, 10); - using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "not lesser_or_equal(&User::id, 10)"); -} -{ // not in(&User::id, {1, 2, 3}) - auto c = not in(&User::id, {1, 2, 3}); - using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple>; - static_assert(is_same::value, "not in(&User::id, {1, 2, 3})"); -} -{ // not is_null(&User::name) - auto c = not is_null(&User::name); - using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "not is_null(&User::name)"); -} -{ // not is_not_null(&User::name) - auto c = not is_not_null(&User::name); - using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "not is_not_null(&User::name)"); -} -{ // not like(&User::name, "*D*") - auto c = not like(&User::name, "*D*"); - using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "not like(&User::name, \"*D*\")"); -} -{ // not glob(&User::name, std::string("_A_")) - auto c = not glob(&User::name, std::string("_A_")); - using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "not glob(&User::name, std::string(\"_A_\"))"); -} -{ // not exists(select(&User::name, where(in(&User::id, {6, 7, 9})))) - auto c = not exists(select(&User::name, where(in(&User::id, {6, 7, 9})))); - using Con = decltype(c); - using Tuple = node_tuple::type; - using Expected = std::tuple>; - static_assert(is_same::value, "not exists(select(&User::name, where(in(&User::id, {6, 7, 9}))))"); -} -} -{ // core_function_t - { // lower - auto f = lower(&User::name); - using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "lower"); + SECTION("replace_raw_t") { + auto expression = + replace(into(), columns(&User::id, &User::name), values(std::make_tuple(1, std::string("Ellie")))); + using Expression = decltype(expression); + using Tuple = node_tuple::type; + static_assert(is_same>::value, + ""); } - { // upper - auto f = upper("hi"); - using Fun = decltype(f); - using Tuple = node_tuple::type; - using ArgType = std::tuple_element<0, Fun::args_type>::type; - static_assert(is_same::value, "upper arg[0]"); - using Expected = std::tuple; - static_assert(is_same::value, "upper"); + SECTION("insert_raw_t") { + auto expression = + insert(into(), columns(&User::id, &User::name), values(std::make_tuple(1, std::string("Ellie")))); + using Expression = decltype(expression); + using Tuple = node_tuple::type; + static_assert(is_same>::value, + ""); } - { // changes - auto f = changes(); - using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple<>; - static_assert(is_same::value, "changes"); + SECTION("tuple") { + auto expression = std::make_tuple(1, std::string("hi")); + using Expression = decltype(expression); + using Tuple = node_tuple::type; + static_assert(is_same>::value, ""); } - { // trim(1) - auto f = trim(&User::name); - using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "trim(1)"); + SECTION("values") { + SECTION("int + string") { + auto expression = values(1, std::string("hi")); + using Expression = decltype(expression); + using Tuple = node_tuple::type; + static_assert(is_same>::value, ""); + } + SECTION("tuple") { + auto expression = values(std::make_tuple(1, std::string("hi"))); + using Expression = decltype(expression); + using Tuple = node_tuple::type; + static_assert(is_same>::value, ""); + } } - { // trim(2) - auto f = trim(&User::name, std::string("pay")); - using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "trim(2)"); + SECTION("into") { + auto expression = into(); + using Expression = decltype(expression); + using Tuple = node_tuple::type; + static_assert(is_same>::value, ""); } - { // ltrim(1) - auto f = ltrim(&User::id); - using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "ltrim(1)"); + SECTION("select") { + SECTION("select(&User::id)") { + auto sel = select(&User::id); + using Sel = decltype(sel); + using Tuple = node_tuple::type; + static_assert(is_same>::value, "select(&User::id)"); + } + SECTION("select(&User::name)") { + auto sel = select(&User::name); + using Sel = decltype(sel); + using Tuple = node_tuple::type; + static_assert(is_same>::value, "select(&User::name)"); + } + SECTION("select(&User::id, where(is_equal(&User::id, 5)))") { + auto sel = select(&User::id, where(is_equal(&User::id, 5))); + using Sel = decltype(sel); + using Tuple = node_tuple::type; + static_assert(is_same>::value, + "select(&User::id, where(is_equal(&User::id, 5)))"); + } + SECTION("select(&User::name, where(lesser_than(&User::id, 10)))") { + auto sel = select(&User::name, where(lesser_than(&User::id, 10))); + using Sel = decltype(sel); + using Tuple = node_tuple::type; + static_assert(is_same>::value, + "select(&User::name, where(lesser_than(&User::id, 10)))"); + } + SECTION("select(columns(&User::id, &User::name), where(greater_or_equal(&User::id, 10) and " + "lesser_or_equal(&User::id, 20)))") { + auto sel = select(columns(&User::id, &User::name), + where(greater_or_equal(&User::id, 10) and lesser_or_equal(&User::id, 20))); + using Sel = decltype(sel); + using Tuple = node_tuple::type; + using Expected = std:: + tuple; + static_assert(is_same::value, + "select(columns(&User::id, &User::name), where(greater_or_equal(&User::id, 10) and " + "lesser_or_equal(&User::id, 20)))"); + } + SECTION("select(columns('ototo', 25))") { + auto statement = select(columns("ototo", 25)); + using Statement = decltype(statement); + using Tuple = node_tuple::type; + using ExpectedTuple = std::tuple; + static_assert(std::is_same::value, ""); + } } - { // ltrim(2) - auto f = ltrim(&User::id, "see"); - using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "ltrim(2)"); + SECTION("get_all_t") { + SECTION("get_all()") { + auto getAll = get_all(); + using GetAll = decltype(getAll); + using Tuple = node_tuple::type; + static_assert(is_same>::value, "get_all()"); + } + SECTION("get_all(where(is_equal(5.0, &User::id)))") { + auto getAll = get_all(where(is_equal(5.0, &User::id))); + using GetAll = decltype(getAll); + using Tuple = node_tuple::type; + static_assert(is_same>::value, + "get_all(where(is_equal(5.0, &User::id)))"); + } + SECTION("get_all(where(is_equal(5.0, &User::id)))") { + auto getAll = get_all(where(is_equal(&User::id, 1) or is_equal(std::string("Alex"), &User::name))); + using GetAll = decltype(getAll); + using Tuple = node_tuple::type; + static_assert( + is_same>::value, + "get_all(where(is_equal(5.0, &User::id)))"); + } } - { // rtrim(1) - auto f = rtrim(&User::name); - using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "rtrim(1)"); + SECTION("having_t") { + using namespace internal; + auto hav = having(greater_or_equal(&User::id, 10)); + using Having = decltype(hav); + using Tuple = node_tuple::type; + static_assert(is_same>::value, + "having(greater_or_equal(&User::id, 10))"); } - { // rtrim(2) - auto f = rtrim(&User::name, &User::id); - using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "rtrim(2)"); + SECTION("cast_t") { + auto sel = select(columns(cast(&User::id), cast(&User::name))); + using Select = decltype(sel); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "named_collate"); } - { // zeroblob - auto f = zeroblob(10); - using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "zeroblob"); + SECTION("negated_condition_t") { + SECTION("not is_equal(20, '20')") { + auto c = not is_equal(20, "20"); + using Con = decltype(c); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "not is_equal(20, \"20\")"); + } + SECTION("not is_not_equal(&User::id, 15.0)") { + auto c = not is_not_equal(&User::id, 15.0); + using Con = decltype(c); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "not is_not_equal(&User::id, 15.0)"); + } + SECTION("not greater_than(20.0f, &User::id)") { + auto c = not greater_than(20.0f, &User::id); + using Con = decltype(c); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "not greater_than(20.0f, &User::id)"); + } + SECTION("not greater_or_equal(&User::id, 5)") { + auto c = not greater_or_equal(&User::id, 5); + using Con = decltype(c); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "not greater_or_equal(&User::id, 5)"); + } + SECTION("not lesser_than(&User::id, std::string('6'))") { + auto c = not lesser_than(&User::id, std::string("6")); + using Con = decltype(c); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "not lesser_than(&User::id, std::string(\"6\"))"); + } + SECTION("not lesser_or_equal(&User::id, 10)") { + auto c = not lesser_or_equal(&User::id, 10); + using Con = decltype(c); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "not lesser_or_equal(&User::id, 10)"); + } + SECTION("not in(&User::id, {1, 2, 3})") { + auto c = not in(&User::id, {1, 2, 3}); + using Con = decltype(c); + using Tuple = node_tuple::type; + using Expected = std::tuple>; + static_assert(is_same::value, "not in(&User::id, {1, 2, 3})"); + } + SECTION("not is_null(&User::name)") { + auto c = not is_null(&User::name); + using Con = decltype(c); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "not is_null(&User::name)"); + } + SECTION("not is_not_null(&User::name)") { + auto c = not is_not_null(&User::name); + using Con = decltype(c); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "not is_not_null(&User::name)"); + } + SECTION("not like(&User::name, '*D*')") { + auto c = not like(&User::name, "*D*"); + using Con = decltype(c); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "not like(&User::name, \"*D*\")"); + } + SECTION("not glob(&User::name, std::string('_A_'))") { + auto c = not glob(&User::name, std::string("_A_")); + using Con = decltype(c); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "not glob(&User::name, std::string(\"_A_\"))"); + } + SECTION("not exists(select(&User::name, where(in(&User::id, {6, 7, 9}))))") { + auto c = not exists(select(&User::name, where(in(&User::id, {6, 7, 9})))); + using Con = decltype(c); + using Tuple = node_tuple::type; + using Expected = std::tuple>; + static_assert(is_same::value, + "not exists(select(&User::name, where(in(&User::id, {6, 7, 9}))))"); + } } - { // substr(2) - auto f = substr(&User::name, 7); - using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "substr"); + SECTION("core_function_t") { + SECTION("lower") { + auto f = lower(&User::name); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "lower"); + } + SECTION("upper") { + auto f = upper("hi"); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using ArgType = std::tuple_element<0, Fun::args_type>::type; + static_assert(is_same::value, "upper arg[0]"); + using Expected = std::tuple; + static_assert(is_same::value, "upper"); + } + SECTION("total_changes") { + auto f = total_changes(); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple<>; + static_assert(is_same::value, "total_changes"); + } + SECTION("changes") { + auto f = changes(); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple<>; + static_assert(is_same::value, "changes"); + } + SECTION("trim(1)") { + auto f = trim(&User::name); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "trim(1)"); + } + SECTION("trim(2)") { + auto f = trim(&User::name, std::string("pay")); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "trim(2)"); + } + SECTION("ltrim(1)") { + auto f = ltrim(&User::id); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "ltrim(1)"); + } + SECTION("ltrim(2)") { + auto f = ltrim(&User::id, "see"); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "ltrim(2)"); + } + SECTION("rtrim(1)") { + auto f = rtrim(&User::name); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "rtrim(1)"); + } + SECTION("rtrim(2)") { + auto f = rtrim(&User::name, &User::id); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "rtrim(2)"); + } +#if SQLITE_VERSION_NUMBER >= 3007016 + SECTION("char_") { + auto f = char_(100, 20.0); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "char_"); + } +#endif + SECTION("coalesce") { + auto f = char_(10, 20); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "coalesce"); + } + SECTION("date") { + auto f = + date(std::string("now"), std::string("start of month"), std::string("+1 month"), std::string("-1 day")); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "date"); + } + SECTION("datetime") { + auto f = datetime("now"); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "datetime"); + } + SECTION("julianday") { + auto f = julianday("now"); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "julianday"); + } + SECTION("zeroblob") { + auto f = zeroblob(10); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "zeroblob"); + } + SECTION("substr(2)") { + auto f = substr(&User::name, 7); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "substr"); + } + SECTION("substr(3)") { + auto f = substr(&User::name, 7, 20.f); + using Fun = decltype(f); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "substr"); + } } - { // substr(3) - auto f = substr(&User::name, 7, 20.f); - using Fun = decltype(f); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "substr"); + SECTION("join") { + SECTION("left_join") { + auto j = left_join(on(is_equal(&User::id, 2))); + using Join = decltype(j); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "left_join"); + } + SECTION("join") { + auto j = join(on(is_equal(&User::id, 2))); + using Join = decltype(j); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "join"); + } + SECTION("left_outer_join") { + auto j = left_outer_join(on(is_equal(&User::id, 2))); + using Join = decltype(j); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "left_outer_join"); + } + SECTION("inner_join") { + auto j = inner_join(on(is_equal(&User::id, 2))); + using Join = decltype(j); + using Tuple = node_tuple::type; + using Expected = std::tuple; + static_assert(is_same::value, "inner_join"); + } } -} -{// join - {// left_join - auto j = left_join(on(is_equal(&User::id, 2))); -using Join = decltype(j); -using Tuple = node_tuple::type; -using Expected = std::tuple; -static_assert(is_same::value, "left_join"); -} -{ // join - auto j = join(on(is_equal(&User::id, 2))); - using Join = decltype(j); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "join"); -} -{ // left_outer_join - auto j = left_outer_join(on(is_equal(&User::id, 2))); - using Join = decltype(j); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "left_outer_join"); -} -{ // inner_join - auto j = inner_join(on(is_equal(&User::id, 2))); - using Join = decltype(j); - using Tuple = node_tuple::type; - using Expected = std::tuple; - static_assert(is_same::value, "inner_join"); -} -} -{ // case - auto c = case_(&User::name).when("USA", then("Dosmetic")).else_("Foreign").end(); - using Case = decltype(c); - using CaseExpressionTuple = node_tuple::type; - static_assert(is_same>::value, ""); + SECTION("case") { + auto c = case_(&User::name).when("USA", then("Dosmetic")).else_("Foreign").end(); + using Case = decltype(c); + using CaseExpressionTuple = node_tuple::type; + static_assert(is_same>::value, ""); - static_assert(is_tuple>::value, ""); - static_assert(is_tuple>::value, ""); - static_assert(!is_tuple::value, ""); - static_assert(is_pair>::value, ""); - static_assert(!is_pair::value, ""); + static_assert(is_tuple>::value, ""); + static_assert(is_tuple>::value, ""); + static_assert(!is_tuple::value, ""); + static_assert(is_pair>::value, ""); + static_assert(!is_pair::value, ""); - using ArgsType = Case::args_type; - static_assert(is_tuple::value, ""); - static_assert(std::tuple_size::value == 1, ""); + using ArgsType = Case::args_type; + static_assert(is_tuple::value, ""); + static_assert(std::tuple_size::value == 1, ""); - using Arg0 = std::tuple_element<0, ArgsType>::type; - static_assert(is_pair::value, ""); - using Arg0First = Arg0::first_type; - static_assert(is_same::value, ""); - using Arg0Second = Arg0::second_type; - static_assert(is_same::value, ""); - static_assert(is_same>>::value, ""); + using Arg0 = std::tuple_element<0, ArgsType>::type; + static_assert(is_pair::value, ""); + using Arg0First = Arg0::first_type; + static_assert(is_same::value, ""); + using Arg0Second = Arg0::second_type; + static_assert(is_same::value, ""); + static_assert(is_same>>::value, ""); - using ElseExpressionTuple = node_tuple::type; - static_assert(is_same>::value, ""); -} -{ // as - struct GradeAlias : alias_tag { - static const std::string &get() { - static const std::string res = "Grade"; - return res; - } - }; - auto a = as(&User::name); - using A = decltype(a); - using Tuple = node_tuple::type; - static_assert(is_same>::value, ""); -} -{ - auto statement = select(columns("ototo", 25)); - using Statement = decltype(statement); - using Tuple = node_tuple::type; - static_assert(std::tuple_size::value == 2, ""); - { - using Arg0 = std::tuple_element<0, Tuple>::type; - static_assert(is_same::value, ""); + using ElseExpressionTuple = node_tuple::type; + static_assert(is_same>::value, ""); } - { - using Arg1 = std::tuple_element<1, Tuple>::type; - static_assert(is_same::value, ""); + SECTION("as") { + struct GradeAlias : alias_tag { + static const std::string& get() { + static const std::string res = "Grade"; + return res; + } + }; + auto a = as(&User::name); + using A = decltype(a); + using Tuple = node_tuple::type; + using ExpectedTuple = std::tuple; + static_assert(is_same::value, ""); + } + SECTION("function_call") { + struct Func { + bool operator()(int value) const { + return value % 2; + } + }; + auto statement = func(8); + using Statement = decltype(statement); + using Tuple = node_tuple::type; + using ExpectedTuple = std::tuple; + static_assert(std::is_same::value, ""); + } + SECTION("excluded") { + auto statement = excluded(&User::id); + using Statement = decltype(statement); + using Tuple = node_tuple::type; + using ExpectedTuple = std::tuple; + static_assert(std::is_same::value, ""); + } + SECTION("upsert_clause") { + auto statement = on_conflict(&User::id).do_update(set(c(&User::name) = excluded(&User::name))); + using Statement = decltype(statement); + using Tuple = node_tuple::type; + using ExpectedTuple = std::tuple; + static_assert(std::is_same::value, ""); } -} } diff --git a/tests/static_tests/same_or_void.cpp b/tests/static_tests/same_or_void.cpp new file mode 100644 index 000000000..8fe610c3b --- /dev/null +++ b/tests/static_tests/same_or_void.cpp @@ -0,0 +1,35 @@ +#include +#include + +#include // std::is_same +#include // std::string + +using namespace sqlite_orm; + +TEST_CASE("same_or_void") { + using internal::same_or_void; + + // one argument + static_assert(std::is_same::type, int>::value, "int"); + static_assert(std::is_same::type, std::string>::value, "std::string"); + static_assert(std::is_same::type, long>::value, "long"); + + // two arguments + static_assert(std::is_same::type, int>::value, "int, int"); + static_assert(std::is_same::type, void>::value, "int, long"); + static_assert(std::is_same::type, std::string>::value, + "std::string, std::string"); + static_assert(std::is_same::type, void>::value, "std::string, short"); + + // three arguments + static_assert(std::is_same::type, int>::value, "int, int, int"); + static_assert(std::is_same::type, long>::value, "long, long, long"); + static_assert(std::is_same::type, void>::value, "int, int, long"); + static_assert(std::is_same::type, void>::value, "long, int, int"); + static_assert(std::is_same::type, void>::value, "long, int, long"); + + // four arguments + static_assert(std::is_same::type, int>::value, "int, int, int, int"); + static_assert(std::is_same::type, long>::value, "long, long, long, long"); + static_assert(std::is_same::type, void>::value, "int, int, int, long"); +} diff --git a/tests/static_tests/static_tests_common.h b/tests/static_tests/static_tests_common.h index b27ae1e26..366440f31 100644 --- a/tests/static_tests/static_tests_common.h +++ b/tests/static_tests/static_tests_common.h @@ -3,11 +3,11 @@ struct User { int id; - const int &getIdByRefConst() const { + const int& getIdByRefConst() const { return this->id; } - const int &getIdByRef() { + const int& getIdByRef() { return this->id; } @@ -19,11 +19,11 @@ struct User { this->id = id_; } - void setIdByConstRef(const int &id_) { + void setIdByConstRef(const int& id_) { this->id = id_; } - void setIdByRef(int &id_) { + void setIdByRef(int& id_) { this->id = id_; } }; diff --git a/tests/static_tests/storage_traits.cpp b/tests/static_tests/storage_traits.cpp new file mode 100644 index 000000000..5ef834ce6 --- /dev/null +++ b/tests/static_tests/storage_traits.cpp @@ -0,0 +1,43 @@ +#include +#include +#include // std::is_same + +using namespace sqlite_orm; + +TEST_CASE("storage traits") { + SECTION("table_types") { + struct Table { + int64_t id; + std::string a; + std::string b; + std::string c; + }; + auto column1 = make_column("id", &Table::id); + auto column2 = make_column("a", &Table::a); + auto column3 = make_column("b", &Table::b); + auto column4 = make_column("c", &Table::c); + auto uniqueC = sqlite_orm::unique(&Table::a, &Table::b, &Table::c); + + using Column1 = decltype(column1); + using Column2 = decltype(column2); + using Column3 = decltype(column3); + using Column4 = decltype(column4); + using UniqueC = decltype(uniqueC); + + auto table = make_table("table", column1, column2, column3, column4, uniqueC); + using TableT = decltype(table); + using TableTypes = internal::storage_traits::table_types; + + using ArgsTuple = TableTypes::args_tuple; + using ExpectedArgsTuple = std::tuple; + static_assert(std::is_same::value, ""); + + using ColumnsTuple = TableTypes::columns_tuple; + using ExpectedColumnsTuple = std::tuple; + static_assert(std::is_same::value, ""); + + using ResultType = TableTypes::type; + using ExpectedResultType = std::tuple; + static_assert(std::is_same::value, ""); + } +} diff --git a/tests/static_tests/tuple_filter_single.cpp b/tests/static_tests/tuple_filter_single.cpp new file mode 100644 index 000000000..815ddf19f --- /dev/null +++ b/tests/static_tests/tuple_filter_single.cpp @@ -0,0 +1,79 @@ +#include +#include +#include // std::is_same + +using namespace sqlite_orm; + +TEST_CASE("tuple_filter") { + struct User { + int id = 0; + }; + SECTION("single") { + SECTION("is_bindable") { + SECTION("int") { + using Arg = int; + using Expected = std::tuple; + using ResultType = internal::tuple_filter_single::type; + static_assert(std::is_same::value, ""); + } + SECTION("std::string") { + using Arg = std::string; + using Expected = std::tuple; + using ResultType = internal::tuple_filter_single::type; + static_assert(std::is_same::value, ""); + } + SECTION("where_t") { + using Arg = internal::where_t; + using Expected = std::tuple<>; + using ResultType = internal::tuple_filter_single::type; + static_assert(std::is_same::value, ""); + } + SECTION("order_by_t") { + using Arg = internal::order_by_t; + using Expected = std::tuple<>; + using ResultType = internal::tuple_filter_single::type; + static_assert(std::is_same::value, ""); + } + } + SECTION("is_column") { + SECTION("column_t") { + auto column = make_column({}, &User::id); + using Arg = decltype(column); + using Expected = std::tuple; + using ResultType = internal::tuple_filter_single::type; + static_assert(std::is_same::value, ""); + } + SECTION("order_by_t") { + using Arg = internal::order_by_t; + using Expected = std::tuple<>; + using ResultType = internal::tuple_filter_single::type; + static_assert(std::is_same::value, ""); + } + SECTION("unique_t") { + using Arg = decltype(unique(&User::id)); + using Expected = std::tuple<>; + using ResultType = internal::tuple_filter_single::type; + static_assert(std::is_same::value, ""); + } + } + } + SECTION("multiple") { + SECTION("is_bindable") { + using Arg = + std::tuple, internal::order_by_t>; + using Expected = std::tuple; + using ResultType = internal::tuple_filter::type; + static_assert(std::is_same::value, ""); + } + SECTION("is_column") { + auto column = make_column({}, &User::id); + using Column = decltype(column); + using OrderBy = internal::order_by_t; + using Unique = decltype(unique(&User::id)); + using Arg = std::tuple; + using Expected = std::tuple; + using ResultType = internal::tuple_filter::type; + static_assert(std::is_same::value, ""); + } + } +} diff --git a/tests/static_tests/tuple_helper.cpp b/tests/static_tests/tuple_helper.cpp new file mode 100644 index 000000000..f72aa735c --- /dev/null +++ b/tests/static_tests/tuple_helper.cpp @@ -0,0 +1,29 @@ +#include +#include +#include // std::is_same + +using namespace sqlite_orm; + +TEST_CASE("tuple_helper static") { + SECTION("tuple_transformer") { + struct Table { + int64_t id; + std::string a; + std::string b; + std::string c; + }; + auto column1 = make_column("id", &Table::id); + auto column2 = make_column("a", &Table::a); + auto column3 = make_column("b", &Table::b); + auto column4 = make_column("c", &Table::c); + + using Column1 = decltype(column1); + using Column2 = decltype(column2); + using Column3 = decltype(column3); + using Column4 = decltype(column4); + using ColumnsTuple = std::tuple; + using ColumnsMappedTypes = internal::tuple_transformer::type; + using Expected = std::tuple; + static_assert(std::is_same::value, ""); + } +} diff --git a/tests/storage_non_crud_tests.cpp b/tests/storage_non_crud_tests.cpp new file mode 100644 index 000000000..9760df827 --- /dev/null +++ b/tests/storage_non_crud_tests.cpp @@ -0,0 +1,385 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("explicit from") { + struct User { + int id = 0; + std::string name; + }; + auto storage = make_storage( + {}, + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name))); + storage.sync_schema(); + + storage.replace(User{1, "Bebe Rexha"}); + + std::vector rows; + decltype(rows) expected; + SECTION("without conditions") { + SECTION("without alias") { + rows = storage.select(&User::id, from()); + } + SECTION("with alias") { + using als = alias_u; + rows = storage.select(alias_column(&User::id), from()); + } + expected.push_back(1); + } + SECTION("with real conditions") { + SECTION("without alias") { + rows = storage.select(&User::id, from(), where(is_equal(&User::name, "Bebe Rexha"))); + } + SECTION("with alias") { + using als = alias_u; + rows = storage.select(alias_column(&User::id), + from(), + where(is_equal(alias_column(&User::name), "Bebe Rexha"))); + } + expected.push_back(1); + } + SECTION("with unreal conditions") { + SECTION("without alias") { + rows = storage.select(&User::id, from(), where(is_equal(&User::name, "Zara Larsson"))); + } + SECTION("with alias") { + using als = alias_u; + rows = storage.select(alias_column(&User::id), + from(), + where(is_equal(alias_column(&User::name), "Zara Larsson"))); + } + } + REQUIRE(expected == rows); +} + +TEST_CASE("update set null") { + + struct User { + int id = 0; + std::unique_ptr name; + }; + + auto storage = make_storage( + {}, + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name))); + storage.sync_schema(); + + storage.replace(User{1, std::make_unique("Ototo")}); + REQUIRE(storage.count() == 1); + { + auto rows = storage.get_all(); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front().name); + } + + storage.update_all(set(assign(&User::name, nullptr))); + { + auto rows = storage.get_all(); + REQUIRE(rows.size() == 1); + REQUIRE(!rows.front().name); + } + + storage.update_all(set(assign(&User::name, "ototo"))); + { + auto rows = storage.get_all(); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front().name); + REQUIRE(*rows.front().name == "ototo"); + } + + storage.update_all(set(assign(&User::name, nullptr)), where(is_equal(&User::id, 1))); + { + auto rows = storage.get_all(); + REQUIRE(rows.size() == 1); + REQUIRE(!rows.front().name); + } +} + +TEST_CASE("InsertRange") { + struct Object { + int id; + std::string name; + }; + + struct ObjectWithoutRowid { + int id; + std::string name; + }; + + auto storage = make_storage( + "test_insert_range.sqlite", + make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name)), + make_table("objects_without_rowid", + make_column("id", &ObjectWithoutRowid::id, primary_key()), + make_column("name", &ObjectWithoutRowid::name)) + .without_rowid()); + + storage.sync_schema(); + storage.remove_all(); + storage.remove_all(); + + SECTION("straight") { + std::vector objects = {100, + Object{ + 0, + "Skillet", + }}; + storage.insert_range(objects.begin(), objects.end()); + REQUIRE(storage.count() == 100); + + // test empty container + std::vector emptyVector; + storage.insert_range(emptyVector.begin(), emptyVector.end()); + + // test insert_range without rowid + std::vector objectsWR = {ObjectWithoutRowid{10, "Life"}, ObjectWithoutRowid{20, "Death"}}; + REQUIRE(objectsWR.size() == 2); + storage.insert_range(objectsWR.begin(), objectsWR.end()); + REQUIRE(storage.get(10).name == "Life"); + REQUIRE(storage.get(20).name == "Death"); + } + SECTION("pointers") { + std::vector> objects; + objects.reserve(100); + for(auto i = 0; i < 100; ++i) { + objects.push_back(std::make_unique(Object{0, "Skillet"})); + } + storage.insert_range(objects.begin(), + objects.end(), + [](const std::unique_ptr &pointer) -> const Object & { + return *pointer; + }); + REQUIRE(storage.count() == 100); + + // test empty container + std::vector> emptyVector; + storage.insert_range(emptyVector.begin(), + emptyVector.end(), + [](const std::unique_ptr &pointer) -> const Object & { + return *pointer; + }); + + // test insert_range without rowid + std::vector> objectsWR; + objectsWR.push_back(std::make_unique(ObjectWithoutRowid{10, "Life"})); + objectsWR.push_back(std::make_unique(ObjectWithoutRowid{20, "Death"})); + + REQUIRE(objectsWR.size() == 2); + storage.insert_range( + objectsWR.begin(), + objectsWR.end(), + [](const std::unique_ptr &pointer) -> const ObjectWithoutRowid & { + return *pointer; + }); + REQUIRE(storage.get(10).name == "Life"); + REQUIRE(storage.get(20).name == "Death"); + } +} + +TEST_CASE("Select") { + sqlite3 *db; + auto dbFileName = "test.db"; + auto rc = sqlite3_open(dbFileName, &db); + assert(rc == SQLITE_OK); + auto sql = "CREATE TABLE IF NOT EXISTS WORDS(" + "ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + "CURRENT_WORD TEXT NOT NULL," + "BEFORE_WORD TEXT NOT NULL," + "AFTER_WORD TEXT NOT NULL," + "OCCURANCES INT NOT NULL);"; + + char *errMsg = nullptr; + rc = sqlite3_exec(db, sql, nullptr, nullptr, &errMsg); + REQUIRE(rc == SQLITE_OK); + + sqlite3_stmt *stmt; + + // delete previous words. This command is excess in travis or other docker based CI tools + // but it is required on local machine + sql = "DELETE FROM WORDS"; + rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); + REQUIRE(rc == SQLITE_OK); + + rc = sqlite3_step(stmt); + if(rc != SQLITE_DONE) { + throw std::runtime_error(sqlite3_errmsg(db)); + } + sqlite3_finalize(stmt); + + sql = "INSERT INTO WORDS (CURRENT_WORD, BEFORE_WORD, AFTER_WORD, OCCURANCES) VALUES(?, ?, ?, ?)"; + rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); + REQUIRE(rc == SQLITE_OK); + + // INSERT [ ID, 'best', 'behaviour', 'hey', 5 ] + + sqlite3_bind_text(stmt, 1, "best", -1, nullptr); + sqlite3_bind_text(stmt, 2, "behaviour", -1, nullptr); + sqlite3_bind_text(stmt, 3, "hey", -1, nullptr); + sqlite3_bind_int(stmt, 4, 5); + rc = sqlite3_step(stmt); + if(rc != SQLITE_DONE) { + throw std::runtime_error(sqlite3_errmsg(db)); + } + sqlite3_finalize(stmt); + + auto firstId = sqlite3_last_insert_rowid(db); + + // INSERT [ ID, 'corruption', 'blood', 'brothers', 15 ] + + rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); + REQUIRE(rc == SQLITE_OK); + sqlite3_bind_text(stmt, 1, "corruption", -1, nullptr); + sqlite3_bind_text(stmt, 2, "blood", -1, nullptr); + sqlite3_bind_text(stmt, 3, "brothers", -1, nullptr); + sqlite3_bind_int(stmt, 4, 15); + rc = sqlite3_step(stmt); + if(rc != SQLITE_DONE) { + throw std::runtime_error(sqlite3_errmsg(db)); + } + sqlite3_finalize(stmt); + + auto secondId = sqlite3_last_insert_rowid(db); + + { + // SELECT ID, CURRENT_WORD, BEFORE_WORD, AFTER_WORD, OCCURANCES + // FROM WORDS + // WHERE ID = firstId + + sql = "SELECT ID, CURRENT_WORD, BEFORE_WORD, AFTER_WORD, OCCURANCES FROM WORDS WHERE ID = ?"; + rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); + REQUIRE(rc == SQLITE_OK); + + sqlite3_bind_int64(stmt, 1, firstId); + rc = sqlite3_step(stmt); + if(rc != SQLITE_ROW) { + throw std::runtime_error(sqlite3_errmsg(db)); + } + REQUIRE(sqlite3_column_int(stmt, 0) == firstId); + REQUIRE(::strcmp((const char *)sqlite3_column_text(stmt, 1), "best") == 0); + REQUIRE(::strcmp((const char *)sqlite3_column_text(stmt, 2), "behaviour") == 0); + REQUIRE(::strcmp((const char *)sqlite3_column_text(stmt, 3), "hey") == 0); + REQUIRE(sqlite3_column_int(stmt, 4) == 5); + sqlite3_finalize(stmt); + } + + sqlite3_close(db); + + struct Word { + int id; + std::string currentWord; + std::string beforeWord; + std::string afterWord; + int occurances; + }; + + auto storage = make_storage(dbFileName, + make_table("WORDS", + make_column("ID", &Word::id, primary_key(), autoincrement()), + make_column("CURRENT_WORD", &Word::currentWord), + make_column("BEFORE_WORD", &Word::beforeWord), + make_column("AFTER_WORD", &Word::afterWord), + make_column("OCCURANCES", &Word::occurances))); + + storage.sync_schema(); // sync schema must not alter any data cause schemas are the same + + REQUIRE(storage.count() == 2); + + auto firstRow = storage.get_no_throw(firstId); + REQUIRE(firstRow); + REQUIRE(firstRow->currentWord == "best"); + REQUIRE(firstRow->beforeWord == "behaviour"); + REQUIRE(firstRow->afterWord == "hey"); + REQUIRE(firstRow->occurances == 5); + + auto secondRow = storage.get_pointer(secondId); + REQUIRE(secondRow); + REQUIRE(secondRow->currentWord == "corruption"); + REQUIRE(secondRow->beforeWord == "blood"); + REQUIRE(secondRow->afterWord == "brothers"); + REQUIRE(secondRow->occurances == 15); + + auto cols = columns(&Word::id, &Word::currentWord, &Word::beforeWord, &Word::afterWord, &Word::occurances); + auto rawTuples = storage.select(cols, where(eq(&Word::id, firstId))); + REQUIRE(rawTuples.size() == 1); + + { + auto &firstTuple = rawTuples.front(); + REQUIRE(std::get<0>(firstTuple) == firstId); + REQUIRE(std::get<1>(firstTuple) == "best"); + REQUIRE(std::get<2>(firstTuple) == "behaviour"); + REQUIRE(std::get<3>(firstTuple) == "hey"); + REQUIRE(std::get<4>(firstTuple) == 5); + } + + rawTuples = storage.select(cols, where(eq(&Word::id, secondId))); + REQUIRE(rawTuples.size() == 1); + + { + auto &secondTuple = rawTuples.front(); + REQUIRE(std::get<0>(secondTuple) == secondId); + REQUIRE(std::get<1>(secondTuple) == "corruption"); + REQUIRE(std::get<2>(secondTuple) == "blood"); + REQUIRE(std::get<3>(secondTuple) == "brothers"); + REQUIRE(std::get<4>(secondTuple) == 15); + } + + auto ordr = order_by(&Word::id); + + auto idsOnly = storage.select(&Word::id, ordr); + REQUIRE(idsOnly.size() == 2); + + REQUIRE(idsOnly[0] == firstId); + REQUIRE(idsOnly[1] == secondId); + + auto currentWordsOnly = storage.select(&Word::currentWord, ordr); + REQUIRE(currentWordsOnly.size() == 2); + + REQUIRE(currentWordsOnly[0] == "best"); + REQUIRE(currentWordsOnly[1] == "corruption"); + + auto beforeWordsOnly = storage.select(&Word::beforeWord, ordr); + REQUIRE(beforeWordsOnly.size() == 2); + + REQUIRE(beforeWordsOnly[0] == "behaviour"); + REQUIRE(beforeWordsOnly[1] == "blood"); + + auto afterWordsOnly = storage.select(&Word::afterWord, ordr); + REQUIRE(afterWordsOnly.size() == 2); + + REQUIRE(afterWordsOnly[0] == "hey"); + REQUIRE(afterWordsOnly[1] == "brothers"); + + auto occurencesOnly = storage.select(&Word::occurances, ordr); + REQUIRE(occurencesOnly.size() == 2); + + REQUIRE(occurencesOnly[0] == 5); + REQUIRE(occurencesOnly[1] == 15); + + // test update_all with the same storage + + storage.update_all(set(assign(&Word::currentWord, "ototo")), where(is_equal(&Word::id, firstId))); + + REQUIRE(storage.get(firstId).currentWord == "ototo"); +} + +TEST_CASE("Remove all") { + struct Object { + int id; + std::string name; + }; + + auto storage = make_storage( + "", + make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name))); + storage.sync_schema(); + + storage.replace(Object{1, "Ototo"}); + storage.replace(Object{2, "Contigo"}); + + REQUIRE(storage.count() == 2); + + storage.remove_all(where(c(&Object::id) == 1)); + + REQUIRE(storage.count() == 1); +} diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index 3f9e1376c..6a15b736a 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -122,3 +122,82 @@ TEST_CASE("rename table") { REQUIRE(storage.tablename() != visitsNewTableName); } } + +TEST_CASE("Storage copy") { + struct User { + int id = 0; + }; + + int calledCount = 0; + + auto storage = make_storage({}, make_table("users", make_column("id", &User::id))); + storage.sync_schema(); + storage.remove_all(); + + storage.on_open = [&calledCount](sqlite3*) { + ++calledCount; + }; + + storage.on_open(nullptr); + REQUIRE(calledCount == 1); + + auto storageCopy = storage; + REQUIRE(storageCopy.on_open); + REQUIRE(calledCount == 2); + storageCopy.on_open(nullptr); + REQUIRE(calledCount == 3); + + storageCopy.sync_schema(); + storageCopy.remove_all(); +} + +TEST_CASE("has_dependent_rows") { + struct User { + int id = 0; + std::string name; + }; + struct Visit { + int id = 0; + int userId = 0; + int date = 0; + }; + auto storage = + make_storage({}, + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)), + make_table("visits", + make_column("id", &Visit::id, primary_key()), + make_column("user_id", &Visit::userId), + make_column("date", &Visit::date), + foreign_key(&Visit::userId).references(&User::id))); + storage.sync_schema(); + + // storage.has_dependent_rows(5); +} + +TEST_CASE("column_name") { + struct User { + int id = 0; + std::string name; + }; + struct Visit { + int id = 0; + int userId = 0; + int date = 0; + + int notUsed = 0; + }; + auto storage = + make_storage({}, + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)), + make_table("visits", + make_column("id", &Visit::id, primary_key()), + make_column("user_id", &Visit::userId), + make_column("date", &Visit::date), + foreign_key(&Visit::userId).references(&User::id))); + REQUIRE(*storage.column_name(&User::id) == "id"); + REQUIRE(*storage.column_name(&User::name) == "name"); + REQUIRE(*storage.column_name(&Visit::id) == "id"); + REQUIRE(*storage.column_name(&Visit::userId) == "user_id"); + REQUIRE(*storage.column_name(&Visit::date) == "date"); + REQUIRE(storage.column_name(&Visit::notUsed) == nullptr); +} diff --git a/tests/sync_schema_tests.cpp b/tests/sync_schema_tests.cpp index 1169ef7c0..9d9389a24 100644 --- a/tests/sync_schema_tests.cpp +++ b/tests/sync_schema_tests.cpp @@ -56,7 +56,7 @@ TEST_CASE("Sync schema") { usersToInsert.push_back({-1, "Brad", std::make_unique(65), nullptr}); usersToInsert.push_back({-1, "Paul", std::make_unique(65), nullptr}); - for(auto &user: usersToInsert) { + for(auto& user: usersToInsert) { auto insertedId = storage.insert(user); user.id = insertedId; } @@ -84,8 +84,8 @@ TEST_CASE("Sync schema") { REQUIRE(usersFromDb.size() == usersToInsert.size()); for(size_t i = 0; i < usersFromDb.size(); ++i) { - auto &userFromDb = usersFromDb[i]; - auto &oldUser = usersToInsert[i]; + auto& userFromDb = usersFromDb[i]; + auto& oldUser = usersToInsert[i]; REQUIRE(userFromDb.id == oldUser.id); REQUIRE(userFromDb.name == oldUser.name); } @@ -104,7 +104,7 @@ TEST_CASE("Sync schema") { auto users = newStorage.get_all(); decltype(ids) idsFromGetAll; idsFromGetAll.reserve(users.size()); - std::transform(users.begin(), users.end(), std::back_inserter(idsFromGetAll), [=](auto &user) { + std::transform(users.begin(), users.end(), std::back_inserter(idsFromGetAll), [=](auto& user) { return user.id; }); REQUIRE(std::equal(ids.begin(), ids.end(), idsFromGetAll.begin(), idsFromGetAll.end())); @@ -141,7 +141,7 @@ TEST_CASE("issue521") { pocosToInsert.push_back({-1, "Michael", 10, 10.10}); pocosToInsert.push_back({-1, "Joyce", 20, 20.20}); - for(auto &poco: pocosToInsert) { + for(auto& poco: pocosToInsert) { auto insertedId = storage.insert(poco); poco.id = insertedId; } @@ -152,8 +152,8 @@ TEST_CASE("issue521") { using namespace sqlite_orm; auto pocosFromDb = storage.get_all(order_by(&MockDatabasePoco::id)); for(size_t i = 0; i < pocosFromDb.size(); ++i) { - auto &pocoFromDb = pocosFromDb[i]; - auto &oldPoco = pocosToInsert[i]; + auto& pocoFromDb = pocosFromDb[i]; + auto& oldPoco = pocosToInsert[i]; REQUIRE(pocoFromDb.id == oldPoco.id); REQUIRE(pocoFromDb.name == oldPoco.name); @@ -176,8 +176,8 @@ TEST_CASE("issue521") { auto pocosFromDb = storage.get_all(order_by(&MockDatabasePoco::id)); for(size_t i = 0; i < pocosFromDb.size(); ++i) { - auto &pocoFromDb = pocosFromDb[i]; - auto &oldPoco = pocosToInsert[i]; + auto& pocoFromDb = pocosFromDb[i]; + auto& oldPoco = pocosToInsert[i]; REQUIRE(pocoFromDb.id == oldPoco.id); REQUIRE(pocoFromDb.name == oldPoco.name); } @@ -201,8 +201,8 @@ TEST_CASE("issue521") { auto pocosFromDb = storage.get_all(order_by(&MockDatabasePoco::id)); for(size_t i = 0; i < pocosFromDb.size(); ++i) { - auto &pocoFromDb = pocosFromDb[i]; - auto &oldPoco = pocosToInsert[i]; + auto& pocoFromDb = pocosFromDb[i]; + auto& oldPoco = pocosToInsert[i]; REQUIRE(pocoFromDb.id == oldPoco.id); REQUIRE(pocoFromDb.name == oldPoco.name); REQUIRE(pocoFromDb.alpha == 1); @@ -228,8 +228,8 @@ TEST_CASE("issue521") { auto pocosFromDb = storage.get_all(order_by(&MockDatabasePoco::id)); for(size_t i = 0; i < pocosFromDb.size(); ++i) { - auto &pocoFromDb = pocosFromDb[i]; - auto &oldPoco = pocosToInsert[i]; + auto& pocoFromDb = pocosFromDb[i]; + auto& oldPoco = pocosToInsert[i]; REQUIRE(pocoFromDb.id == oldPoco.id); REQUIRE(pocoFromDb.name == oldPoco.name); @@ -238,7 +238,7 @@ TEST_CASE("issue521") { } } -bool compareUniquePointers(const std::unique_ptr &lhs, const std::unique_ptr &rhs) { +bool compareUniquePointers(const std::unique_ptr& lhs, const std::unique_ptr& rhs) { if(!lhs && !rhs) { return true; } else { @@ -266,11 +266,11 @@ TEST_CASE("sync_schema") { User(int id_, int age_) : id(id_), age(age_) {} - User(const User &other) : + User(const User& other) : id(other.id), name(other.name), age(other.age), ageNullable(other.ageNullable ? std::make_unique(*other.ageNullable) : nullptr) {} - bool operator==(const User &other) const { + bool operator==(const User& other) const { return this->id == other.id && this->name == other.name && this->age == other.age; } }; @@ -413,3 +413,15 @@ TEST_CASE("sync_schema") { } } } + +TEST_CASE("sync_schema_simulate") { + struct Cols { + int Col1; + }; + + auto storage = + make_storage("db", make_index("IX_Col1", &Cols::Col1), make_table("Table", make_column("Col1", &Cols::Col1))); + + storage.sync_schema(); + storage.sync_schema_simulate(); +} diff --git a/tests/table_tests.cpp b/tests/table_tests.cpp index b96bb796a..fb22c7710 100644 --- a/tests/table_tests.cpp +++ b/tests/table_tests.cpp @@ -29,12 +29,12 @@ TEST_CASE("table") { make_column("country_code", &Contact::countryCode), make_column("phone_number", &Contact::phoneNumber), make_column("visits_count", &Contact::visitsCount)); - REQUIRE(table.find_column_name(&Contact::id) == "contact_id"); - REQUIRE(table.find_column_name(&Contact::firstName) == "first_name"); - REQUIRE(table.find_column_name(&Contact::lastName) == "last_name"); - REQUIRE(table.find_column_name(&Contact::countryCode) == "country_code"); - REQUIRE(table.find_column_name(&Contact::phoneNumber) == "phone_number"); - REQUIRE(table.find_column_name(&Contact::visitsCount) == "visits_count"); + REQUIRE(*table.find_column_name(&Contact::id) == "contact_id"); + REQUIRE(*table.find_column_name(&Contact::firstName) == "first_name"); + REQUIRE(*table.find_column_name(&Contact::lastName) == "last_name"); + REQUIRE(*table.find_column_name(&Contact::countryCode) == "country_code"); + REQUIRE(*table.find_column_name(&Contact::phoneNumber) == "phone_number"); + REQUIRE(*table.find_column_name(&Contact::visitsCount) == "visits_count"); } { struct Contact { @@ -55,7 +55,7 @@ TEST_CASE("table") { this->_id = value; } - const std::string &firstName() const { + const std::string& firstName() const { return this->_firstName; } @@ -63,7 +63,7 @@ TEST_CASE("table") { this->_firstName = move(value); } - const std::string &lastName() const { + const std::string& lastName() const { return this->_lastName; } @@ -79,7 +79,7 @@ TEST_CASE("table") { this->_countryCode = value; } - const std::string &phoneNumber() const { + const std::string& phoneNumber() const { return this->_phoneNumber; } @@ -104,23 +104,23 @@ TEST_CASE("table") { make_column("phone_number", &Contact::phoneNumber, &Contact::setPhoneNumber), make_column("visits_count", &Contact::visitsCount, &Contact::setVisitsCount)); - REQUIRE(table.find_column_name(&Contact::id) == "contact_id"); - REQUIRE(table.find_column_name(&Contact::setId) == "contact_id"); + REQUIRE(*table.find_column_name(&Contact::id) == "contact_id"); + REQUIRE(*table.find_column_name(&Contact::setId) == "contact_id"); - REQUIRE(table.find_column_name(&Contact::firstName) == "first_name"); - REQUIRE(table.find_column_name(&Contact::setFirstName) == "first_name"); + REQUIRE(*table.find_column_name(&Contact::firstName) == "first_name"); + REQUIRE(*table.find_column_name(&Contact::setFirstName) == "first_name"); - REQUIRE(table.find_column_name(&Contact::lastName) == "last_name"); - REQUIRE(table.find_column_name(&Contact::setLastName) == "last_name"); + REQUIRE(*table.find_column_name(&Contact::lastName) == "last_name"); + REQUIRE(*table.find_column_name(&Contact::setLastName) == "last_name"); - REQUIRE(table.find_column_name(&Contact::countryCode) == "country_code"); - REQUIRE(table.find_column_name(&Contact::setCountryCode) == "country_code"); + REQUIRE(*table.find_column_name(&Contact::countryCode) == "country_code"); + REQUIRE(*table.find_column_name(&Contact::setCountryCode) == "country_code"); - REQUIRE(table.find_column_name(&Contact::phoneNumber) == "phone_number"); - REQUIRE(table.find_column_name(&Contact::setPhoneNumber) == "phone_number"); + REQUIRE(*table.find_column_name(&Contact::phoneNumber) == "phone_number"); + REQUIRE(*table.find_column_name(&Contact::setPhoneNumber) == "phone_number"); - REQUIRE(table.find_column_name(&Contact::visitsCount) == "visits_count"); - REQUIRE(table.find_column_name(&Contact::setVisitsCount) == "visits_count"); + REQUIRE(*table.find_column_name(&Contact::visitsCount) == "visits_count"); + REQUIRE(*table.find_column_name(&Contact::setVisitsCount) == "visits_count"); } } diff --git a/tests/tests.cpp b/tests/tests.cpp index adbe80e96..a1a0c2be7 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -117,7 +117,7 @@ TEST_CASE("Explicit insert") { class Visit { public: - const int &id() const { + const int& id() const { return _id; } @@ -125,7 +125,7 @@ TEST_CASE("Explicit insert") { _id = newValue; } - const time_t &createdAt() const { + const time_t& createdAt() const { return _createdAt; } @@ -133,7 +133,7 @@ TEST_CASE("Explicit insert") { _createdAt = newValue; } - const int &usedId() const { + const int& usedId() const { return _usedId; } @@ -202,7 +202,7 @@ TEST_CASE("Explicit insert") { storage.insert(user4, columns(&User::name)); REQUIRE(false); // throw std::runtime_error("Must not fire"); - } catch(const std::system_error &) { + } catch(const std::system_error&) { // cout << e.what() << endl; } } @@ -256,14 +256,14 @@ TEST_CASE("Explicit insert") { try { storage.insert(visit3, columns(&Visit::id)); REQUIRE(false); - } catch(const std::system_error &) { + } catch(const std::system_error&) { // cout << e.what() << endl; } try { storage.insert(visit3, columns(&Visit::setId)); REQUIRE(false); - } catch(const std::system_error &) { + } catch(const std::system_error&) { // cout << e.what() << endl; } } @@ -277,75 +277,122 @@ TEST_CASE("Custom collate") { std::string name; }; + struct OtotoCollation { + int operator()(int leftLength, const void* lhs, int rightLength, const void* rhs) const { + if(leftLength == rightLength) { + return ::strncmp((const char*)lhs, (const char*)rhs, leftLength); + } else { + return 1; + } + } + + static const char* name() { + return "ototo"; + } + }; + + struct AlwaysEqualCollation { + int operator()(int leftLength, const void* lhs, int rightLength, const void* rhs) const { + return 0; + } + + static const char* name() { + return "alwaysequal"; + } + }; + + auto useLegacyScript = false; + SECTION("legacy API") { + useLegacyScript = true; + } + SECTION("modern API") { + useLegacyScript = false; + } + + auto filename = "custom_collate.sqlite"; + ::remove(filename); auto storage = make_storage( - "custom_collate.sqlite", + filename, make_table("items", make_column("id", &Item::id, primary_key()), make_column("name", &Item::name))); - // storage.open_forever(); + storage.open_forever(); storage.sync_schema(); storage.remove_all(); storage.insert(Item{0, "Mercury"}); storage.insert(Item{0, "Mars"}); - storage.create_collation("ototo", [](int, const void *lhs, int, const void *rhs) { - return strcmp((const char *)lhs, (const char *)rhs); - }); - storage.create_collation("alwaysequal", [](int, const void *, int, const void *) { - return 0; - }); + if(useLegacyScript) { + storage.create_collation("ototo", [](int leftLength, const void* lhs, int rightLength, const void* rhs) { + if(leftLength == rightLength) { + return ::strncmp((const char*)lhs, (const char*)rhs, leftLength); + } else { + return 1; + } + }); + storage.create_collation("alwaysequal", [](int, const void*, int, const void*) { + return 0; + }); + } else { + storage.create_collation(); + storage.create_collation(); + } auto rows = storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("ototo"))); REQUIRE(rows.size() == 1); REQUIRE(rows.front() == "Mercury"); + rows = storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate())); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "Mercury"); + rows = storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("alwaysequal")), order_by(&Item::name).collate("ototo")); - storage.create_collation("ototo", {}); + rows = storage.select(&Item::name, + where(is_equal(&Item::name, "Mercury").collate()), + order_by(&Item::name).collate()); + + if(useLegacyScript) { + storage.create_collation("ototo", {}); + } else { + storage.delete_collation(); + } try { rows = storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("ototo"))); REQUIRE(false); - } catch(const std::system_error &e) { + } catch(const std::system_error&) { + // cout << e.what() << endl; + } + try { + rows = storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate())); + REQUIRE(false); + } catch(const std::system_error&) { // cout << e.what() << endl; } try { rows = storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("ototo2"))); REQUIRE(false); - } catch(const std::system_error &e) { + } catch(const std::system_error&) { // cout << e.what() << endl; } + rows = storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("alwaysequal")), order_by(&Item::name).collate_rtrim()); + REQUIRE(rows.size() == static_cast(storage.count())); + + rows = storage.select(&Item::name, + where(is_equal(&Item::name, "Mercury").collate()), + order_by(&Item::name).collate_rtrim()); + REQUIRE(rows.size() == static_cast(storage.count())); rows = storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("alwaysequal")), order_by(&Item::name).collate("alwaysequal")); REQUIRE(rows.size() == static_cast(storage.count())); -} - -TEST_CASE("collate") { - struct User { - int id = 0; - std::string firstName; - - bool operator==(const User &user) const { - return this->id == user.id && this->firstName == user.firstName; - } - }; - auto storage = make_storage( - {}, - make_table("users", make_column("id", &User::id, primary_key()), make_column("first_name", &User::firstName))); - storage.sync_schema(); - User user1{1, "HELLO"}; - User user2{2, "Hello"}; - User user3{3, "HEllo"}; - storage.replace(user1); - storage.replace(user2); - storage.replace(user3); - - auto rows = storage.get_all(where(is_equal(&User::firstName, "hello").collate_nocase())); - std::vector expected = {user1, user2, user3}; - REQUIRE(rows == expected); + rows = storage.select(&Item::name, + where(is_equal(&Item::name, "Mercury").collate()), + order_by(&Item::name).collate()); + REQUIRE(rows.size() == static_cast(storage.count())); } TEST_CASE("Vacuum") { @@ -366,71 +413,3 @@ TEST_CASE("Vacuum") { storage.remove_all(); storage.vacuum(); } - -TEST_CASE("Remove all") { - struct Object { - int id; - std::string name; - }; - - auto storage = make_storage( - "", - make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name))); - storage.sync_schema(); - - storage.replace(Object{1, "Ototo"}); - storage.replace(Object{2, "Contigo"}); - - REQUIRE(storage.count() == 2); - - storage.remove_all(where(c(&Object::id) == 1)); - - REQUIRE(storage.count() == 1); -} - -TEST_CASE("Escaped index name") { - struct User { - std::string group; - }; - auto storage = make_storage("index_group.sqlite", - make_index("index", &User::group), - make_table("users", make_column("group", &User::group))); - storage.sync_schema(); -} - -TEST_CASE("Where") { - struct User { - int id = 0; - int age = 0; - std::string name; - }; - - auto storage = make_storage("", - make_table("users", - make_column("id", &User::id, primary_key()), - make_column("age", &User::age), - make_column("name", &User::name))); - storage.sync_schema(); - - storage.replace(User{1, 4, "Jeremy"}); - storage.replace(User{2, 18, "Nataly"}); - - auto users = storage.get_all(); - REQUIRE(users.size() == 2); - - auto users2 = storage.get_all(where(true)); - REQUIRE(users2.size() == 2); - - auto users3 = storage.get_all(where(false)); - REQUIRE(users3.size() == 0); - - auto users4 = storage.get_all(where(true and c(&User::id) == 1)); - REQUIRE(users4.size() == 1); - REQUIRE(users4.front().id == 1); - - auto users5 = storage.get_all(where(false and c(&User::id) == 1)); - REQUIRE(users5.size() == 0); - - auto users6 = storage.get_all(where((false or c(&User::id) == 4) and (false or c(&User::age) == 18))); - REQUIRE(users6.empty()); -} diff --git a/tests/tests2.cpp b/tests/tests2.cpp index 6483895ff..24413bac6 100644 --- a/tests/tests2.cpp +++ b/tests/tests2.cpp @@ -64,196 +64,6 @@ TEST_CASE("Remove") { } } -TEST_CASE("Select") { - sqlite3 *db; - auto dbFileName = "test.db"; - auto rc = sqlite3_open(dbFileName, &db); - assert(rc == SQLITE_OK); - auto sql = "CREATE TABLE IF NOT EXISTS WORDS(" - "ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," - "CURRENT_WORD TEXT NOT NULL," - "BEFORE_WORD TEXT NOT NULL," - "AFTER_WORD TEXT NOT NULL," - "OCCURANCES INT NOT NULL);"; - - char *errMsg = nullptr; - rc = sqlite3_exec(db, sql, nullptr, nullptr, &errMsg); - REQUIRE(rc == SQLITE_OK); - - sqlite3_stmt *stmt; - - // delete previous words. This command is excess in travis or other docker based CI tools - // but it is required on local machine - sql = "DELETE FROM WORDS"; - rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); - REQUIRE(rc == SQLITE_OK); - - rc = sqlite3_step(stmt); - if(rc != SQLITE_DONE) { - // cout << sqlite3_errmsg(db) << endl; - throw std::runtime_error(sqlite3_errmsg(db)); - } - sqlite3_finalize(stmt); - - sql = "INSERT INTO WORDS (CURRENT_WORD, BEFORE_WORD, AFTER_WORD, OCCURANCES) VALUES(?, ?, ?, ?)"; - rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); - REQUIRE(rc == SQLITE_OK); - - // INSERT [ ID, 'best', 'behaviour', 'hey', 5 ] - - sqlite3_bind_text(stmt, 1, "best", -1, nullptr); - sqlite3_bind_text(stmt, 2, "behaviour", -1, nullptr); - sqlite3_bind_text(stmt, 3, "hey", -1, nullptr); - sqlite3_bind_int(stmt, 4, 5); - rc = sqlite3_step(stmt); - if(rc != SQLITE_DONE) { - // cout << sqlite3_errmsg(db) << endl; - throw std::runtime_error(sqlite3_errmsg(db)); - } - sqlite3_finalize(stmt); - - auto firstId = sqlite3_last_insert_rowid(db); - - // INSERT [ ID, 'corruption', 'blood', 'brothers', 15 ] - - rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); - REQUIRE(rc == SQLITE_OK); - sqlite3_bind_text(stmt, 1, "corruption", -1, nullptr); - sqlite3_bind_text(stmt, 2, "blood", -1, nullptr); - sqlite3_bind_text(stmt, 3, "brothers", -1, nullptr); - sqlite3_bind_int(stmt, 4, 15); - rc = sqlite3_step(stmt); - if(rc != SQLITE_DONE) { - // cout << sqlite3_errmsg(db) << endl; - throw std::runtime_error(sqlite3_errmsg(db)); - } - sqlite3_finalize(stmt); - - auto secondId = sqlite3_last_insert_rowid(db); - - { - // SELECT ID, CURRENT_WORD, BEFORE_WORD, AFTER_WORD, OCCURANCES - // FROM WORDS - // WHERE ID = firstId - - sql = "SELECT ID, CURRENT_WORD, BEFORE_WORD, AFTER_WORD, OCCURANCES FROM WORDS WHERE ID = ?"; - rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr); - REQUIRE(rc == SQLITE_OK); - - sqlite3_bind_int64(stmt, 1, firstId); - rc = sqlite3_step(stmt); - if(rc != SQLITE_ROW) { - // cout << sqlite3_errmsg(db) << endl; - throw std::runtime_error(sqlite3_errmsg(db)); - } - REQUIRE(sqlite3_column_int(stmt, 0) == firstId); - REQUIRE(::strcmp((const char *)sqlite3_column_text(stmt, 1), "best") == 0); - REQUIRE(::strcmp((const char *)sqlite3_column_text(stmt, 2), "behaviour") == 0); - REQUIRE(::strcmp((const char *)sqlite3_column_text(stmt, 3), "hey") == 0); - REQUIRE(sqlite3_column_int(stmt, 4) == 5); - sqlite3_finalize(stmt); - } - - sqlite3_close(db); - - struct Word { - int id; - std::string currentWord; - std::string beforeWord; - std::string afterWord; - int occurances; - }; - - auto storage = make_storage(dbFileName, - make_table("WORDS", - make_column("ID", &Word::id, primary_key(), autoincrement()), - make_column("CURRENT_WORD", &Word::currentWord), - make_column("BEFORE_WORD", &Word::beforeWord), - make_column("AFTER_WORD", &Word::afterWord), - make_column("OCCURANCES", &Word::occurances))); - - storage.sync_schema(); // sync schema must not alter any data cause schemas are the same - - REQUIRE(storage.count() == 2); - - auto firstRow = storage.get_no_throw(firstId); - REQUIRE(firstRow); - REQUIRE(firstRow->currentWord == "best"); - REQUIRE(firstRow->beforeWord == "behaviour"); - REQUIRE(firstRow->afterWord == "hey"); - REQUIRE(firstRow->occurances == 5); - - auto secondRow = storage.get_pointer(secondId); - REQUIRE(secondRow); - REQUIRE(secondRow->currentWord == "corruption"); - REQUIRE(secondRow->beforeWord == "blood"); - REQUIRE(secondRow->afterWord == "brothers"); - REQUIRE(secondRow->occurances == 15); - - auto cols = columns(&Word::id, &Word::currentWord, &Word::beforeWord, &Word::afterWord, &Word::occurances); - auto rawTuples = storage.select(cols, where(eq(&Word::id, firstId))); - REQUIRE(rawTuples.size() == 1); - - { - auto &firstTuple = rawTuples.front(); - REQUIRE(std::get<0>(firstTuple) == firstId); - REQUIRE(std::get<1>(firstTuple) == "best"); - REQUIRE(std::get<2>(firstTuple) == "behaviour"); - REQUIRE(std::get<3>(firstTuple) == "hey"); - REQUIRE(std::get<4>(firstTuple) == 5); - } - - rawTuples = storage.select(cols, where(eq(&Word::id, secondId))); - REQUIRE(rawTuples.size() == 1); - - { - auto &secondTuple = rawTuples.front(); - REQUIRE(std::get<0>(secondTuple) == secondId); - REQUIRE(std::get<1>(secondTuple) == "corruption"); - REQUIRE(std::get<2>(secondTuple) == "blood"); - REQUIRE(std::get<3>(secondTuple) == "brothers"); - REQUIRE(std::get<4>(secondTuple) == 15); - } - - auto ordr = order_by(&Word::id); - - auto idsOnly = storage.select(&Word::id, ordr); - REQUIRE(idsOnly.size() == 2); - - REQUIRE(idsOnly[0] == firstId); - REQUIRE(idsOnly[1] == secondId); - - auto currentWordsOnly = storage.select(&Word::currentWord, ordr); - REQUIRE(currentWordsOnly.size() == 2); - - REQUIRE(currentWordsOnly[0] == "best"); - REQUIRE(currentWordsOnly[1] == "corruption"); - - auto beforeWordsOnly = storage.select(&Word::beforeWord, ordr); - REQUIRE(beforeWordsOnly.size() == 2); - - REQUIRE(beforeWordsOnly[0] == "behaviour"); - REQUIRE(beforeWordsOnly[1] == "blood"); - - auto afterWordsOnly = storage.select(&Word::afterWord, ordr); - REQUIRE(afterWordsOnly.size() == 2); - - REQUIRE(afterWordsOnly[0] == "hey"); - REQUIRE(afterWordsOnly[1] == "brothers"); - - auto occurencesOnly = storage.select(&Word::occurances, ordr); - REQUIRE(occurencesOnly.size() == 2); - - REQUIRE(occurencesOnly[0] == 5); - REQUIRE(occurencesOnly[1] == 15); - - // test update_all with the same storage - - storage.update_all(set(assign(&Word::currentWord, "ototo")), where(is_equal(&Word::id, firstId))); - - REQUIRE(storage.get(firstId).currentWord == "ototo"); -} - TEST_CASE("Replace query") { struct Object { int id; @@ -322,23 +132,43 @@ TEST_CASE("Replace query") { REQUIRE(ototo.id == 100); REQUIRE(ototo.name == "Ototo"); - auto initList = { - Object{ - 300, - "Iggy", - }, - Object{ - 400, - "Azalea", - }, - }; - storage.replace_range(initList.begin(), initList.end()); - REQUIRE(storage.count() == 4); - - // test empty container - std::vector emptyVector; - storage.replace_range(emptyVector.begin(), emptyVector.end()); - + SECTION("straight") { + auto initList = { + Object{ + 300, + "Iggy", + }, + Object{ + 400, + "Azalea", + }, + }; + storage.replace_range(initList.begin(), initList.end()); + REQUIRE(storage.count() == 4); + + // test empty container + std::vector emptyVector; + storage.replace_range(emptyVector.begin(), emptyVector.end()); + } + SECTION("pointers") { + std::vector> vector; + vector.push_back(std::make_unique(Object{300, "Iggy"})); + vector.push_back(std::make_unique(Object{400, "Azalea"})); + storage.replace_range(vector.begin(), + vector.end(), + [](const std::unique_ptr &pointer) -> const Object & { + return *pointer; + }); + REQUIRE(storage.count() == 4); + + // test empty container + std::vector> emptyVector; + storage.replace_range(emptyVector.begin(), + emptyVector.end(), + [](const std::unique_ptr &pointer) -> const Object & { + return *pointer; + }); + } REQUIRE(storage.count() == 0); storage.replace(User{10, "Daddy Yankee"}); } @@ -390,12 +220,34 @@ TEST_CASE("Insert") { }; auto countBefore = storage.count(); - storage.insert_range(initList.begin(), initList.end()); - REQUIRE(storage.count() == countBefore + static_cast(initList.size())); + SECTION("straight") { + storage.insert_range(initList.begin(), initList.end()); + REQUIRE(storage.count() == countBefore + static_cast(initList.size())); - // test empty container - std::vector emptyVector; - storage.insert_range(emptyVector.begin(), emptyVector.end()); + // test empty container + std::vector emptyVector; + storage.insert_range(emptyVector.begin(), emptyVector.end()); + } + SECTION("pointers") { + std::vector> pointers; + pointers.reserve(initList.size()); + std::transform(initList.begin(), initList.end(), std::back_inserter(pointers), [](const Object &object) { + return std::make_unique(Object{object}); + }); + storage.insert_range(pointers.begin(), + pointers.end(), + [](const std::unique_ptr &pointer) -> const Object & { + return *pointer; + }); + + // test empty container + std::vector> emptyVector; + storage.insert_range(emptyVector.begin(), + emptyVector.end(), + [](const std::unique_ptr &pointer) -> const Object & { + return *pointer; + }); + } // test insert without rowid storage.insert(ObjectWithoutRowid{10, "Life"}); @@ -403,3 +255,307 @@ TEST_CASE("Insert") { storage.insert(ObjectWithoutRowid{20, "Death"}); REQUIRE(storage.get(20).name == "Death"); } + +struct SqrtFunction { + static int callsCount; + + double operator()(double arg) const { + ++callsCount; + return std::sqrt(arg); + } + + static const char *name() { + return "SQRT_CUSTOM"; + } +}; + +int SqrtFunction::callsCount = 0; + +struct HasPrefixFunction { + static int callsCount; + static int objectsCount; + + HasPrefixFunction() { + ++objectsCount; + } + + HasPrefixFunction(const HasPrefixFunction &) { + ++objectsCount; + } + + HasPrefixFunction(HasPrefixFunction &&) { + ++objectsCount; + } + + ~HasPrefixFunction() { + --objectsCount; + } + + bool operator()(const std::string &str, const std::string &prefix) { + ++callsCount; + return str.compare(0, prefix.size(), prefix) == 0; + } + + static std::string name() { + return "HAS_PREFIX"; + } +}; + +int HasPrefixFunction::callsCount = 0; +int HasPrefixFunction::objectsCount = 0; + +struct MeanFunction { + double total = 0; + int count = 0; + + static int objectsCount; + + MeanFunction() { + ++objectsCount; + } + + MeanFunction(const MeanFunction &) { + ++objectsCount; + } + + MeanFunction(MeanFunction &&) { + ++objectsCount; + } + + ~MeanFunction() { + --objectsCount; + } + + void step(double value) { + total += value; + ++count; + } + + double fin() const { + return total / count; + } + + static std::string name() { + return "MEAN"; + } +}; + +int MeanFunction::objectsCount = 0; + +struct FirstFunction { + static int objectsCount; + static int callsCount; + + FirstFunction() { + ++objectsCount; + } + + FirstFunction(const MeanFunction &) { + ++objectsCount; + } + + FirstFunction(MeanFunction &&) { + ++objectsCount; + } + + ~FirstFunction() { + --objectsCount; + } + + std::string operator()(const arg_values &args) const { + ++callsCount; + std::string res; + res.reserve(args.size()); + for(auto value: args) { + auto stringValue = value.get(); + if(!stringValue.empty()) { + res += stringValue.front(); + } + } + return res; + } + + static const char *name() { + return "FIRST"; + } +}; + +struct MultiSum { + double sum = 0; + + static int objectsCount; + + MultiSum() { + ++objectsCount; + } + + MultiSum(const MeanFunction &) { + ++objectsCount; + } + + MultiSum(MeanFunction &&) { + ++objectsCount; + } + + ~MultiSum() { + --objectsCount; + } + + void step(const arg_values &args) { + for(auto it = args.begin(); it != args.end(); ++it) { + if(!it->empty() && (it->is_integer() || it->is_float())) { + this->sum += it->get(); + } + } + } + + double fin() const { + return this->sum; + } + + static const char *name() { + return "MULTI_SUM"; + } +}; + +int MultiSum::objectsCount = 0; + +int FirstFunction::objectsCount = 0; +int FirstFunction::callsCount = 0; + +TEST_CASE("custom functions") { + SqrtFunction::callsCount = 0; + HasPrefixFunction::callsCount = 0; + FirstFunction::callsCount = 0; + + std::string path; + SECTION("in memory") { + path = {}; + } + SECTION("file") { + path = "custom_function.sqlite"; + ::remove(path.c_str()); + } + struct User { + int id = 0; + }; + auto storage = make_storage(path, make_table("users", make_column("id", &User::id))); + storage.sync_schema(); + { // call before creation + try { + auto rows = storage.select(func(4)); + REQUIRE(false); + } catch(const std::system_error &) { + //.. + } + } + + // create function + REQUIRE(SqrtFunction::callsCount == 0); + + storage.create_scalar_function(); + + REQUIRE(SqrtFunction::callsCount == 0); + + // call after creation + { + auto rows = storage.select(func(4)); + REQUIRE(SqrtFunction::callsCount == 1); + decltype(rows) expected; + expected.push_back(2); + REQUIRE(rows == expected); + } + + // create function + REQUIRE(HasPrefixFunction::callsCount == 0); + REQUIRE(HasPrefixFunction::objectsCount == 0); + storage.create_scalar_function(); + REQUIRE(HasPrefixFunction::callsCount == 0); + REQUIRE(HasPrefixFunction::objectsCount == 0); + + // call after creation + { + auto rows = storage.select(func("one", "o")); + decltype(rows) expected; + expected.push_back(true); + REQUIRE(rows == expected); + } + REQUIRE(HasPrefixFunction::callsCount == 1); + REQUIRE(HasPrefixFunction::objectsCount == 0); + { + auto rows = storage.select(func("two", "b")); + decltype(rows) expected; + expected.push_back(false); + REQUIRE(rows == expected); + } + REQUIRE(HasPrefixFunction::callsCount == 2); + REQUIRE(HasPrefixFunction::objectsCount == 0); + + // delete function + storage.delete_scalar_function(); + + // delete function + storage.delete_scalar_function(); + + storage.create_aggregate_function(); + + storage.replace(User{1}); + storage.replace(User{2}); + storage.replace(User{3}); + REQUIRE(storage.count() == 3); + { + REQUIRE(MeanFunction::objectsCount == 0); + auto rows = storage.select(func(&User::id)); + REQUIRE(MeanFunction::objectsCount == 0); + decltype(rows) expected; + expected.push_back(2); + REQUIRE(rows == expected); + } + storage.delete_aggregate_function(); + + storage.create_scalar_function(); + { + auto rows = storage.select(func("Vanotek", "Tinashe", "Pitbull")); + decltype(rows) expected; + expected.push_back("VTP"); + REQUIRE(rows == expected); + REQUIRE(FirstFunction::objectsCount == 0); + REQUIRE(FirstFunction::callsCount == 1); + } + { + auto rows = storage.select(func("Charli XCX", "Rita Ora")); + decltype(rows) expected; + expected.push_back("CR"); + REQUIRE(rows == expected); + REQUIRE(FirstFunction::objectsCount == 0); + REQUIRE(FirstFunction::callsCount == 2); + } + { + auto rows = storage.select(func("Ted")); + decltype(rows) expected; + expected.push_back("T"); + REQUIRE(rows == expected); + REQUIRE(FirstFunction::objectsCount == 0); + REQUIRE(FirstFunction::callsCount == 3); + } + { + auto rows = storage.select(func()); + decltype(rows) expected; + expected.push_back(""); + REQUIRE(rows == expected); + REQUIRE(FirstFunction::objectsCount == 0); + REQUIRE(FirstFunction::callsCount == 4); + } + storage.delete_scalar_function(); + + storage.create_aggregate_function(); + { + REQUIRE(MultiSum::objectsCount == 0); + auto rows = storage.select(func(&User::id, 5)); + decltype(rows) expected; + expected.push_back(21); + REQUIRE(rows == expected); + REQUIRE(MultiSum::objectsCount == 0); + } + storage.delete_aggregate_function(); +} diff --git a/tests/tests3.cpp b/tests/tests3.cpp index 2a2418aae..36b55d7c2 100644 --- a/tests/tests3.cpp +++ b/tests/tests3.cpp @@ -61,41 +61,6 @@ TEST_CASE("Issue 105") { storage.insert(d); } -TEST_CASE("Row id") { - struct SimpleTable { - std::string letter; - std::string desc; - }; - - auto storage = make_storage( - "rowid.sqlite", - make_table("tbl1", make_column("letter", &SimpleTable::letter), make_column("desc", &SimpleTable::desc))); - storage.sync_schema(); - storage.remove_all(); - - storage.insert(SimpleTable{"A", "first letter"}); - storage.insert(SimpleTable{"B", "second letter"}); - storage.insert(SimpleTable{"C", "third letter"}); - - auto rows = storage.select(columns(rowid(), - oid(), - _rowid_(), - rowid(), - oid(), - _rowid_(), - &SimpleTable::letter, - &SimpleTable::desc)); - for(size_t i = 0; i < rows.size(); ++i) { - auto &row = rows[i]; - REQUIRE(std::get<0>(row) == std::get<1>(row)); - REQUIRE(std::get<1>(row) == std::get<2>(row)); - REQUIRE(std::get<2>(row) == static_cast(i + 1)); - REQUIRE(std::get<2>(row) == std::get<3>(row)); - REQUIRE(std::get<3>(row) == std::get<4>(row)); - REQUIRE(std::get<4>(row) == std::get<5>(row)); - } -} - TEST_CASE("Issue 87") { struct Data { uint8_t mDefault = 0; /**< 0=User or 1=Default*/ @@ -150,7 +115,7 @@ TEST_CASE("Wide string") { L"АаБбВвГгДдЕеЁёЖжЗзИиЙйКкЛлМмНнОоППРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭэЮюЯя", // russian L"AaBbCcÇçDdEeFFGgĞğHhIıİiJjKkLlMmNnOoÖöPpRrSsŞşTtUuÜüVvYyZz", // turkish }; - for(auto &expectedString: expectedStrings) { + for(auto& expectedString: expectedStrings) { auto id = storage.insert(Alphabet{0, expectedString}); REQUIRE(storage.get(id).letters == expectedString); } @@ -193,7 +158,7 @@ TEST_CASE("Aggregate functions") { this->id = newValue; } - const int &getId() const { + const int& getId() const { return this->id; } @@ -201,7 +166,7 @@ TEST_CASE("Aggregate functions") { this->name = newValue; } - const std::string &getName() const { + const std::string& getName() const { return this->name; } @@ -209,7 +174,7 @@ TEST_CASE("Aggregate functions") { this->age = newValue; } - const int &getAge() const { + const int& getAge() const { return this->age; } }; @@ -348,8 +313,8 @@ TEST_CASE("Blob") { }; using byte = char; - auto generateData = [](size_t size) -> byte * { - auto data = (byte *)::malloc(size * sizeof(byte)); + auto generateData = [](size_t size) -> byte* { + auto data = (byte*)::malloc(size * sizeof(byte)); for(int i = 0; i < static_cast(size); ++i) { if((i + 1) % 10 == 0) { data[i] = 0; @@ -377,7 +342,7 @@ TEST_CASE("Blob") { { auto vd = storage.get_all(); assert(vd.size() == 1); - auto &blob = vd.front(); + auto& blob = vd.front(); REQUIRE(blob.data.size() == size); REQUIRE(std::equal(data, data + size, blob.data.begin())); } @@ -386,7 +351,7 @@ TEST_CASE("Blob") { { auto blobData = storage.select(&BlobData::data); assert(blobData.size() == 1); - auto &blob = blobData.front(); + auto& blob = blobData.front(); REQUIRE(blob.size() == size); REQUIRE(std::equal(data, data + size, blob.begin())); } @@ -395,7 +360,7 @@ TEST_CASE("Blob") { { auto blobData = storage.select(columns(&BlobData::data)); REQUIRE(blobData.size() == 1); - auto &blob = std::get<0>(blobData.front()); + auto& blob = std::get<0>(blobData.front()); REQUIRE(blob.size() == size); REQUIRE(std::equal(data, data + size, blob.begin())); } diff --git a/tests/tests4.cpp b/tests/tests4.cpp index 885b8c7ba..115580aed 100644 --- a/tests/tests4.cpp +++ b/tests/tests4.cpp @@ -7,103 +7,6 @@ using namespace sqlite_orm; -TEST_CASE("Case") { - - struct User { - int id = 0; - std::string firstName; - std::string lastName; - std::string country; - }; - - struct Track { - int id = 0; - std::string name; - long milliseconds = 0; - }; - - auto storage = make_storage({}, - make_table("users", - make_column("id", &User::id, autoincrement(), primary_key()), - make_column("first_name", &User::firstName), - make_column("last_name", &User::lastName), - make_column("country", &User::country)), - make_table("tracks", - make_column("trackid", &Track::id, autoincrement(), primary_key()), - make_column("name", &Track::name), - make_column("milliseconds", &Track::milliseconds))); - storage.sync_schema(); - - struct GradeAlias : alias_tag { - static const std::string &get() { - static const std::string res = "Grade"; - return res; - } - }; - - { - storage.insert(User{0, "Roberto", "Almeida", "Mexico"}); - storage.insert(User{0, "Julia", "Bernett", "USA"}); - storage.insert(User{0, "Camille", "Bernard", "Argentina"}); - storage.insert(User{0, "Michelle", "Brooks", "USA"}); - storage.insert(User{0, "Robet", "Brown", "USA"}); - - auto rows = storage.select( - columns(case_(&User::country).when("USA", then("Dosmetic")).else_("Foreign").end()), - multi_order_by(order_by(&User::lastName), order_by(&User::firstName))); - auto verifyRows = [&storage](auto &rows) { - REQUIRE(rows.size() == storage.count()); - REQUIRE(std::get<0>(rows[0]) == "Foreign"); - REQUIRE(std::get<0>(rows[1]) == "Foreign"); - REQUIRE(std::get<0>(rows[2]) == "Dosmetic"); - REQUIRE(std::get<0>(rows[3]) == "Dosmetic"); - REQUIRE(std::get<0>(rows[4]) == "Dosmetic"); - }; - verifyRows(rows); - - rows = storage.select( - columns(as( - case_(&User::country).when("USA", then("Dosmetic")).else_("Foreign").end())), - multi_order_by(order_by(&User::lastName), order_by(&User::firstName))); - - verifyRows(rows); - } - { - storage.insert(Track{0, "For Those About To Rock", 400000}); - storage.insert(Track{0, "Balls to the Wall", 500000}); - storage.insert(Track{0, "Fast as a Shark", 200000}); - storage.insert(Track{0, "Restless and Wild", 100000}); - storage.insert(Track{0, "Princess of the Dawn", 50000}); - - auto rows = storage.select( - case_() - .when(c(&Track::milliseconds) < 60000, then("short")) - .when(c(&Track::milliseconds) >= 60000 and c(&Track::milliseconds) < 300000, then("medium")) - .else_("long") - .end(), - order_by(&Track::name)); - auto verifyRows = [&storage](auto &rows) { - REQUIRE(rows.size() == storage.count()); - REQUIRE(rows[0] == "long"); - REQUIRE(rows[1] == "medium"); - REQUIRE(rows[2] == "long"); - REQUIRE(rows[3] == "short"); - REQUIRE(rows[4] == "medium"); - }; - verifyRows(rows); - - rows = storage.select( - as( - case_() - .when(c(&Track::milliseconds) < 60000, then("short")) - .when(c(&Track::milliseconds) >= 60000 and c(&Track::milliseconds) < 300000, then("medium")) - .else_("long") - .end()), - order_by(&Track::name)); - verifyRows(rows); - } -} - TEST_CASE("Unique ptr in update") { struct User { @@ -132,7 +35,7 @@ TEST_CASE("Unique ptr in update") { } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -TEST_CASE("Optional in update") { +TEST_CASE("optional in update") { struct User { int id = 0; @@ -166,7 +69,7 @@ TEST_CASE("Optional in update") { } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED -TEST_CASE("Join") { +TEST_CASE("join") { struct User { int id = 0; @@ -231,73 +134,147 @@ TEST_CASE("Join") { } } -TEST_CASE("Storage copy") { - struct User { - int id = 0; +TEST_CASE("two joins") { + struct Statement { + int id_statement; + long date; }; - int calledCount = 0; - - auto storage = make_storage({}, make_table("users", make_column("id", &User::id))); - storage.sync_schema(); - storage.remove_all(); + struct Concepto { + int id_concepto; + std::string name; // TFT-SINPE A: 15103-02**-****-8467 + int fkey_account; // { 15103-02**-****-8467, ...} + }; - storage.on_open = [&calledCount](sqlite3 *) { - ++calledCount; + struct Account { + int id_account; + std::string number; // 15103-02**-****-8467 + int fkey_bank; // { BAC San Jose, "Barrio Dent", { Costa Rica} } + int fkey_account_owner; // { Juan Dent Herrera, ... } + std::string description; // AMEX Cashback Premium + bool is_tarjeta; // true }; - storage.on_open(nullptr); - REQUIRE(calledCount == 1); + struct Pais { + int id_pais; + std::string name; + }; - auto storageCopy = storage; - REQUIRE(storageCopy.on_open); - REQUIRE(calledCount == 2); - storageCopy.on_open(nullptr); - REQUIRE(calledCount == 3); + struct Banco { + int id_bank; + std::string nombre; + std::string ubicacion; + int fkey_pais; + }; - storageCopy.sync_schema(); - storageCopy.remove_all(); -} + struct AccountOwner { + int id_owner; + std::string name; + }; -TEST_CASE("Set null") { + struct Transaccion { + int id_transaccion; + double amount_colones; + double amount_dolares; + int fkey_account_own; // Account + int fkey_account_other = 0; // Account optional + + long line_date; + std::string descripcion; + int fkey_category; + int fkey_concepto; + int fkey_statement; + int row; // fkey_statement + row is unique + }; - struct User { - int id = 0; - std::unique_ptr name; + struct Categoria { + int id_categoria; + std::string name; + bool is_expense_or_income; }; auto storage = make_storage( {}, - make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name))); + make_table("Statement", + make_column("id_statement", &Statement::id_statement, autoincrement(), primary_key()), + make_column("date", &Statement::date)), + make_table("Categoria", + make_column("id_category", &Categoria::id_categoria, autoincrement(), primary_key()), + make_column("name", &Categoria::name, collate_nocase()), + make_column("is_expense_or_income", &Categoria::is_expense_or_income)), + make_table("Concepto", + make_column("id_concepto", &Concepto::id_concepto, autoincrement(), primary_key()), + make_column("name", &Concepto::name, collate_nocase()), + make_column("fkey_account", &Concepto::fkey_account), + foreign_key(&Concepto::fkey_account).references(&Account::id_account)), + make_table("Account", + make_column("id_account", &Account::id_account, autoincrement(), primary_key()), + make_column("number", &Account::number, collate_nocase()), + make_column("fkey_bank", &Account::fkey_bank), + make_column("fkey_account_owner", &Account::fkey_account_owner), + make_column("description", &Account::description, collate_nocase()), + make_column("is_tarjeta", &Account::is_tarjeta), + foreign_key(&Account::fkey_account_owner).references(&AccountOwner::id_owner), + foreign_key(&Account::fkey_bank).references(&Banco::id_bank)), + make_table("Banco", + make_column("id_bank", &Banco::id_bank, autoincrement(), primary_key()), + make_column("nombre", &Banco::nombre, collate_nocase()), + make_column("ubicacion", &Banco::ubicacion, collate_nocase()), + make_column("fkey_Pais", &Banco::fkey_pais), + foreign_key(&Banco::fkey_pais).references(&Pais::id_pais)), + make_table("AccountOwner", + make_column("id_owner", &AccountOwner::id_owner, autoincrement(), primary_key()), + make_column("name", &AccountOwner::name, collate_nocase())), + make_table("Transaccion", + make_column("id_transaccion", &Transaccion::id_transaccion, autoincrement(), primary_key()), + make_column("colones", &Transaccion::amount_colones), + make_column("dolares", &Transaccion::amount_dolares), + make_column("fkey_account_own", &Transaccion::fkey_account_own), + make_column("fkey_account_other", &Transaccion::fkey_account_other), + make_column("line_date", &Transaccion::line_date), + make_column("descripcion", &Transaccion::descripcion), + make_column("fkey_category", &Transaccion::fkey_category), + make_column("concepto", &Transaccion::fkey_concepto), + make_column("fkey_statement", &Transaccion::fkey_statement), + make_column("row", &Transaccion::row), + foreign_key(&Transaccion::fkey_account_own).references(&Account::id_account), + foreign_key(&Transaccion::fkey_account_other).references(&Account::id_account), + foreign_key(&Transaccion::fkey_category).references(&Categoria::id_categoria), + foreign_key(&Transaccion::fkey_concepto).references(&Concepto::id_concepto), + foreign_key(&Transaccion::fkey_statement).references(&Statement::id_statement)), + make_table("Pais", + make_column("id_pais", &Pais::id_pais, autoincrement(), primary_key()), + make_column("name", &Pais::name, collate_nocase()))); storage.sync_schema(); - storage.replace(User{1, std::make_unique("Ototo")}); - REQUIRE(storage.count() == 1); - { - auto rows = storage.get_all(); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front().name); - } - - storage.update_all(set(assign(&User::name, nullptr))); - { - auto rows = storage.get_all(); - REQUIRE(rows.size() == 1); - REQUIRE(!rows.front().name); - } - - storage.update_all(set(assign(&User::name, "ototo"))); - { - auto rows = storage.get_all(); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front().name); - REQUIRE(*rows.front().name == "ototo"); - } - - storage.update_all(set(assign(&User::name, nullptr)), where(is_equal(&User::id, 1))); - { - auto rows = storage.get_all(); - REQUIRE(rows.size() == 1); - REQUIRE(!rows.front().name); - } + using als_t = alias_t; + using als_a = alias_a; + using als_b = alias_b; + + std::ignore = storage.select(columns(alias_column(&Transaccion::fkey_account_other), + alias_column(&Transaccion::fkey_account_own), + alias_column(&Account::id_account), + alias_column(&Account::id_account)), + left_outer_join(on(c(alias_column(&Transaccion::fkey_account_other)) == + alias_column(&Account::id_account))), + inner_join(on(c(alias_column(&Transaccion::fkey_account_own)) == + alias_column(&Account::id_account)))); + + std::ignore = storage.select(columns(alias_column(&Transaccion::fkey_account_other), + alias_column(&Transaccion::fkey_account_own), + alias_column(&Account::id_account), + alias_column(&Account::id_account)), + left_join(on(c(alias_column(&Transaccion::fkey_account_other)) == + alias_column(&Account::id_account))), + inner_join(on(c(alias_column(&Transaccion::fkey_account_own)) == + alias_column(&Account::id_account)))); + + std::ignore = storage.select(columns(alias_column(&Transaccion::fkey_account_other), + alias_column(&Transaccion::fkey_account_own), + alias_column(&Account::id_account), + alias_column(&Account::id_account)), + join(on(c(alias_column(&Transaccion::fkey_account_other)) == + alias_column(&Account::id_account))), + inner_join(on(c(alias_column(&Transaccion::fkey_account_own)) == + alias_column(&Account::id_account)))); } diff --git a/tests/tests5.cpp b/tests/tests5.cpp index 866c17be4..1287ab9af 100644 --- a/tests/tests5.cpp +++ b/tests/tests5.cpp @@ -4,44 +4,6 @@ using namespace sqlite_orm; -TEST_CASE("Exists") { - struct User { - int id = 0; - std::string name; - }; - - struct Visit { - int id = 0; - int userId = 0; - time_t time = 0; - }; - - auto storage = - make_storage("", - make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)), - make_table("visits", - make_column("id", &Visit::id, primary_key()), - make_column("userId", &Visit::userId), - make_column("time", &Visit::time), - foreign_key(&Visit::userId).references(&User::id))); - storage.sync_schema(); - - storage.replace(User{1, "Daddy Yankee"}); - storage.replace(User{2, "Don Omar"}); - - storage.replace(Visit{1, 1, 100000}); - storage.replace(Visit{2, 1, 100001}); - storage.replace(Visit{3, 1, 100002}); - storage.replace(Visit{4, 1, 200000}); - - storage.replace(Visit{5, 2, 100000}); - - auto rows = storage.select( - &User::id, - where(exists(select(&Visit::id, where(c(&Visit::time) == 200000 and eq(&Visit::userId, &User::id)))))); - REQUIRE(!rows.empty() == 1); -} - TEST_CASE("Iterate blob") { struct Test { int64_t id; @@ -49,7 +11,7 @@ TEST_CASE("Iterate blob") { }; struct TestComparator { - bool operator()(const Test &lhs, const Test &rhs) const { + bool operator()(const Test& lhs, const Test& rhs) const { return lhs.id == rhs.id && lhs.key == rhs.key; } }; @@ -67,11 +29,11 @@ TEST_CASE("Iterate blob") { db.replace(v); TestComparator testComparator; - for(auto &obj: db.iterate()) { + for(auto& obj: db.iterate()) { REQUIRE(testComparator(obj, v)); } // test that view_t and iterator_t compile - for(const auto &obj: db.iterate()) { + for(const auto& obj: db.iterate()) { REQUIRE(testComparator(obj, v)); } // test that view_t and iterator_t compile @@ -85,7 +47,7 @@ TEST_CASE("Iterate blob") { } { int iterationsCount = 0; - for(auto &w: db.iterate(where(c(&Test::key) == key))) { + for(auto& w: db.iterate(where(c(&Test::key) == key))) { REQUIRE(testComparator(w, v)); ++iterationsCount; } @@ -114,23 +76,23 @@ TEST_CASE("Different getters and setters") { return this->name; } - void setNameByConstRef(const std::string &name_) { + void setNameByConstRef(const std::string& name_) { this->name = name_; } - const int &getConstIdByRefConst() const { + const int& getConstIdByRefConst() const { return this->id; } - void setIdByRef(int &id_) { + void setIdByRef(int& id_) { this->id = id_; } - const std::string &getConstNameByRefConst() const { + const std::string& getConstNameByRefConst() const { return this->name; } - void setNameByRef(std::string &name_) { + void setNameByRef(std::string& name_) { this->name = std::move(name_); } }; @@ -298,3 +260,27 @@ TEST_CASE("Dump") { REQUIRE(dumpUser2 == std::string{"{ id : '2', car_year : '2006' }"}); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED + +TEST_CASE("issue730") { + struct Table { + int64_t id; + std::string a; + std::string b; + std::string c; + }; + auto storage = make_storage({}, + make_table("table", + make_column("id", &Table::id), + make_column("a", &Table::a), + make_column("b", &Table::b), + make_column("c", &Table::c), + sqlite_orm::unique(&Table::a, &Table::b, &Table::c))); + storage.sync_schema(); + + auto rows = storage.select(asterisk()); + + using Rows = decltype(rows); + using ExpectedRows = std::vector>; + + static_assert(std::is_same::value, ""); +} diff --git a/tests/third_party/sqlite/CMakeLists.txt b/tests/third_party/sqlite/CMakeLists.txt deleted file mode 100644 index c792b188d..000000000 --- a/tests/third_party/sqlite/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -cmake_minimum_required (VERSION 3.2) - -set(SQLITE3_ARCH_NAME "${CMAKE_CURRENT_BINARY_DIR}/sqlite.zip") -set(SQLITE3_LINK "https://www.sqlite.org/2020/sqlite-amalgamation-3320300.zip") -set(SQLITE3_ARCH_SHA1 "0c805bea134712a903290a26b2a61c3a8a3bd8cc") - -add_custom_command( - OUTPUT ${SQLITE3_ARCH_NAME} - COMMAND - ${CMAKE_COMMAND} -DSQLITE3_ARCH_NAME=${SQLITE3_ARCH_NAME} -DSQLITE3_LINK=${SQLITE3_LINK} -DSQLITE3_ARCH_SHA1=${SQLITE3_ARCH_SHA1} -P "${CMAKE_CURRENT_SOURCE_DIR}/DownloadSqlite3.cmake") - -add_custom_command( - OUTPUT sqlite-amalgamation-3320300 sqlite-amalgamation-3320300/sqlite3.c sqlite-amalgamation-3320300/sqlite3.h - DEPENDS ${SQLITE3_ARCH_NAME} - COMMAND - ${CMAKE_COMMAND} -E tar xfz ${SQLITE3_ARCH_NAME}) - -add_library(sqlite3 sqlite-amalgamation-3320300/sqlite3.c) - -set(THREADS_PREFER_PTHREAD_FLAG ON) -find_package(Threads REQUIRED) - -target_include_directories(sqlite3 INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/sqlite-amalgamation-3320300") -target_link_libraries(sqlite3 PRIVATE ${CMAKE_DL_LIBS} Threads::Threads) diff --git a/tests/third_party/sqlite/DownloadSqlite3.cmake b/tests/third_party/sqlite/DownloadSqlite3.cmake deleted file mode 100644 index 06ebb028c..000000000 --- a/tests/third_party/sqlite/DownloadSqlite3.cmake +++ /dev/null @@ -1,16 +0,0 @@ -cmake_minimum_required (VERSION 3.2) - -file(DOWNLOAD ${SQLITE3_LINK} ${SQLITE3_ARCH_NAME} - INACTIVITY_TIMEOUT 5 - STATUS SQLITE3_DOWNLOADED - SHOW_PROGRESS) - -if(NOT SQLITE3_DOWNLOADED) - message(FATAL_ERROR "Unable to download ${SQLITE3_LINK}") -endif() - -file(SHA1 ${SQLITE3_ARCH_NAME} SQLITE3_SHA1) -if(${SQLITE3_SHA1} STREQUAL ${SQLITE3_ARCH_SHA1}) -else() - message(FATAL_ERROR "Invalid hash: ${SQLITE3_SHA1} != ${SQLITE3_ARCH_SHA1}") -endif() diff --git a/tests/transaction_tests.cpp b/tests/transaction_tests.cpp index 301fe1f0f..07509f03d 100644 --- a/tests/transaction_tests.cpp +++ b/tests/transaction_tests.cpp @@ -23,6 +23,38 @@ TEST_CASE("transaction") { REQUIRE(!storage.is_opened()); } +TEST_CASE("transaction_rollback") { + struct Object { + int id = 0; + std::string name; + }; + + auto storage = make_storage( + "test_transaction_guard.sqlite", + make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name))); + + storage.sync_schema(); + storage.remove_all(); + + storage.insert(Object{0, "Jack"}); + + SECTION("insert, call make a storage to call an exception and check that rollback was fired") { + auto countBefore = storage.count(); + try { + storage.transaction([&] { + storage.insert(Object{0, "John"}); + storage.get(-1); + REQUIRE(false); + return true; + }); + } catch(...) { + auto countNow = storage.count(); + + REQUIRE(countBefore == countNow); + } + } +} + TEST_CASE("Transaction guard") { struct Object { int id = 0; diff --git a/tests/tuple_helper_tests.cpp b/tests/tuple_helper_tests.cpp new file mode 100644 index 000000000..b0bb0346f --- /dev/null +++ b/tests/tuple_helper_tests.cpp @@ -0,0 +1,66 @@ +#include +#include +#include // std::type_index +#include // std::string +#include // std::decay + +using namespace sqlite_orm; + +TEST_CASE("tuple_helper") { + using namespace internal; + + std::vector expected; + std::vector types; + SECTION("iterate_tuple with tuple instance") { + auto lambda = [&types](const auto &item) { + types.push_back(typeid(item)); + }; + SECTION("empty") { + std::tuple<> tuple; + iterate_tuple(tuple, lambda); + } + SECTION("int") { + std::tuple tuple; + expected.push_back(typeid(int)); + iterate_tuple(tuple, lambda); + } + SECTION("char, long") { + std::tuple tuple; + expected.push_back(typeid(char)); + expected.push_back(typeid(long)); + iterate_tuple(tuple, lambda); + } + SECTION("std::string, std::string, int") { + std::tuple tuple; + expected.push_back(typeid(std::string)); + expected.push_back(typeid(std::string)); + expected.push_back(typeid(int)); + iterate_tuple(tuple, lambda); + } + } + SECTION("iterate_tuple with no tuple instance") { + auto lambda = [&types](auto *itemPointer) { + using Item = typename std::remove_pointer::type; + types.push_back(typeid(Item)); + }; + SECTION("empty") { + iterate_tuple>(lambda); + } + SECTION("int") { + iterate_tuple>(lambda); + expected.push_back(typeid(int)); + } + SECTION("char, long") { + iterate_tuple>(lambda); + expected.push_back(typeid(char)); + expected.push_back(typeid(long)); + } + SECTION("std::string, std::string, int") { + iterate_tuple>(lambda); + expected.push_back(typeid(std::string)); + expected.push_back(typeid(std::string)); + expected.push_back(typeid(int)); + } + } + REQUIRE(expected == types); +} diff --git a/tests/unique_cases/get_all_with_two_tables.cpp b/tests/unique_cases/get_all_with_two_tables.cpp index 878904aaf..423e602d8 100644 --- a/tests/unique_cases/get_all_with_two_tables.cpp +++ b/tests/unique_cases/get_all_with_two_tables.cpp @@ -12,7 +12,7 @@ namespace GetAllWithTwoTablesInternal { std::string attributes; }; - inline bool operator==(const Item &lhs, const Item &rhs) { + inline bool operator==(const Item& lhs, const Item& rhs) { return lhs.id == rhs.id && lhs.attributes == rhs.attributes; } } @@ -36,12 +36,26 @@ TEST_CASE("get_all with two tables") { storage.replace(item2); storage.replace(item3); - std::vector patterns; - patterns.push_back({"n"}); - patterns.push_back({"w"}); + SECTION("straight insert") { + std::vector patterns; + patterns.push_back({"n"}); + patterns.push_back({"w"}); - storage.begin_transaction(); - storage.insert_range(patterns.begin(), patterns.end()); + storage.begin_transaction(); + storage.insert_range(patterns.begin(), patterns.end()); + } + SECTION("pointers insert") { + std::vector> patterns; + patterns.push_back(std::make_unique(Pattern{"n"})); + patterns.push_back(std::make_unique(Pattern{"w"})); + + storage.begin_transaction(); + storage.insert_range(patterns.begin(), + patterns.end(), + [](const std::unique_ptr& pointer) -> const Pattern& { + return *pointer; + }); + } { auto rows = storage.select(&Item::id, where(like(&Item::attributes, conc(conc("%", &Pattern::value), "%")))); REQUIRE_THAT(rows, UnorderedEquals({1, 2})); diff --git a/tests/unique_cases/issue663.cpp b/tests/unique_cases/issue663.cpp new file mode 100644 index 000000000..73b3f6bbd --- /dev/null +++ b/tests/unique_cases/issue663.cpp @@ -0,0 +1,197 @@ +#include +#include + +using namespace sqlite_orm; + +namespace { + namespace primary_key_case { + + template + void insertSection(S& storage) { + TUser user{}; + user.id = -1; + user.name = "Juan"; + user.age = 57; + user.email = "dummy@email.com"; + const auto id = storage.insert(user); + const auto users = storage.template get_all(); + REQUIRE(users.size() == 1); + REQUIRE(-1 != users.front().id); + REQUIRE(id == users.front().id); + } + + template + void insertRangeSection(S& storage) { + std::vector usersInput; + usersInput.push_back({-1, "Juan", 57, "dummy@email.com"}); + usersInput.push_back({-1, "Kevin", 27, "dummy@email.com"}); + storage.insert_range(usersInput.begin(), usersInput.end()); + const auto users = storage.template get_all(); + REQUIRE(users.size() == usersInput.size()); + for(size_t i = 0; i < users.size(); ++i) { + REQUIRE(-1 != users[i].id); + usersInput[i].id = users[i].id; + } + REQUIRE(users == usersInput); + } + + } // end of namespace primary_key_case + namespace default_value_case { + + static const char* const defaultID = "100"; + static const char* const defaultName = "dummy_name"; + + template + void insertSection(S& storage) { + storage.template insert({"_", "_"}); + const auto users = storage.template get_all(); + REQUIRE(users.size() == 1); + REQUIRE(defaultID == users.front().id); + REQUIRE(defaultName == users.front().name); + } + + template + void insertRangeSection(S& storage) { + std::vector inputUsers = {{"_", "_"}}; + storage.insert_range(inputUsers.begin(), inputUsers.end()); + const auto users = storage.template get_all(); + REQUIRE(users.size() == 1); + REQUIRE(defaultID == users.front().id); + REQUIRE(defaultName == users.front().name); + } + + } // end of namespace default_value_case +} // end of anonymous namespace + +TEST_CASE("Issue 663 - pk inside") { + + struct User { + bool operator==(const User& rhs) const { + return std::tie(id, name, age, email) == std::tie(rhs.id, rhs.name, rhs.age, rhs.email); + } + bool operator!=(const User& rhs) const { + return !(rhs == *this); + } + int id; + std::string name; + int age; + std::string email; + }; + + auto storage = make_storage("", /// + make_table("users", + make_column("id", &User::id, primary_key()), + make_column("name", &User::name), + make_column("age", &User::age), + make_column("email", &User::email, default_value("dummy@email.com")))); + storage.sync_schema(); + + SECTION("insert") { + primary_key_case::insertSection(storage); + } + + SECTION("insert_range") { + primary_key_case::insertRangeSection(storage); + } +} + +TEST_CASE("Issue 663 - pk outside") { + + struct User { + bool operator==(const User& rhs) const { + return std::tie(id, name, age, email) == std::tie(rhs.id, rhs.name, rhs.age, rhs.email); + } + bool operator!=(const User& rhs) const { + return !(rhs == *this); + } + int id; + std::string name; + int age; + std::string email; + }; + + auto storage = make_storage("", /// + make_table("users", + make_column("id", &User::id), + make_column("name", &User::name), + make_column("age", &User::age), + make_column("email", &User::email, default_value("dummy@email.com")), + primary_key(&User::id))); + storage.sync_schema(); + + SECTION("insert") { + primary_key_case::insertSection(storage); + } + + SECTION("insert_range") { + primary_key_case::insertRangeSection(storage); + } +} + +TEST_CASE("Issue 663 - pk outside, with default") { + + struct User { + std::string id; + std::string name; + }; + + auto storage = + make_storage("", /// + make_table("users", + make_column("id", &User::id, default_value(default_value_case::defaultID)), + make_column("name", &User::name, default_value(default_value_case::defaultName)), + primary_key(&User::id, &User::name))); + storage.sync_schema(); + + SECTION("insert") { + default_value_case::insertSection(storage); + } + + SECTION("insert_range") { + default_value_case::insertRangeSection(storage); + } +} + +TEST_CASE("Issue 663 - pk inside, with default") { + struct User { + std::string id; + }; + + auto storage = make_storage("", /// + make_table("users", make_column("id", &User::id, primary_key(), default_value("200")))); + storage.sync_schema(); + + SECTION("insert") { + storage.insert({"_"}); + const auto users = storage.get_all(); + REQUIRE(users.size() == 1); + REQUIRE("200" == users.front().id); + } + + SECTION("insert_range") { + std::vector inputUsers = {{"_"}}; + storage.insert_range(inputUsers.begin(), inputUsers.end()); + const auto users = storage.get_all(); + REQUIRE(users.size() == 1); + REQUIRE("200" == users.front().id); + } +} + +TEST_CASE("Issue 663 - fail test") { + struct User { + std::string id; + }; + + auto storage = make_storage("", /// + make_table("users", make_column("id", &User::id, primary_key(), default_value("200")))); + storage.sync_schema(); + + std::vector inputUsers = {{"_"}, {"_"}}; + try { + storage.insert_range(inputUsers.begin(), inputUsers.end()); + REQUIRE(false); + } catch(const std::system_error& e) { + REQUIRE(e.code() == std::make_error_code(orm_error_code::cannot_use_default_value)); + REQUIRE(storage.count() == 0); + } +} \ No newline at end of file diff --git a/tests/unique_cases/nonstandart_primary_key.cpp b/tests/unique_cases/nonstandart_primary_key.cpp new file mode 100644 index 000000000..588da38f1 --- /dev/null +++ b/tests/unique_cases/nonstandart_primary_key.cpp @@ -0,0 +1,52 @@ +#include +#include + +using namespace sqlite_orm; + +namespace { + const std::string expectedErrorText = + "Attempting to execute 'insert' request resulted in an error like \"NOT NULL constraint failed: " + "users.username: constraint failed\". Perhaps ordinary 'insert' is not acceptable for this table and you " + "should try 'replace' or 'insert' with explicit column listing?: constraint failed"; +} + +TEST_CASE("Nonstandart primary key - fail test") { + struct User { + std::string username; + std::string password; + bool isActive; + }; + + auto storage = make_storage({}, + make_table("users", + make_column("username", &User::username), + make_column("password", &User::password), + make_column("isActive", &User::isActive), + primary_key(&User::username))); + storage.sync_schema(); + + SECTION("insert") { + try { + storage.insert(User{"testName", "testPassword2", false}); + REQUIRE(false); + } catch(const std::system_error& e) { + if(e.code() != std::error_code(SQLITE_CONSTRAINT, get_sqlite_error_category())) + throw; + REQUIRE(e.what() == expectedErrorText); + } + } + + SECTION("insert_range") { + try { + std::vector users = {/// + {"testName1", "testPassword1", false}, + {"testName2", "testPassword2", true}}; + storage.insert_range(users.begin(), users.end()); + REQUIRE(false); + } catch(const std::system_error& e) { + if(e.code() != std::error_code(SQLITE_CONSTRAINT, get_sqlite_error_category())) + throw; + REQUIRE(e.what() == expectedErrorText); + } + } +} diff --git a/third_party/amalgamate/config.json b/third_party/amalgamate/config.json index d9dfc9c54..abb516bd5 100755 --- a/third_party/amalgamate/config.json +++ b/third_party/amalgamate/config.json @@ -4,7 +4,11 @@ "sources": [ "dev/start_macros.h", "dev/error_code.h", - "dev/tuple_helper.h", + "dev/tuple_helper/tuple_helper.h", + "dev/tuple_helper/find_in_tuple.h", + "dev/tuple_helper/tuple_transformer.h", + "dev/tuple_helper/count_tuple.h", + "dev/tuple_helper/same_or_void.h", "dev/type_printer.h", "dev/collate_argument.h", "dev/constraints.h", @@ -19,12 +23,12 @@ "dev/core_functions.h", "dev/typed_comparator.h", "dev/select_constraints.h", - "dev/table_type.h", "dev/table_info.h", "dev/statement_finalizer.h", "dev/arithmetic_tag.h", "dev/statement_binder.h", "dev/row_extractor.h", + "dev/util.h", "dev/sync_schema_result.h", "dev/index.h", "dev/mapped_type_proxy.h", @@ -34,8 +38,8 @@ "dev/storage_impl.h", "dev/storage.h", "dev/finish_macros.h", - "dev/node_tuple.h", - "dev/get_prepared_statement.h" + "dev/node_tuple.h", + "dev/get_prepared_statement.h" ], "include_paths": ["dev"] }