diff --git a/CMakeLists.txt b/CMakeLists.txt index daa4187..9b0022d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,19 +7,25 @@ option(ENABLE_TSAN "Enable the thread data race sanitizer" OFF) set(CMAKE_CXX_STANDARD 17) +add_compile_options(-march=native) + +set(SQLITE_ORM_ENABLE_CXX_17 ON) + add_subdirectory(libs/blt) add_subdirectory(libs/DPP-10.0.29) +add_subdirectory(libs/sqlite_orm-1.8.2) include_directories(include/) file(GLOB_RECURSE PROJECT_BUILD_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") add_executable(discord_bot ${PROJECT_BUILD_FILES}) -target_compile_options(discord_bot PRIVATE -Wall -Werror -Wpedantic -Wno-comment) -target_link_options(discord_bot PRIVATE -Wall -Werror -Wpedantic -Wno-comment) +target_compile_options(discord_bot PUBLIC -Wall -Wpedantic -Wno-comment -march=native) +target_link_options(discord_bot PUBLIC -Wall -Wpedantic -Wno-comment) target_link_libraries(discord_bot PUBLIC BLT) target_link_libraries(discord_bot PUBLIC dpp) +target_link_libraries(discord_bot PUBLIC sqlite_orm) if (${ENABLE_ADDRSAN} MATCHES ON) target_compile_options(discord_bot PRIVATE -fsanitize=address) diff --git a/include/filemanager.h b/include/filemanager.h new file mode 100644 index 0000000..ffab1d6 --- /dev/null +++ b/include/filemanager.h @@ -0,0 +1,145 @@ +#pragma once +/* + * Copyright (C) 2024 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DISCORD_BOT_FILEMANAGER_H +#define DISCORD_BOT_FILEMANAGER_H + +#include +#include +#include +#include +#include +#include +#include + +namespace db +{ + // path to archive_folder/raw/guildID/current_day/channelID(name)/ + // in archive_folder/raw/guildID/ + // (days) + // server_name.txt (changes in server name) + // migrations.txt (changes in channel name) + // channels.txt (deleted / create channels) + + // in current_day: + // channelID + + // message id: (username): + class msg_fs_manager + { + private: + std::fstream msg_file; + public: + explicit msg_fs_manager(const std::string& file): msg_file(file, std::ios::in | std::ios::out) + {} + }; + + class channel_fs_manager + { + private: + blt::u64 channelID; + HASHMAP msgs; + public: + explicit channel_fs_manager(blt::u64 channelID): channelID(channelID) + {} + }; + + class channel_flusher + { + private: + std::vector> channels_to_flush; + public: + void flush_channels(HASHMAP>& map); + }; + + class guild_fs_manager + { + private: + std::string archive_path; + std::string path; + blt::u64 guildID; + HASHMAP> channels; + dpp::cluster& bot; + channel_flusher flusher; + std::fstream f_server_name; + std::fstream f_migrations; + std::fstream f_channels; + std::atomic_int32_t complete{0}; + + void check_for_channel_updates(); + + void check_for_guild_updates(); + + public: + guild_fs_manager(std::string_view archive_path, std::string_view filePath, blt::u64 guildID, dpp::cluster& bot): + archive_path(archive_path), path(filePath), guildID(guildID), bot(bot), + f_server_name(this->archive_path + "server_name.txt", std::ios::out | std::ios::in | std::ios::app), + f_migrations(this->archive_path + "migrations.txt", std::ios::out | std::ios::in | std::ios::app), + f_channels(this->archive_path + "channels.txt", std::ios::out | std::ios::in | std::ios::app) + {} + + void message_create(blt::u64 channel_id, blt::u64 msg_id, std::string_view content, std::string_view username, + std::string_view display_name, const std::vector& attachments); + + void message_delete(blt::u64 channel_id, blt::u64 msg_id); + + void message_bulk_delete(blt::u64 channel_id, const std::vector& message_ids) + { + for (const auto& v : message_ids) + message_delete(channel_id, format_as(v)); + } + + void message_update(blt::u64 channel_id, blt::u64 msg_id, std::string_view new_content); + + void flush(std::string new_path); + + void await_completions(); + }; + + class fs_manager + { + private: + HASHMAP> guild_handlers; + dpp::cluster& bot; + std::string archive_path; + int current_day = 0; + std::atomic_bool is_flushing{false}; + + std::string create_path(blt::u64 guildID); + + public: + explicit fs_manager(std::string_view archive_path, dpp::cluster& bot); + + void create(blt::u64 guildID) + { + guild_handlers.insert({guildID, std::make_unique(archive_path, create_path(guildID), guildID, bot)}); + } + + void flush(); + + guild_fs_manager& operator[](blt::u64 guildID) + { + while (is_flushing) + {} + return *guild_handlers.at(guildID); + } + }; + +} + +#endif //DISCORD_BOT_FILEMANAGER_H diff --git a/libs/blt b/libs/blt index 3845293..61d46de 160000 --- a/libs/blt +++ b/libs/blt @@ -1 +1 @@ -Subproject commit 384529333c0f46ef76dced3d78769250d3e227e6 +Subproject commit 61d46de5737b09d1eb1a6ef240f2af5e6ef2de71 diff --git a/libs/sqlite_orm-1.8.2/.clang-format b/libs/sqlite_orm-1.8.2/.clang-format new file mode 100644 index 0000000..f4bc58f --- /dev/null +++ b/libs/sqlite_orm-1.8.2/.clang-format @@ -0,0 +1,125 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortLambdasOnASingleLine: Empty +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: AfterColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: Never +SpaceBeforeRangeBasedForLoopColon: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +StatementMacros: + - __pragma + - _Pragma + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +... + diff --git a/libs/sqlite_orm-1.8.2/.github/FUNDING.yml b/libs/sqlite_orm-1.8.2/.github/FUNDING.yml new file mode 100644 index 0000000..0ddfa7a --- /dev/null +++ b/libs/sqlite_orm-1.8.2/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: fnc12 +custom: https://paypal.me/fnc12 diff --git a/libs/sqlite_orm-1.8.2/.github/workflows/clang-format-lint.yaml b/libs/sqlite_orm-1.8.2/.github/workflows/clang-format-lint.yaml new file mode 100644 index 0000000..3916bbb --- /dev/null +++ b/libs/sqlite_orm-1.8.2/.github/workflows/clang-format-lint.yaml @@ -0,0 +1,14 @@ +name: clang-format lint + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: clang-format lint + uses: DoozyX/clang-format-lint-action@v0.15 + with: + clangFormatVersion: 15 diff --git a/libs/sqlite_orm-1.8.2/.gitignore b/libs/sqlite_orm-1.8.2/.gitignore new file mode 100644 index 0000000..f5cff1a --- /dev/null +++ b/libs/sqlite_orm-1.8.2/.gitignore @@ -0,0 +1,8 @@ +.DS_store +examples/simple_neural_network.cpp + +cmake-build-debug/ + +.idea/ + +/compile diff --git a/libs/sqlite_orm-1.8.2/.travis.yml b/libs/sqlite_orm-1.8.2/.travis.yml new file mode 100644 index 0000000..d0a3685 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/.travis.yml @@ -0,0 +1,157 @@ +# Defaults +os: linux +dist: focal + +matrix: + include: + - name: "[C++14] GCC-9" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-9 + - ninja-build + env: + - CC: gcc-9 + - CXX: g++-9 + + - name: "[C++14] GCC-7" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-7 + - ninja-build + env: + - CC: gcc-7 + - CXX: g++-7 + + - name: "[C++14] LLVM/Clang (Travis default)" + language: cpp + compiler: clang + addons: + apt: + packages: + - ninja-build + 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: + - ninja + update: true + + - 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 + - ninja + update: true + env: + - CC: gcc-6 + - CXX: g++-6 + + - name: "[C++17] GCC-9" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-9 + - ninja-build + env: + - CC: gcc-9 + - CXX: g++-9 + - SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_17=ON" + + - name: "[C++17] GCC-7" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-7 + - ninja-build + env: + - CC: gcc-7 + - 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: + - ninja + update: true + + - 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"; fi + +# scripts to run before build +before_script: + - if [[ "$CXX" == *"clang"* ]]; then clang --version ; fi + - cd ${TRAVIS_BUILD_DIR} + - mkdir compile && cd compile + - cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ${SQLITE_ORM_CXX_STANDARD} -DSQLITE_ORM_OMITS_CODECVT="${SQLITE_ORM_OMITS_CODECVT:OFF}" .. + +# 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) \ No newline at end of file diff --git a/libs/sqlite_orm-1.8.2/CMakeLists.txt b/libs/sqlite_orm-1.8.2/CMakeLists.txt new file mode 100644 index 0000000..a56294f --- /dev/null +++ b/libs/sqlite_orm-1.8.2/CMakeLists.txt @@ -0,0 +1,93 @@ +# note: the minimum required version needs to go hand in hand with appveyor builds, +# which is CMake 3.16 for the Visual Studio 2017 build worker image +cmake_minimum_required (VERSION 3.16) + +# 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.8.0") +set(PACKAGE_VERSION ${sqlite_orm_VERSION}) + +project("sqlite_orm" VERSION ${PACKAGE_VERSION}) + +# Handling C++ standard version to use +option(SQLITE_ORM_ENABLE_CXX_20 "Enable C++ 20" OFF) +option(SQLITE_ORM_ENABLE_CXX_17 "Enable C++ 17" OFF) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +if(SQLITE_ORM_ENABLE_CXX_20) + set(CMAKE_CXX_STANDARD 20) + message(STATUS "SQLITE_ORM: Build with C++20 features") +elseif(SQLITE_ORM_ENABLE_CXX_17) + set(CMAKE_CXX_STANDARD 17) + message(STATUS "SQLITE_ORM: Build with C++17 features") +else() + # fallback to C++14 if there is no special instruction + set(CMAKE_CXX_STANDARD 14) + message(STATUS "SQLITE_ORM: Build with C++14 features") +endif() +set(CMAKE_CXX_EXTENSIONS OFF) + + +set(CMAKE_VERBOSE_MAKEFILE ON) + +message(STATUS "Configuring ${PROJECT_NAME} ${sqlite_orm_VERSION}") + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") + +include(CTest) + +### Dependencies +add_subdirectory(dependencies) + +### Main Build Targets +set(SqliteOrm_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/include") +add_library(sqlite_orm INTERFACE) +add_library(sqlite_orm::sqlite_orm ALIAS sqlite_orm) + +find_package(SQLite3 REQUIRED) +target_link_libraries(sqlite_orm INTERFACE SQLite::SQLite3) + +target_sources(sqlite_orm INTERFACE $) + +target_include_directories(sqlite_orm INTERFACE $) + +include(ucm) + +if (MSVC) + string(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") + string(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") + add_compile_options(/EHsc) + if (MSVC_VERSION GREATER_EQUAL 1914) + add_compile_options(/Zc:__cplusplus) + endif() + if (MSVC_VERSION GREATER_EQUAL 1910) + # VC 2017 issues a deprecation warning for `strncpy` + add_compile_definitions(_CRT_SECURE_NO_WARNINGS) + endif() + add_compile_options(/MP) # multi-processor compilation + if (CMAKE_CXX_STANDARD GREATER 14) + add_compile_definitions(_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING) + endif() + + if ("${CMAKE_GENERATOR}" MATCHES "(Win64|x64)") + message(STATUS "Add /bigobj flag to compiler") + add_compile_options(/bigobj) + endif() +endif() + +ucm_print_flags() +message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") + +# Tests +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) + add_subdirectory(tests) +endif() + +option(BUILD_EXAMPLES "Build code examples" OFF) +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + +### Packaging +add_subdirectory(packaging) diff --git a/libs/sqlite_orm-1.8.2/COMM-LICENSE b/libs/sqlite_orm-1.8.2/COMM-LICENSE new file mode 100644 index 0000000..d64eaa5 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/COMM-LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2012-2023 Eugene Zakharov 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/libs/sqlite_orm-1.8.2/CONTRIBUTING.md b/libs/sqlite_orm-1.8.2/CONTRIBUTING.md new file mode 100644 index 0000000..b1baf53 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/CONTRIBUTING.md @@ -0,0 +1,94 @@ +# How to Contribute # + +Thank you for your interest in contributing to the sqlite_orm project! + +## GitHub pull requests ## + +This is the preferred method of submitting changes. When you submit a pull request through github, +it activates the continuous integration (CI) build systems at Appveyor and Travis to build your changes +on a variety of Linux, Windows and MacOS configurations and run all the test suites. Follow these requirements +for a successful pull request: + + 1. All significant changes require a [github issue](https://github.com/fnc12/sqlite_orm/issues). Trivial changes such as fixing a typo or a compiler warning do not. + + 1. The pull request title must begin with the github issue identifier if it has an associated issue, for example: + + #9999 : an example pull request title + + 1. Commit messages must be understandable in future by different developers and must be written in english language only: + +Instructions: + + 1. Create a fork in your GitHub account of http://github.com/fnc12/sqlite_orm + 1. Clone the fork to your development system. + 1. Create a branch for your changes (best practice is following git flow pattern with issue number as branch name, e.g. feature/9999-some-feature or bugfix/9999-some-bug). + 1. Modify the source to include the improvement/bugfix, and: + + * Remember to provide *tests* for all submitted changes! + * Use test-driven development (TDD): add a test that will isolate the bug *before* applying the change that fixes it. + * Verify that you follow current code style on sqlite_orm. + * [*optional*] Verify that your change works on other platforms by adding a GitHub service hook to [Travis CI](http://docs.travis-ci.com/user/getting-started/#Step-one%3A-Sign-in) and [AppVeyor](http://www.appveyor.com/docs). You can use this technique to run the sqlite_orm CI jobs in your account to check your changes before they are made public. Every GitHub pull request into sqlite_orm will run the full CI build and test suite on your changes. + + 1. Commit and push changes to your branch (please use issue name and description as commit title, e.g. "make it perfect. (fixes #9999)"). + 1. Use GitHub to create a pull request going from your branch to sqlite_orm:dev. Ensure that the github issue number is at the beginning of the title of your pull request. + 1. Wait for other contributors or committers to review your new addition, and for a CI build to complete. + 1. Wait for a owner or collaborators to commit your patch. + +## If you want to build the project locally ## + +See our detailed instructions on the [CMake README](https://github.com/fnc12/sqlite_orm#usage). + +## If you want to review open issues... ## + + 1. Review the [GitHub Pull Request Backlog](https://github.com/fnc12/sqlite_orm/pulls). Code reviews are opened to all. + +## If you discovered a defect... ## + + 1. Check to see if the issue is already in the [github issues](https://github.com/fnc12/sqlite_orm/issues). + 1. If not please create an issue describing the change you're proposing in the github issues page. + 1. Contribute your code changes using the GitHub pull request method: + +## GitHub recipes for Pull Requests ## + +Sometimes commmitters may ask you to take actions in your pull requests. Here are some recipes that will help you accomplish those requests. These examples assume you are working on github issue 9999. You should also be familiar with the [upstream](https://help.github.com/articles/syncing-a-fork/) repository concept. + +### Squash your changes ### + +If you have commits with adding code which is removed in a different commit within the same PR then please squash all commits to remove unnecessary add commits. + +1. Use the command ``git log`` to identify how many commits you made since you began. +2. Use the command ``git rebase -i HEAD~N`` where N is the number of commits. +3. Leave "pull" in the first line. +4. Change all other lines from "pull" to "fixup". +5. All your changes are now in a single commit. + +If you already have a pull request outstanding, you will need to do a "force push" to overwrite it since you changed your commit history: + + git push -u origin feature/9999-make-perfect --force + +A more detailed walkthrough of a squash can be found at [Git Ready](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html). + +### Rebase your pull request ### + +If your pull request has a conflict with dev, it needs to be rebased: + + git checkout feature/9999-make-perfect + git rebase upstream dev + (resolve any conflicts, make sure it builds) + git push -u origin feature/9999-make-perfect --force + +### Fix a bad merge ### + +If your pull request contains commits that are not yours, then you should use the following technique to fix the bad merge in your branch: + + git checkout dev + git pull upstream dev + git checkout -b feature/9999-make-perfect-take-2 + git cherry-pick ... + (pick only your commits from your original pull request in ascending chronological order) + squash your changes to a single commit if there is more than one (see above) + git push -u origin feature/9999-make-perfect-take-2:feature/9999-make-perfect + +This procedure will apply only your commits in order to the current dev, then you will squash them to a single commit, and then you force push your local feature/9999-make-perfect-take-2 into remote feature/9999-make-perfect which represents your pull request, replacing all the commits with the new one. + + diff --git a/libs/sqlite_orm-1.8.2/LICENSE b/libs/sqlite_orm-1.8.2/LICENSE new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/libs/sqlite_orm-1.8.2/LICENSE @@ -0,0 +1,661 @@ + 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/libs/sqlite_orm-1.8.2/README.md b/libs/sqlite_orm-1.8.2/README.md new file mode 100644 index 0000000..9fbe234 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/README.md @@ -0,0 +1,813 @@ +

+ Sublime's custom image +

+ +[![C++](https://img.shields.io/badge/c++-%2300599C.svg?style=for-the-badge&logo=c%2B%2B&logoColor=white)](https://en.cppreference.com/w/) +[![SQLite](https://img.shields.io/badge/sqlite-%2307405e.svg?style=for-the-badge&logo=sqlite&logoColor=white)](https://www.sqlite.org/index.html) +[![GitHub Actions](https://img.shields.io/badge/githubactions-%232671E5.svg?style=for-the-badge&logo=githubactions&logoColor=white)](https://github.com/fnc12/sqlite_orm/actions) +[![CMake](https://img.shields.io/badge/CMake-%23008FBA.svg?style=for-the-badge&logo=cmake&logoColor=white)](https://github.com/fnc12/sqlite_orm/blob/dev/CMakeLists.txt) +[![Stack Overflow](https://img.shields.io/badge/-Stackoverflow-FE7A16?style=for-the-badge&logo=stack-overflow&logoColor=white)](https://stackoverflow.com/search?q=sqlite_orm) +[![PayPal](https://img.shields.io/badge/PayPal-00457C?style=for-the-badge&logo=paypal&logoColor=white)](https://paypal.me/fnc12) +[![Twitter](https://img.shields.io/badge/sqlite_orm-%231DA1F2.svg?style=for-the-badge&logo=Twitter&logoColor=white)](https://twitter.com/sqlite_orm) +[![Patreon](https://img.shields.io/badge/Patreon-F96854?style=for-the-badge&logo=patreon&logoColor=white)](https://patreon.com/fnc12) + +# SQLite ORM +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 | +| :----- | :----- | :------- | +| [`master`](https://github.com/fnc12/sqlite_orm/tree/master) | [![Build Status](https://travis-ci.org/fnc12/sqlite_orm.svg?branch=master)](https://travis-ci.org/fnc12/sqlite_orm) | [![Build status](https://ci.appveyor.com/api/projects/status/github/fnc12/sqlite_orm?branch=master&svg=true)](https://ci.appveyor.com/project/fnc12/sqlite-orm/history) | | | [![Website](https://img.shields.io/badge/official-website-brightgreen.svg)](https://github.com/fnc12/sqlite_orm/) | +| [`dev`](https://github.com/fnc12/sqlite_orm/tree/dev) | [![Build Status](https://travis-ci.org/fnc12/sqlite_orm.svg?branch=dev)](https://travis-ci.org/fnc12/sqlite_orm) | [![Build status](https://ci.appveyor.com/api/projects/status/github/fnc12/sqlite_orm?branch=dev&svg=true)](https://ci.appveyor.com/project/fnc12/sqlite-orm/history) | | | [![Website](https://img.shields.io/badge/official-website-brightgreen.svg)](https://github.com/fnc12/sqlite_orm/tree/dev) | + +# Advantages + +* **No raw string queries** +* **Intuitive syntax** +* **Comfortable interface - one code line per single query** +* **Built with modern C++14 features (no macros and external scripts)** +* **CRUD support** +* **Pure select query support** +* **Prepared statements support** +* **UNION, EXCEPT and INTERSECT support** +* **STL compatible** +* **Custom types binding support** +* **BLOB support** - maps to `std::vector` or one can bind your custom type +* **FOREIGN KEY support** +* **Composite key support** +* **JOIN support** +* **Transactions support** +* **Migrations functionality** +* **Powerful conditions** +* **ORDER BY and LIMIT, OFFSET support** +* **GROUP BY / DISTINCT support** +* **INDEX support** +* **Follows single responsibility principle** - no need write code inside your data model classes +* **Easy integration** - single header only lib. +* **The only dependency** - libsqlite3 +* **C++ standard code style** +* **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: + +```c++ + +struct User{ + int id; + std::string firstName; + std::string lastName; + int birthDate; + std::unique_ptr imageUrl; + int typeId; +}; + +struct UserType { + int id; + std::string name; +}; + +``` + +So we have database with predefined schema like + +`CREATE TABLE users (id integer primary key autoincrement, first_name text not null, last_name text not null, birth_date integer not null, image_url text, type_id integer not null)` + +`CREATE TABLE user_types (id integer primary key autoincrement, name text not null DEFAULT 'name_placeholder')` + +Now we tell `sqlite_orm` library about our schema and provide database filename. We create `storage` service object that has CRUD interface. Also we create every table and every column. All code is intuitive and minimalistic. + +```c++ + +using namespace sqlite_orm; +auto storage = make_storage("db.sqlite", + make_table("users", + make_column("id", &User::id, primary_key().autoincrement()), + make_column("first_name", &User::firstName), + make_column("last_name", &User::lastName), + make_column("birth_date", &User::birthDate), + make_column("image_url", &User::imageUrl), + make_column("type_id", &User::typeId)), + make_table("user_types", + make_column("id", &UserType::id, primary_key().autoincrement()), + make_column("name", &UserType::name, default_value("name_placeholder")))); +``` + +Too easy isn't it? You do not have to specify mapped type explicitly - it is deduced from your member pointers you pass during making a column (for example: `&User::id`). To create a column you have to pass two arguments at least: its name in the table and your mapped class member pointer. You can also add extra arguments to tell your storage about column's constraints like `primary_key`, `autoincrement`, `default_value`, `unique` or `generated_always_as` (order isn't important; `not_null` is deduced from type automatically). + +More details about making storage can be found in [tutorial](https://github.com/fnc12/sqlite_orm/wiki/Making-a-storage). + +If your datamodel classes have private or protected members to map to sqlite then you can make a storage with setter and getter functions. More info in the [example](https://github.com/fnc12/sqlite_orm/blob/master/examples/private_class_members.cpp). + +# CRUD + +Let's create and insert new `User` into our database. First we need to create a `User` object with any id and call `insert` function. It will return id of just created user or throw exception if something goes wrong. + +```c++ +User user{-1, "Jonh", "Doe", 664416000, std::make_unique("url_to_heaven"), 3 }; + +auto insertedId = storage.insert(user); +cout << "insertedId = " << insertedId << endl; // insertedId = 8 +user.id = insertedId; + +User secondUser{-1, "Alice", "Inwonder", 831168000, {} , 2}; +insertedId = storage.insert(secondUser); +secondUser.id = insertedId; + +``` + +Note: if we need to insert a new user with specified id call `storage.replace(user);` instead of `insert`. + +Next let's get our user by id. + +```c++ +try{ + auto user = storage.get(insertedId); + cout << "user = " << user.firstName << " " << user.lastName << endl; +}catch(std::system_error e) { + cout << e.what() << endl; +}catch(...){ + cout << "unknown exeption" << endl; +} +``` + +Probably you may not like throwing exceptions. Me too. Exception `std::system_error` is thrown because return type in `get` function is not nullable. You can use alternative version `get_pointer` which returns `std::unique_ptr` and doesn't throw `not_found_exception` if nothing found - just returns `nullptr`. + +```c++ +if(auto user = storage.get_pointer(insertedId)){ + cout << "user = " << user->firstName << " " << user->lastName << endl; +}else{ + cout << "no user with id " << insertedId << endl; +} +``` + +`std::unique_ptr` is used as optional in `sqlite_orm`. Of course there is class optional in C++14 located at `std::experimental::optional`. But we don't want to use it until it is `experimental`. + +We can also update our user. It updates row by id provided in `user` object and sets all other non `primary_key` fields to values stored in the passed `user` object. So you can just assign members to `user` object you want and call `update` + +```c++ +user.firstName = "Nicholas"; +user.imageUrl = "https://cdn1.iconfinder.com/data/icons/man-icon-set/100/man_icon-21-512.png" +storage.update(user); +``` + +Also there is a non-CRUD update version `update_all`: + +```c++ +storage.update_all(set(c(&User::lastName) = "Hardey", + c(&User::typeId) = 2), + where(c(&User::firstName) == "Tom")); +``` + +And delete. To delete you have to pass id only, not whole object. Also we need to explicitly tell which class of object we want to delete. Function name is `remove` not `delete` cause `delete` is a reserved word in C++. + +```c++ +storage.remove(insertedId) +``` + +Also we can extract all objects into `std::vector`. + +```c++ +auto allUsers = storage.get_all(); +cout << "allUsers (" << allUsers.size() << "):" << endl; +for(auto &user : allUsers) { + cout << storage.dump(user) << endl; // dump returns std::string with json-like style object info. For example: { id : '1', first_name : 'Jonh', last_name : 'Doe', birth_date : '664416000', image_url : 'https://cdn1.iconfinder.com/data/icons/man-icon-set/100/man_icon-21-512.png', type_id : '3' } +} +``` + +And one can specify return container type explicitly: let's get all users in `std::list`, not `std::vector`: + +```c++ +auto allUsersList = storage.get_all>(); +``` + +Container must be STL compatible (must have `push_back(T&&)` function in this case). + +`get_all` can be too heavy for memory so you can iterate row by row (i.e. object by object): + +```c++ +for(auto &user : storage.iterate()) { + cout << storage.dump(user) << endl; +} +``` + +`iterate` member function returns adapter object that has `begin` and `end` member functions returning iterators that fetch object on dereference operator call. + +CRUD functions `get`, `get_pointer`, `remove`, `update` (not `insert`) work only if your type has a primary key column. If you try to `get` an object that is mapped to your storage but has no primary key column a `std::system_error` will be thrown cause `sqlite_orm` cannot detect an id. If you want to know how to perform a storage without primary key take a look at `date_time.cpp` example in `examples` folder. + +# Prepared statements + +Prepared statements are strongly typed. + +```c++ +// SELECT doctor_id +// FROM visits +// WHERE LENGTH(patient_name) > 8 +auto selectStatement = storage.prepare(select(&Visit::doctor_id, where(length(&Visit::patient_name) > 8))); +cout << "selectStatement = " << selectStatement.sql() << endl; // prints "SELECT doctor_id FROM ..." +auto rows = storage.execute(selectStatement); // rows is std::vector + +// SELECT doctor_id +// FROM visits +// WHERE LENGTH(patient_name) > 11 +get<0>(selectStatement) = 11; +auto rows2 = storage.execute(selectStatement); +``` +`get(statement)` function call allows you to access fields to bind them to your statement. + +# Aggregate Functions + +```c++ +// SELECT AVG(id) FROM users +auto averageId = storage.avg(&User::id); +cout << "averageId = " << averageId << endl; // averageId = 4.5 + +// SELECT AVG(birth_date) FROM users +auto averageBirthDate = storage.avg(&User::birthDate); +cout << "averageBirthDate = " << averageBirthDate << endl; // averageBirthDate = 6.64416e+08 + +// SELECT COUNT(*) FROM users +auto usersCount = storage.count(); +cout << "users count = " << usersCount << endl; // users count = 8 + +// SELECT COUNT(id) FROM users +auto countId = storage.count(&User::id); +cout << "countId = " << countId << endl; // countId = 8 + +// SELECT COUNT(image_url) FROM users +auto countImageUrl = storage.count(&User::imageUrl); +cout << "countImageUrl = " << countImageUrl << endl; // countImageUrl = 5 + +// SELECT GROUP_CONCAT(id) FROM users +auto concatedUserId = storage.group_concat(&User::id); +cout << "concatedUserId = " << concatedUserId << endl; // concatedUserId = 1,2,3,4,5,6,7,8 + +// SELECT GROUP_CONCAT(id, "---") FROM users +auto concatedUserIdWithDashes = storage.group_concat(&User::id, "---"); +cout << "concatedUserIdWithDashes = " << concatedUserIdWithDashes << endl; // concatedUserIdWithDashes = 1---2---3---4---5---6---7---8 + +// SELECT MAX(id) FROM users +if(auto maxId = storage.max(&User::id)){ + cout << "maxId = " << *maxId <) +}else{ + cout << "maxId is null" << endl; +} + +// SELECT MAX(first_name) FROM users +if(auto maxFirstName = storage.max(&User::firstName)){ + cout << "maxFirstName = " << *maxFirstName << endl; // maxFirstName = Jonh (maxFirstName is std::unique_ptr) +}else{ + cout << "maxFirstName is null" << endl; +} + +// SELECT MIN(id) FROM users +if(auto minId = storage.min(&User::id)){ + cout << "minId = " << *minId << endl; // minId = 1 (minId is std::unique_ptr) +}else{ + cout << "minId is null" << endl; +} + +// SELECT MIN(last_name) FROM users +if(auto minLastName = storage.min(&User::lastName)){ + cout << "minLastName = " << *minLastName << endl; // minLastName = Doe +}else{ + cout << "minLastName is null" << endl; +} + +// SELECT SUM(id) FROM users +if(auto sumId = storage.sum(&User::id)){ // sumId is std::unique_ptr + cout << "sumId = " << *sumId << endl; +}else{ + cout << "sumId is null" << endl; +} + +// SELECT TOTAL(id) FROM users +auto totalId = storage.total(&User::id); +cout << "totalId = " << totalId << endl; // totalId is double (always) +``` + +# Where conditions + +You also can select objects with custom where conditions with `=`, `!=`, `>`, `>=`, `<`, `<=`, `IN`, `BETWEEN` and `LIKE`. + +For example: let's select users with id lesser than 10: + +```c++ +// SELECT * FROM users WHERE id < 10 +auto idLesserThan10 = storage.get_all(where(c(&User::id) < 10)); +cout << "idLesserThan10 count = " << idLesserThan10.size() << endl; +for(auto &user : idLesserThan10) { + cout << storage.dump(user) << endl; +} +``` + +Or select all users who's first name is not equal "John": + +```c++ +// SELECT * FROM users WHERE first_name != 'John' +auto notJohn = storage.get_all(where(c(&User::firstName) != "John")); +cout << "notJohn count = " << notJohn.size() << endl; +for(auto &user : notJohn) { + cout << storage.dump(user) << endl; +} +``` + +By the way one can implement not equal in a different way using C++ negation operator: + +```c++ +auto notJohn2 = storage.get_all(where(not (c(&User::firstName) == "John"))); +``` + +You can use `!` and `not` in this case cause they are equal. Also you can chain several conditions with `and` and `or` operators. Let's try to get users with query with conditions like `where id >= 5 and id <= 7 and not id = 6`: + +```c++ +auto id5and7 = storage.get_all(where(c(&User::id) <= 7 and c(&User::id) >= 5 and not (c(&User::id) == 6))); +cout << "id5and7 count = " << id5and7.size() << endl; +for(auto &user : id5and7) { + cout << storage.dump(user) << endl; +} +``` + +Or let's just export two users with id 10 or id 16 (of course if these users exist): + +```c++ +auto id10or16 = storage.get_all(where(c(&User::id) == 10 or c(&User::id) == 16)); +cout << "id10or16 count = " << id10or16.size() << endl; +for(auto &user : id10or16) { + cout << storage.dump(user) << endl; +} +``` + +In fact you can chain together any number of different conditions with any operator from `and`, `or` and `not`. All conditions are templated so there is no runtime overhead. And this makes `sqlite_orm` the most powerful **sqlite** C++ ORM library! + +Moreover you can use parentheses to set the priority of query conditions: + +```c++ +auto cuteConditions = storage.get_all(where((c(&User::firstName) == "John" or c(&User::firstName) == "Alex") and c(&User::id) == 4)); // where (first_name = 'John' or first_name = 'Alex') and id = 4 +cout << "cuteConditions count = " << cuteConditions.size() << endl; // cuteConditions count = 1 +cuteConditions = storage.get_all(where(c(&User::firstName) == "John" or (c(&User::firstName) == "Alex" and c(&User::id) == 4))); // where first_name = 'John' or (first_name = 'Alex' and id = 4) +cout << "cuteConditions count = " << cuteConditions.size() << endl; // cuteConditions count = 2 +``` + +Also we can implement `get` by id with `get_all` and `where` like this: + +```c++ +// SELECT * FROM users WHERE ( 2 = id ) +auto idEquals2 = storage.get_all(where(2 == c(&User::id))); +cout << "idEquals2 count = " << idEquals2.size() << endl; +if(idEquals2.size()){ + cout << storage.dump(idEquals2.front()) << endl; +}else{ + cout << "user with id 2 doesn't exist" << endl; +} +``` + +Lets try the `IN` operator: + +```c++ +// SELECT * FROM users WHERE id IN (2, 4, 6, 8, 10) +auto evenLesserTen10 = storage.get_all(where(in(&User::id, {2, 4, 6, 8, 10}))); +cout << "evenLesserTen10 count = " << evenLesserTen10.size() << endl; +for(auto &user : evenLesserTen10) { + cout << storage.dump(user) << endl; +} + +// SELECT * FROM users WHERE last_name IN ("Doe", "White") +auto doesAndWhites = storage.get_all(where(in(&User::lastName, {"Doe", "White"}))); +cout << "doesAndWhites count = " << doesAndWhites.size() << endl; +for(auto &user : doesAndWhites) { + cout << storage.dump(user) << endl; +} +``` + +And `BETWEEN`: + +```c++ +// SELECT * FROM users WHERE id BETWEEN 66 AND 68 +auto betweenId = storage.get_all(where(between(&User::id, 66, 68))); +cout << "betweenId = " << betweenId.size() << endl; +for(auto &user : betweenId) { + cout << storage.dump(user) << endl; +} +``` + +And even `LIKE`: + +```c++ +// SELECT * FROM users WHERE last_name LIKE 'D%' +auto whereNameLike = storage.get_all(where(like(&User::lastName, "D%"))); +cout << "whereNameLike = " << whereNameLike.size() << endl; +for(auto &user : whereNameLike) { + cout << storage.dump(user) << endl; +} +``` + +Looks like magic but it works very simple. Cute function `c` (column) takes a class member pointer and returns a special expression middle object that can be used with operators overloaded in `::sqlite_orm` namespace. Operator overloads act just like functions + +* is_equal +* is_not_equal +* greater_than +* greater_or_equal +* lesser_than +* lesser_or_equal +* is_null +* is_not_null + +that simulate binary comparison operator so they take 2 arguments: left hand side and right hand side. Arguments may be either member pointer of mapped class or any other expression (core/aggregate function, literal or subexpression). Binary comparison functions map arguments to text to be passed to sqlite engine to process query. Member pointers are being mapped to column names and literals/variables/constants to '?' and then are bound automatically. Next `where` function places brackets around condition and adds "WHERE" keyword before condition text. Next resulted string appends to a query string and is being processed further. + +If you omit `where` function in `get_all` it will return all objects from a table: + +```c++ +auto allUsers = storage.get_all(); +``` + +Also you can use `remove_all` function to perform `DELETE FROM ... WHERE` query with the same type of conditions. + +```c++ +storage.remove_all(where(c(&User::id) < 100)); +``` + +# Raw select + +If you need to extract only a single column (`SELECT %column_name% FROM %table_name% WHERE %conditions%`) you can use a non-CRUD `select` function: + +```c++ + +// SELECT id FROM users +auto allIds = storage.select(&User::id); +cout << "allIds count = " << allIds.size() << endl; // allIds is std::vector +for(auto &id : allIds) { + cout << id << " "; +} +cout << endl; + +// SELECT id FROM users WHERE last_name = 'Doe' +auto doeIds = storage.select(&User::id, where(c(&User::lastName) == "Doe")); +cout << "doeIds count = " << doeIds.size() << endl; // doeIds is std::vector +for(auto &doeId : doeIds) { + cout << doeId << " "; +} +cout << endl; + +// SELECT last_name FROM users WHERE id < 300 +auto allLastNames = storage.select(&User::lastName, where(c(&User::id) < 300)); +cout << "allLastNames count = " << allLastNames.size() << endl; // allLastNames is std::vector +for(auto &lastName : allLastNames) { + cout << lastName << " "; +} +cout << endl; + +// SELECT id FROM users WHERE image_url IS NULL +auto idsWithoutUrls = storage.select(&User::id, where(is_null(&User::imageUrl))); +for(auto id : idsWithoutUrls) { + cout << "id without image url " << id << endl; +} + +// SELECT id FROM users WHERE image_url IS NOT NULL +auto idsWithUrl = storage.select(&User::id, where(is_not_null(&User::imageUrl))); +for(auto id : idsWithUrl) { + cout << "id with image url " << id << endl; +} +auto idsWithUrl2 = storage.select(&User::id, where(not is_null(&User::imageUrl))); +assert(std::equal(idsWithUrl2.begin(), + idsWithUrl2.end(), + idsWithUrl.begin())); +``` + +Also you're able to select several column in a vector of tuples. Example: + +```c++ +// `SELECT first_name, last_name FROM users WHERE id > 250 ORDER BY id` +auto partialSelect = storage.select(columns(&User::firstName, &User::lastName), + where(c(&User::id) > 250), + order_by(&User::id)); +cout << "partialSelect count = " << partialSelect.size() << endl; +for(auto &t : partialSelect) { + auto &firstName = std::get<0>(t); + auto &lastName = std::get<1>(t); + cout << firstName << " " << lastName << endl; +} +``` + +# ORDER BY support + +ORDER BY query option can be applied to `get_all` and `select` functions just like `where` but with `order_by` function. It can be mixed with WHERE in a single query. Examples: + +```c++ +// `SELECT * FROM users ORDER BY id` +auto orderedUsers = storage.get_all(order_by(&User::id)); +cout << "orderedUsers count = " << orderedUsers.size() << endl; +for(auto &user : orderedUsers) { + cout << storage.dump(user) << endl; +} + +// `SELECT * FROM users WHERE id < 250 ORDER BY first_name` +auto orderedUsers2 = storage.get_all(where(c(&User::id) < 250), order_by(&User::firstName)); +cout << "orderedUsers2 count = " << orderedUsers2.size() << endl; +for(auto &user : orderedUsers2) { + cout << storage.dump(user) << endl; +} + +// `SELECT * FROM users WHERE id > 100 ORDER BY first_name ASC` +auto orderedUsers3 = storage.get_all(where(c(&User::id) > 100), order_by(&User::firstName).asc()); +cout << "orderedUsers3 count = " << orderedUsers3.size() << endl; +for(auto &user : orderedUsers3) { + cout << storage.dump(user) << endl; +} + +// `SELECT * FROM users ORDER BY id DESC` +auto orderedUsers4 = storage.get_all(order_by(&User::id).desc()); +cout << "orderedUsers4 count = " << orderedUsers4.size() << endl; +for(auto &user : orderedUsers4) { + cout << storage.dump(user) << endl; +} + +// `SELECT first_name FROM users ORDER BY ID DESC` +auto orderedFirstNames = storage.select(&User::firstName, order_by(&User::id).desc()); +cout << "orderedFirstNames count = " << orderedFirstNames.size() << endl; +for(auto &firstName : orderedFirstNames) { + cout << "firstName = " << firstName << endl; +} +``` + +# LIMIT and OFFSET + +There are three available versions of `LIMIT`/`OFFSET` options: + +- LIMIT %limit% +- LIMIT %limit% OFFSET %offset% +- LIMIT %offset%, %limit% + +All these versions available with the same interface: + +```c++ +// `SELECT * FROM users WHERE id > 250 ORDER BY id LIMIT 5` +auto limited5 = storage.get_all(where(c(&User::id) > 250), + order_by(&User::id), + limit(5)); +cout << "limited5 count = " << limited5.size() << endl; +for(auto &user : limited5) { + cout << storage.dump(user) << endl; +} + +// `SELECT * FROM users WHERE id > 250 ORDER BY id LIMIT 5, 10` +auto limited5comma10 = storage.get_all(where(c(&User::id) > 250), + order_by(&User::id), + limit(5, 10)); +cout << "limited5comma10 count = " << limited5comma10.size() << endl; +for(auto &user : limited5comma10) { + cout << storage.dump(user) << endl; +} + +// `SELECT * FROM users WHERE id > 250 ORDER BY id LIMIT 5 OFFSET 10` +auto limit5offset10 = storage.get_all(where(c(&User::id) > 250), + order_by(&User::id), + limit(5, offset(10))); +cout << "limit5offset10 count = " << limit5offset10.size() << endl; +for(auto &user : limit5offset10) { + cout << storage.dump(user) << endl; +} +``` + +Please beware that queries `LIMIT 5, 10` and `LIMIT 5 OFFSET 10` mean different. `LIMIT 5, 10` means `LIMIT 10 OFFSET 5`. + +# JOIN support + +You can perform simple `JOIN`, `CROSS JOIN`, `INNER JOIN`, `LEFT JOIN` or `LEFT OUTER JOIN` in your query. Instead of joined table specify mapped type. Example for doctors and visits: + +```c++ +// SELECT a.doctor_id, a.doctor_name, +// c.patient_name, c.vdate +// FROM doctors a +// LEFT JOIN visits c +// 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))); // one `c` call is enough cause operator overloads are templated +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; +} +cout << endl; +``` + +Simple `JOIN`: + +```c++ +// SELECT a.doctor_id,a.doctor_name, +// c.patient_name,c.vdate +// FROM doctors a +// JOIN visits c +// 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) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) << endl; +} +cout << endl; +``` + +Two `INNER JOIN`s in one query: + +```c++ +// SELECT +// trackid, +// tracks.name AS Track, +// albums.title AS Album, +// artists.name AS Artist +// FROM +// tracks +// INNER JOIN albums ON albums.albumid = tracks.albumid +// INNER JOIN artists ON artists.artistid = albums.artistid; +auto innerJoinRows2 = storage.select(columns(&Track::trackId, &Track::name, &Album::title, &Artist::name), + inner_join(on(c(&Album::albumId) == &Track::albumId)), + inner_join(on(c(&Artist::artistId) == &Album::artistId))); +// innerJoinRows2 is std::vector> +``` + +More join examples can be found in [examples folder](https://github.com/fnc12/sqlite_orm/blob/master/examples/left_and_inner_join.cpp). + +# Migrations functionality + +There are no explicit `up` and `down` functions that are used to be used in migrations. Instead `sqlite_orm` offers `sync_schema` function that takes responsibility of comparing actual db file schema with one you specified in `make_storage` call and if something is not equal it alters or drops/creates schema. + +```c++ +storage.sync_schema(); +// or +storage.sync_schema(true); +``` + +Please beware that `sync_schema` doesn't guarantee that data will be saved. It *tries* to save it only. Below you can see rules list that `sync_schema` follows during call: +* if there are excess tables exist in db they are ignored (not dropped) +* every table from storage is compared with it's db analog and + * if table doesn't exist it is created + * if table exists its colums are being compared with table_info from db and + * if there are columns in db that do not exist in storage (excess) table will be dropped and recreated if `preserve` is `false`, and table will be copied into temporary table without excess columns, source table will be dropped, copied table will be renamed to source table (sqlite remove column technique) if `preserve` is `true`. `preserve` is the first argument in `sync_schema` function. It's default value is `false`. Beware that setting it to `true` may take time for copying table rows. + * if there are columns in storage that do not exist in db they will be added using 'ALTER TABLE ... ADD COLUMN ...' command and table data will not be dropped but if any of added columns is null but has not default value table will be dropped and recreated + * if there is any column existing in both db and storage but differs by any of properties (type, pk, notnull) table will be dropped and recreated (dflt_value isn't checked cause there can be ambiguity in default values, please beware). + +The best practice is to call this function right after storage creation. + +# Transactions + +There are three ways to begin and commit/rollback transactions: +* explicitly call `begin_transaction();`, `rollback();` or `commit();` functions +* use `transaction` function which begins transaction implicitly and takes a lambda argument which returns true for commit and false for rollback. All storage calls performed in lambda can be commited or rollbacked by returning `true` or `false`. +* use `transaction_guard` function which returns a guard object which works just like `lock_guard` for `std::mutex`. + +Example for explicit call: + +```c++ +auto secondUser = storage.get(2); + +storage.begin_transaction(); +secondUser.typeId = 3; +storage.update(secondUser); +storage.rollback(); // or storage.commit(); + +secondUser = storage.get(secondUser.id); +assert(secondUser.typeId != 3); +``` + +Example for implicit call: + +```c++ +storage.transaction([&] () mutable { // mutable keyword allows make non-const function calls + auto secondUser = storage.get(2); + secondUser.typeId = 1; + storage.update(secondUser); + auto gottaRollback = bool(rand() % 2); + if(gottaRollback){ // dummy condition for test + return false; // exits lambda and calls ROLLBACK + } + return true; // exits lambda and calls COMMIT +}); +``` + +The second way guarantees that `commit` or `rollback` will be called. You can use either way. + +Trancations are useful with `changes` sqlite function that returns number of rows modified. + +```c++ +storage.transaction([&] () mutable { + storage.remove_all(where(c(&User::id) < 100)); + auto usersRemoved = storage.changes(); + cout << "usersRemoved = " << usersRemoved << endl; + return true; +}); +``` + +It will print a number of deleted users (rows). But if you call `changes` without a transaction and your database is located in file not in RAM the result will be 0 always cause `sqlite_orm` opens and closes connection every time you call a function without a transaction. + +Also a `transaction` function returns `true` if transaction is commited and `false` if it is rollbacked. It can be useful if your next actions depend on transaction result: + +```c++ +auto commited = storage.transaction([&] () mutable { + auto secondUser = storage.get(2); + secondUser.typeId = 1; + storage.update(secondUser); + auto gottaRollback = bool(rand() % 2); + if(gottaRollback){ // dummy condition for test + return false; // exits lambda and calls ROLLBACK + } + return true; // exits lambda and calls COMMIT +}); +if(commited){ + cout << "Commited successfully, go on." << endl; +}else{ + cerr << "Commit failed, process an error" << endl; +} +``` + +Example for `transaction_guard` function: + +```c++ +try{ + auto guard = storage.transaction_guard(); // calls BEGIN TRANSACTION and returns guard object + user.name = "Paul"; + auto notExisting = storage.get(-1); // exception is thrown here, guard calls ROLLBACK in its destructor + guard.commit(); +}catch(...){ + cerr << "exception" << endl; +} +``` + +# In memory database + +To manage in memory database just provide `:memory:` or `""` instead as filename to `make_storage`. + +# Comparison with other C++ libs + +| |sqlite_orm|[SQLiteCpp](https://github.com/SRombauts/SQLiteCpp)|[hiberlite](https://github.com/paulftw/hiberlite)|[ODB](https://www.codesynthesis.com/products/odb/)| +|---|:---:|:---:|:---:|:---:| +|Schema sync|yes|no|yes|no| +|Single responsibility principle|yes|yes|no|no| +|STL compatible|yes|no|no|no| +|No raw string queries|yes|no|yes|yes| +|Transactions|yes|yes|no|yes| +|Custom types binding|yes|no|yes|yes| +|Doesn't use macros and/or external codegen scripts|yes|yes|no|no| +|Aggregate functions|yes|yes|no|yes| +|Prepared statements|yes|yes|no|no| + +# Notes + +To work well your data model class must be default constructable and must not have const fields mapped to database cause they are assigned during queries. Otherwise code won't compile on line with member assignment operator. + +For more details please check the project [wiki](https://github.com/fnc12/sqlite_orm/wiki). + +# Installation + +**Note**: Installation is not necessary if you plan to use the fetchContent method, see below in Usage. + +Use a popular package manager like [vcpkg](https://github.com/Microsoft/vcpkg) and just install it with the `vcpkg install sqlite-orm` command. + +Or you build it from source: + +```bash +git clone https://github.com/fnc12/sqlite_orm.git sqlite_orm +cd sqlite_orm +cmake -B build +cmake --build build --target install +``` +You might need admin rights for the last command. + +# Usage + +## CMake + +If you use cmake, there are two supported ways how to use it with cmake (if another works as well or should be supported, open an issue). + +Either way you choose, the include path as well as the dependency sqlite3 will be set automatically on your target. So usage is straight forward, but you need to have installed sqlite3 on your system (see Requirements below) + +## Find Package + +If you have installed the lib system wide and it's in your PATH, you can use find_package to include it in cmake. It will make a target `sqlite_orm::sqlite_orm` available which you can link against. Have a look at examples/find_package for a full example. + +```cmake +find_package(SqliteOrm REQUIRED) + +target_link_libraries(main PRIVATE sqlite_orm::sqlite_orm) +``` + +## Fetch Content (Recommended) + +Alternatively, cmake can download the project directly from github during configure stage and therefore you don't need to install the lib before. +Againt a target `sqlite_orm::sqlite_orm` will be available which you can link against. Have a look at examples/fetch_content for a full example. + +## No CMake + +If you want to use the lib directly with Make or something else, just set the inlcude path correctly (should be correct on Linux already), so `sqlite_orm/sqlite_orm.h` is found. As this is a header only lib, there is nothing more you have to do. + +# Requirements + +* C++14 compatible compiler (not C++11 cause of templated lambdas in the lib). +* Sqlite3 installed on your system and in the path, so cmake can find it (or linked to you project if you don't use cmake) + +# Video from conference + +[![Video from conference](https://img.youtube.com/vi/ngsilquWgpo/0.jpg)](https://www.youtube.com/watch?v=ngsilquWgpo) + +# SqliteMan + +In case you need a native SQLite client for macOS or Windows 10 you can use SqliteMan https://sqliteman.dev. It is not a commercial. It is a free native client being developed by the maintainer of this repo. diff --git a/libs/sqlite_orm-1.8.2/TODO.md b/libs/sqlite_orm-1.8.2/TODO.md new file mode 100644 index 0000000..197a2ca --- /dev/null +++ b/libs/sqlite_orm-1.8.2/TODO.md @@ -0,0 +1,27 @@ +# To do list + +`sqlite_orm` is a wonderful library but there are still features that are not implemented. Here you can find a list of them: + +* `FOREIGN KEY` - sync_schema fk comparison and ability of two tables to have fk to each other (`PRAGMA foreign_key_list(%table_name%);` may be useful) +* rest of core functions(https://sqlite.org/lang_corefunc.html) +* `ATTACH` +* blob incremental I/O https://sqlite.org/c3ref/blob_open.html +* CREATE VIEW and other view operations https://sqlite.org/lang_createview.html +* query static check for correct order (e.g. `GROUP BY` after `WHERE`) +* `WINDOW` +* `SAVEPOINT` https://www.sqlite.org/lang_savepoint.html +* 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) +* 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)) +* strict tables https://sqlite.org/stricttables.html +* static assert when UPDATE is called with no PKs +* `json_each` and `json_tree` functions for JSON1 extension +* update hook +* `RAISE` + +Please feel free to add any feature that isn't listed here and not implemented yet. diff --git a/libs/sqlite_orm-1.8.2/_config.yml b/libs/sqlite_orm-1.8.2/_config.yml new file mode 100644 index 0000000..2f7efbe --- /dev/null +++ b/libs/sqlite_orm-1.8.2/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-minimal \ No newline at end of file diff --git a/libs/sqlite_orm-1.8.2/appveyor.yml b/libs/sqlite_orm-1.8.2/appveyor.yml new file mode 100644 index 0000000..55bed40 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/appveyor.yml @@ -0,0 +1,184 @@ +# build format +version: "{build}" + +skip_branch_with_pr: true +skip_commits: + files: + - .git* + - .travis.yml + - _config.yml + - LICENSE + - '*.md' + - '*.png' + - '*.sh' + +# configurations to add to build matrix +configuration: + #- Debug + - Release + +environment: + appveyor_yml_disable_ps_linux: true + matrix: + - job_name: clang, C++14 + appveyor_build_worker_image: Ubuntu + CC: clang + CXX: clang++ + SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_14=ON" + cmake_build_parallel: --parallel + + - job_name: gcc, C++14 + appveyor_build_worker_image: Ubuntu + CC: gcc + CXX: g++ + SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_14=ON" + # gcc was stuck with a parallel build + cmake_build_parallel: "" + + # Representative for C++14 + - job_name: Visual Studio 2017, x64, C++14 + appveyor_build_worker_image: Visual Studio 2017 + platform: x64 + SQLITE_ORM_CXX_STANDARD: "" + + - job_name: clang, C++17 + appveyor_build_worker_image: Ubuntu + CC: clang + CXX: clang++ + SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_17=ON" + cmake_build_parallel: --parallel + + - job_name: gcc, C++17 + appveyor_build_worker_image: Ubuntu + CC: gcc + CXX: g++ + SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_17=ON" + cmake_build_parallel: "" + + - job_name: clang, C++20 (with examples) + appveyor_build_worker_image: Ubuntu + CC: clang + CXX: clang++ + SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_20=ON" + # clang was stuck with a parallel build of examples + cmake_build_parallel: "" + cmake_build_examples: "-DBUILD_EXAMPLES=ON" + + - job_name: gcc, C++20 + appveyor_build_worker_image: Ubuntu + CC: gcc + CXX: g++ + SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_20=ON" + cmake_build_parallel: "" + + - job_name: Visual Studio 2022, x64, C++17 + appveyor_build_worker_image: Visual Studio 2022 + platform: x64 + SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_17=ON" + + - job_name: Visual Studio 2022, x64, C++20 + appveyor_build_worker_image: Visual Studio 2022 + platform: x64 + SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_20=ON" + + - job_name: Visual Studio 2022, x86, C++20 + appveyor_build_worker_image: Visual Studio 2022 + platform: x86 + SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_20=ON" + +matrix: + fast_finish: true + +for: +- + # Windows + matrix: + only: + - appveyor_build_worker_image: Visual Studio 2017 + - appveyor_build_worker_image: Visual Studio 2022 + init: + - |- + echo %appveyor_build_worker_image% - %platform% - %configuration% + cmake --version + if "%platform%"=="x64" (set architecture=-A x64) + if "%platform%"=="x86" (set architecture=-A Win32) + if "%appveyor_build_worker_image%"=="Visual Studio 2022" (set generator="Visual Studio 17 2022" %architecture%) + if "%appveyor_build_worker_image%"=="Visual Studio 2017" (set generator="Visual Studio 15 2017" %architecture%) + install: + - |- + cd C:\Tools\vcpkg + git fetch --tags && git checkout 2023.01.09 + cd %APPVEYOR_BUILD_FOLDER% + C:\Tools\vcpkg\bootstrap-vcpkg.bat -disableMetrics + C:\Tools\vcpkg\vcpkg integrate install + set VCPKG_DEFAULT_TRIPLET=%platform%-windows + vcpkg install sqlite3 + rem The Visual Studio 2017 build worker image comes with CMake 3.16 only, and sqlite_orm will build the Catch2 dependency from source + if not "%appveyor_build_worker_image%"=="Visual Studio 2017" (vcpkg install catch2) + before_build: + - |- + mkdir compile + cd compile + cmake %SQLITE_ORM_CXX_STANDARD% -G %generator% -DCMAKE_TOOLCHAIN_FILE=C:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake .. + # build examples, and run tests (ie make & make test) + build_script: + - |- + cmake --build . --config %configuration% -- /m + ctest --verbose --output-on-failure --build-config %configuration% + +- + # Linux + matrix: + only: + - appveyor_build_worker_image: Ubuntu + init: + - |- + echo $appveyor_build_worker_image + $CXX --version + cmake --version + # using custom vcpkg triplets for building and linking dynamic dependent libraries + install: + - |- + pushd $HOME/vcpkg + git fetch --tags && git checkout 2023.01.09 + popd + $HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics + $HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets + vcpkg install sqlite3 catch2 --overlay-triplets=vcpkg/triplets + before_build: + - |- + mkdir compile + cd compile + cmake $SQLITE_ORM_CXX_STANDARD $cmake_build_examples --toolchain $HOME/vcpkg/scripts/buildsystems/vcpkg.cmake .. + # build examples, and run tests (ie make & make test) + build_script: + - |- + cmake --build . $cmake_build_parallel + ctest --verbose --output-on-failure +- + # macOS + matrix: + only: + - appveyor_build_worker_image: macOS + init: + - |- + echo $appveyor_build_worker_image + $CXX --version + cmake --version + # using custom vcpkg triplets for building and linking dynamic dependent libraries + install: + - |- + git clone --depth 1 --branch 2023.01.09 https://github.com/microsoft/vcpkg.git $HOME/vcpkg + $HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics + $HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets + vcpkg install sqlite3 catch2 --overlay-triplets=vcpkg/triplets + before_build: + - |- + mkdir compile + cd compile + cmake $SQLITE_ORM_CXX_STANDARD --toolchain $HOME/vcpkg/scripts/buildsystems/vcpkg.cmake .. + # build examples, and run tests (ie make & make test) + build_script: + - |- + cmake --build . --parallel + ctest --verbose --output-on-failure diff --git a/libs/sqlite_orm-1.8.2/cmake/SqliteOrmConfig.cmake.in b/libs/sqlite_orm-1.8.2/cmake/SqliteOrmConfig.cmake.in new file mode 100644 index 0000000..e0635d2 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/cmake/SqliteOrmConfig.cmake.in @@ -0,0 +1,4 @@ +include(CMakeFindDependencyMacro) +find_dependency(SQLite3) + +include(${CMAKE_CURRENT_LIST_DIR}/SqliteOrmTargets.cmake) diff --git a/libs/sqlite_orm-1.8.2/cmake/ucm.cmake b/libs/sqlite_orm-1.8.2/cmake/ucm.cmake new file mode 100755 index 0000000..4947070 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/cmake/ucm.cmake @@ -0,0 +1,663 @@ +# +# ucm.cmake - useful cmake macros +# +# Copyright (c) 2016 Viktor Kirilov +# +# Distributed under the MIT Software License +# See accompanying file LICENSE.txt or copy at +# https://opensource.org/licenses/MIT +# +# The documentation can be found at the library's page: +# https://github.com/onqtam/ucm + +cmake_minimum_required(VERSION 2.8.12) + +include(CMakeParseArguments) + +# optionally include cotire - the git submodule might not be inited (or the user might have already included it) +if(NOT COMMAND cotire) + include(${CMAKE_CURRENT_LIST_DIR}/../cotire/CMake/cotire.cmake OPTIONAL) +endif() + +if(COMMAND cotire AND "1.7.9" VERSION_LESS "${COTIRE_CMAKE_MODULE_VERSION}") + set(ucm_with_cotire 1) +else() + set(ucm_with_cotire 0) +endif() + +option(UCM_UNITY_BUILD "Enable unity build for targets registered with the ucm_add_target() macro" OFF) +option(UCM_NO_COTIRE_FOLDER "Do not use a cotire folder in the solution explorer for all unity and cotire related targets" ON) + +# ucm_add_flags +# Adds compiler flags to CMAKE__FLAGS or to a specific config +macro(ucm_add_flags) + cmake_parse_arguments(ARG "C;CXX;CLEAR_OLD" "" "CONFIG" ${ARGN}) + + if(NOT ARG_CONFIG) + set(ARG_CONFIG " ") + endif() + + foreach(CONFIG ${ARG_CONFIG}) + # determine to which flags to add + if(NOT ${CONFIG} STREQUAL " ") + string(TOUPPER ${CONFIG} CONFIG) + set(CXX_FLAGS CMAKE_CXX_FLAGS_${CONFIG}) + set(C_FLAGS CMAKE_C_FLAGS_${CONFIG}) + else() + set(CXX_FLAGS CMAKE_CXX_FLAGS) + set(C_FLAGS CMAKE_C_FLAGS) + endif() + + # clear the old flags + if(${ARG_CLEAR_OLD}) + if("${ARG_CXX}" OR NOT "${ARG_C}") + set(${CXX_FLAGS} "") + endif() + if("${ARG_C}" OR NOT "${ARG_CXX}") + set(${C_FLAGS} "") + endif() + endif() + + # add all the passed flags + foreach(flag ${ARG_UNPARSED_ARGUMENTS}) + if("${ARG_CXX}" OR NOT "${ARG_C}") + set(${CXX_FLAGS} "${${CXX_FLAGS}} ${flag}") + endif() + if("${ARG_C}" OR NOT "${ARG_CXX}") + set(${C_FLAGS} "${${C_FLAGS}} ${flag}") + endif() + endforeach() + endforeach() + +endmacro() + +# ucm_set_flags +# Sets the CMAKE__FLAGS compiler flags or for a specific config +macro(ucm_set_flags) + ucm_add_flags(CLEAR_OLD ${ARGN}) +endmacro() + +# ucm_add_linker_flags +# Adds linker flags to CMAKE__LINKER_FLAGS or to a specific config +macro(ucm_add_linker_flags) + cmake_parse_arguments(ARG "CLEAR_OLD;EXE;MODULE;SHARED;STATIC" "" "CONFIG" ${ARGN}) + + if(NOT ARG_CONFIG) + set(ARG_CONFIG " ") + endif() + + foreach(CONFIG ${ARG_CONFIG}) + string(TOUPPER "${CONFIG}" CONFIG) + + if(NOT ${ARG_EXE} AND NOT ${ARG_MODULE} AND NOT ${ARG_SHARED} AND NOT ${ARG_STATIC}) + set(ARG_EXE 1) + set(ARG_MODULE 1) + set(ARG_SHARED 1) + set(ARG_STATIC 1) + endif() + + set(flags_configs "") + if(${ARG_EXE}) + if(NOT "${CONFIG}" STREQUAL " ") + list(APPEND flags_configs CMAKE_EXE_LINKER_FLAGS_${CONFIG}) + else() + list(APPEND flags_configs CMAKE_EXE_LINKER_FLAGS) + endif() + endif() + if(${ARG_MODULE}) + if(NOT "${CONFIG}" STREQUAL " ") + list(APPEND flags_configs CMAKE_MODULE_LINKER_FLAGS_${CONFIG}) + else() + list(APPEND flags_configs CMAKE_MODULE_LINKER_FLAGS) + endif() + endif() + if(${ARG_SHARED}) + if(NOT "${CONFIG}" STREQUAL " ") + list(APPEND flags_configs CMAKE_SHARED_LINKER_FLAGS_${CONFIG}) + else() + list(APPEND flags_configs CMAKE_SHARED_LINKER_FLAGS) + endif() + endif() + if(${ARG_STATIC}) + if(NOT "${CONFIG}" STREQUAL " ") + list(APPEND flags_configs CMAKE_STATIC_LINKER_FLAGS_${CONFIG}) + else() + list(APPEND flags_configs CMAKE_STATIC_LINKER_FLAGS) + endif() + endif() + + # clear the old flags + if(${ARG_CLEAR_OLD}) + foreach(flags ${flags_configs}) + set(${flags} "") + endforeach() + endif() + + # add all the passed flags + foreach(flag ${ARG_UNPARSED_ARGUMENTS}) + foreach(flags ${flags_configs}) + set(${flags} "${${flags}} ${flag}") + endforeach() + endforeach() + endforeach() +endmacro() + +# ucm_set_linker_flags +# Sets the CMAKE__LINKER_FLAGS linker flags or for a specific config +macro(ucm_set_linker_flags) + ucm_add_linker_flags(CLEAR_OLD ${ARGN}) +endmacro() + +# ucm_gather_flags +# Gathers all lists of flags for printing or manipulation +macro(ucm_gather_flags with_linker result) + set(${result} "") + # add the main flags without a config + list(APPEND ${result} CMAKE_C_FLAGS) + list(APPEND ${result} CMAKE_CXX_FLAGS) + if(${with_linker}) + list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS) + list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS) + list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS) + list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS) + endif() + + if("${CMAKE_CONFIGURATION_TYPES}" STREQUAL "" AND NOT "${CMAKE_BUILD_TYPE}" STREQUAL "") + # handle single config generators - like makefiles/ninja - when CMAKE_BUILD_TYPE is set + string(TOUPPER ${CMAKE_BUILD_TYPE} config) + list(APPEND ${result} CMAKE_C_FLAGS_${config}) + list(APPEND ${result} CMAKE_CXX_FLAGS_${config}) + if(${with_linker}) + list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS_${config}) + list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS_${config}) + list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS_${config}) + list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS_${config}) + endif() + else() + # handle multi config generators (like msvc, xcode) + foreach(config ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${config} config) + list(APPEND ${result} CMAKE_C_FLAGS_${config}) + list(APPEND ${result} CMAKE_CXX_FLAGS_${config}) + if(${with_linker}) + list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS_${config}) + list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS_${config}) + list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS_${config}) + list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS_${config}) + endif() + endforeach() + endif() +endmacro() + +# ucm_set_runtime +# Sets the runtime (static/dynamic) for msvc/gcc +macro(ucm_set_runtime) + cmake_parse_arguments(ARG "STATIC;DYNAMIC" "" "" ${ARGN}) + + if(ARG_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "unrecognized arguments: ${ARG_UNPARSED_ARGUMENTS}") + endif() + + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" STREQUAL "") + message(AUTHOR_WARNING "ucm_set_runtime() does not support clang yet!") + endif() + + ucm_gather_flags(0 flags_configs) + + # add/replace the flags + # note that if the user has messed with the flags directly this function might fail + # - for example if with MSVC and the user has removed the flags - here we just switch/replace them + if("${ARG_STATIC}") + foreach(flags ${flags_configs}) + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + if(NOT ${flags} MATCHES "-static-libstdc\\+\\+") + set(${flags} "${${flags}} -static-libstdc++") + endif() + if(NOT ${flags} MATCHES "-static-libgcc") + set(${flags} "${${flags}} -static-libgcc") + endif() + elseif(MSVC) + if(${flags} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flags} "${${flags}}") + endif() + endif() + endforeach() + elseif("${ARG_DYNAMIC}") + foreach(flags ${flags_configs}) + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + if(${flags} MATCHES "-static-libstdc\\+\\+") + string(REGEX REPLACE "-static-libstdc\\+\\+" "" ${flags} "${${flags}}") + endif() + if(${flags} MATCHES "-static-libgcc") + string(REGEX REPLACE "-static-libgcc" "" ${flags} "${${flags}}") + endif() + elseif(MSVC) + if(${flags} MATCHES "/MT") + string(REGEX REPLACE "/MT" "/MD" ${flags} "${${flags}}") + endif() + endif() + endforeach() + endif() +endmacro() + +# ucm_print_flags +# Prints all compiler flags for all configurations +macro(ucm_print_flags) + ucm_gather_flags(1 flags_configs) + message("") + foreach(flags ${flags_configs}) + message("${flags}: ${${flags}}") + endforeach() + message("") +endmacro() + +# ucm_set_xcode_attrib +# Set xcode attributes - name value CONFIG config1 conifg2.. +macro(ucm_set_xcode_attrib) + cmake_parse_arguments(ARG "" "CLEAR" "CONFIG" ${ARGN}) + + if(NOT ARG_CONFIG) + set(ARG_CONFIG " ") + endif() + + foreach(CONFIG ${ARG_CONFIG}) + # determine to which attributes to add + if(${CONFIG} STREQUAL " ") + if(${ARG_CLEAR}) + # clear the old flags + unset(CMAKE_XCODE_ATTRIBUTE_${ARGV0}) + else() + set(CMAKE_XCODE_ATTRIBUTE_${ARGV0} ${ARGV1}) + endif() + else() + if(${ARG_CLEAR}) + # clear the old flags + unset(CMAKE_XCODE_ATTRIBUTE_${ARGV0}[variant=${CONFIG}]) + else() + set(CMAKE_XCODE_ATTRIBUTE_${ARGV0}[variant=${CONFIG}] ${ARGV1}) + endif() + endif() + endforeach() +endmacro() + +# ucm_count_sources +# Counts the number of source files +macro(ucm_count_sources) + cmake_parse_arguments(ARG "" "RESULT" "" ${ARGN}) + if(${ARG_RESULT} STREQUAL "") + message(FATAL_ERROR "Need to pass RESULT and a variable name to ucm_count_sources()") + endif() + + set(result 0) + foreach(SOURCE_FILE ${ARG_UNPARSED_ARGUMENTS}) + if("${SOURCE_FILE}" MATCHES \\.\(c|C|cc|cp|cpp|CPP|c\\+\\+|cxx|i|ii\)$) + math(EXPR result "${result} + 1") + endif() + endforeach() + set(${ARG_RESULT} ${result}) +endmacro() + +# ucm_include_file_in_sources +# Includes the file to the source with compiler flags +macro(ucm_include_file_in_sources) + cmake_parse_arguments(ARG "" "HEADER" "" ${ARGN}) + if(${ARG_HEADER} STREQUAL "") + message(FATAL_ERROR "Need to pass HEADER and a header file to ucm_include_file_in_sources()") + endif() + + foreach(src ${ARG_UNPARSED_ARGUMENTS}) + if(${src} MATCHES \\.\(c|C|cc|cp|cpp|CPP|c\\+\\+|cxx\)$) + # get old flags + get_source_file_property(old_compile_flags ${src} COMPILE_FLAGS) + if(old_compile_flags STREQUAL "NOTFOUND") + set(old_compile_flags "") + endif() + + # update flags + if(MSVC) + set_source_files_properties(${src} PROPERTIES COMPILE_FLAGS + "${old_compile_flags} /FI\"${CMAKE_CURRENT_SOURCE_DIR}/${ARG_HEADER}\"") + else() + set_source_files_properties(${src} PROPERTIES COMPILE_FLAGS + "${old_compile_flags} -include \"${CMAKE_CURRENT_SOURCE_DIR}/${ARG_HEADER}\"") + endif() + endif() + endforeach() +endmacro() + +# ucm_dir_list +# Returns a list of subdirectories for a given directory +macro(ucm_dir_list thedir result) + file(GLOB sub-dir "${thedir}/*") + set(list_of_dirs "") + foreach(dir ${sub-dir}) + if(IS_DIRECTORY ${dir}) + get_filename_component(DIRNAME ${dir} NAME) + LIST(APPEND list_of_dirs ${DIRNAME}) + endif() + endforeach() + set(${result} ${list_of_dirs}) +endmacro() + +# ucm_trim_front_words +# Trims X times the front word from a string separated with "/" and removes +# the front "/" characters after that (used for filters for visual studio) +macro(ucm_trim_front_words source out num_filter_trims) + set(result "${source}") + set(counter 0) + while(${counter} LESS ${num_filter_trims}) + MATH(EXPR counter "${counter} + 1") + # removes everything at the front up to a "/" character + string(REGEX REPLACE "^([^/]+)" "" result "${result}") + # removes all consecutive "/" characters from the front + string(REGEX REPLACE "^(/+)" "" result "${result}") + endwhile() + set(${out} ${result}) +endmacro() + +# ucm_remove_files +# Removes source files from a list of sources (path is the relative path for it to be found) +macro(ucm_remove_files) + cmake_parse_arguments(ARG "" "FROM" "" ${ARGN}) + + if("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "") + message(FATAL_ERROR "Need to pass some relative files to ucm_remove_files()") + endif() + if(${ARG_FROM} STREQUAL "") + message(FATAL_ERROR "Need to pass FROM and a variable name to ucm_remove_files()") + endif() + + foreach(cur_file ${ARG_UNPARSED_ARGUMENTS}) + list(REMOVE_ITEM ${ARG_FROM} ${cur_file}) + endforeach() +endmacro() + +# ucm_remove_directories +# Removes all source files from the given directories from the sources list +macro(ucm_remove_directories) + cmake_parse_arguments(ARG "" "FROM" "MATCHES" ${ARGN}) + + if("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "") + message(FATAL_ERROR "Need to pass some relative directories to ucm_remove_directories()") + endif() + if(${ARG_FROM} STREQUAL "") + message(FATAL_ERROR "Need to pass FROM and a variable name to ucm_remove_directories()") + endif() + + foreach(cur_dir ${ARG_UNPARSED_ARGUMENTS}) + foreach(cur_file ${${ARG_FROM}}) + string(REGEX MATCH ${cur_dir} res ${cur_file}) + if(NOT "${res}" STREQUAL "") + if("${ARG_MATCHES}" STREQUAL "") + list(REMOVE_ITEM ${ARG_FROM} ${cur_file}) + else() + foreach(curr_ptrn ${ARG_MATCHES}) + string(REGEX MATCH ${curr_ptrn} res ${cur_file}) + if(NOT "${res}" STREQUAL "") + list(REMOVE_ITEM ${ARG_FROM} ${cur_file}) + break() + endif() + endforeach() + endif() + endif() + endforeach() + endforeach() +endmacro() + +# ucm_add_files_impl +macro(ucm_add_files_impl result trim files) + foreach(cur_file ${files}) + SET(${result} ${${result}} ${cur_file}) + get_filename_component(FILEPATH ${cur_file} PATH) + ucm_trim_front_words("${FILEPATH}" FILEPATH "${trim}") + # replacing forward slashes with back slashes so filters can be generated (back slash used in parsing...) + STRING(REPLACE "/" "\\" FILTERS "${FILEPATH}") + SOURCE_GROUP("${FILTERS}" FILES ${cur_file}) + endforeach() +endmacro() + +# ucm_add_files +# Adds files to a list of sources +macro(ucm_add_files) + cmake_parse_arguments(ARG "" "TO;FILTER_POP" "" ${ARGN}) + + if("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "") + message(FATAL_ERROR "Need to pass some relative files to ucm_add_files()") + endif() + if(${ARG_TO} STREQUAL "") + message(FATAL_ERROR "Need to pass TO and a variable name to ucm_add_files()") + endif() + + if("${ARG_FILTER_POP}" STREQUAL "") + set(ARG_FILTER_POP 0) + endif() + + ucm_add_files_impl(${ARG_TO} ${ARG_FILTER_POP} "${ARG_UNPARSED_ARGUMENTS}") +endmacro() + +# ucm_add_dir_impl +macro(ucm_add_dir_impl result rec trim dirs_in additional_ext) + set(dirs "${dirs_in}") + + # handle the "" and "." cases + if("${dirs}" STREQUAL "" OR "${dirs}" STREQUAL ".") + set(dirs "./") + endif() + + foreach(cur_dir ${dirs}) + # to circumvent some linux/cmake/path issues - barely made it work... + if(cur_dir STREQUAL "./") + set(cur_dir "") + else() + set(cur_dir "${cur_dir}/") + endif() + + # since unix is case sensitive - add these valid extensions too + # we don't use "UNIX" but instead "CMAKE_HOST_UNIX" because we might be cross + # compiling (for example emscripten) under windows and UNIX may be set to 1 + # Also OSX is case insensitive like windows... + set(additional_file_extensions "") + if(CMAKE_HOST_UNIX AND NOT APPLE) + set(additional_file_extensions + "${cur_dir}*.CPP" + "${cur_dir}*.C" + "${cur_dir}*.H" + "${cur_dir}*.HPP" + ) + endif() + + foreach(ext ${additional_ext}) + list(APPEND additional_file_extensions "${cur_dir}*.${ext}") + endforeach() + + # find all sources and set them as result + FILE(GLOB found_sources RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + # https://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Overall-Options.html#index-file-name-suffix-71 + # sources + "${cur_dir}*.cpp" + "${cur_dir}*.cxx" + "${cur_dir}*.c++" + "${cur_dir}*.cc" + "${cur_dir}*.cp" + "${cur_dir}*.c" + "${cur_dir}*.i" + "${cur_dir}*.ii" + # headers + "${cur_dir}*.h" + "${cur_dir}*.h++" + "${cur_dir}*.hpp" + "${cur_dir}*.hxx" + "${cur_dir}*.hh" + "${cur_dir}*.inl" + "${cur_dir}*.inc" + "${cur_dir}*.ipp" + "${cur_dir}*.ixx" + "${cur_dir}*.txx" + "${cur_dir}*.tpp" + "${cur_dir}*.tcc" + "${cur_dir}*.tpl" + ${additional_file_extensions}) + SET(${result} ${${result}} ${found_sources}) + + # set the proper filters + ucm_trim_front_words("${cur_dir}" cur_dir "${trim}") + # replacing forward slashes with back slashes so filters can be generated (back slash used in parsing...) + STRING(REPLACE "/" "\\" FILTERS "${cur_dir}") + SOURCE_GROUP("${FILTERS}" FILES ${found_sources}) + endforeach() + + if(${rec}) + foreach(cur_dir ${dirs}) + ucm_dir_list("${cur_dir}" subdirs) + foreach(subdir ${subdirs}) + ucm_add_dir_impl(${result} ${rec} ${trim} "${cur_dir}/${subdir}" "${additional_ext}") + endforeach() + endforeach() + endif() +endmacro() + +# ucm_add_dirs +# Adds all files from directories traversing them recursively to a list of sources +# and generates filters according to their location (accepts relative paths only). +# Also this macro trims X times the front word from the filter string for visual studio filters. +macro(ucm_add_dirs) + cmake_parse_arguments(ARG "RECURSIVE" "TO;FILTER_POP" "ADDITIONAL_EXT" ${ARGN}) + + if(${ARG_TO} STREQUAL "") + message(FATAL_ERROR "Need to pass TO and a variable name to ucm_add_dirs()") + endif() + + if("${ARG_FILTER_POP}" STREQUAL "") + set(ARG_FILTER_POP 0) + endif() + + ucm_add_dir_impl(${ARG_TO} ${ARG_RECURSIVE} ${ARG_FILTER_POP} "${ARG_UNPARSED_ARGUMENTS}" "${ARG_ADDITIONAL_EXT}") +endmacro() + +# ucm_add_target +# Adds a target eligible for cotiring - unity build and/or precompiled header +macro(ucm_add_target) + cmake_parse_arguments(ARG "UNITY" "NAME;TYPE;PCH_FILE;CPP_PER_UNITY" "UNITY_EXCLUDED;SOURCES" ${ARGN}) + + if(NOT "${ARG_UNPARSED_ARGUMENTS}" STREQUAL "") + message(FATAL_ERROR "Unrecognized options passed to ucm_add_target()") + endif() + if("${ARG_NAME}" STREQUAL "") + message(FATAL_ERROR "Need to pass NAME and a name for the target to ucm_add_target()") + endif() + set(valid_types EXECUTABLE STATIC SHARED MODULE) + list(FIND valid_types "${ARG_TYPE}" is_type_valid) + if(${is_type_valid} STREQUAL "-1") + message(FATAL_ERROR "Need to pass TYPE and the type for the target [EXECUTABLE/STATIC/SHARED/MODULE] to ucm_add_target()") + endif() + if("${ARG_SOURCES}" STREQUAL "") + message(FATAL_ERROR "Need to pass SOURCES and a list of source files to ucm_add_target()") + endif() + + # init with the global unity flag + set(do_unity ${UCM_UNITY_BUILD}) + + # check the UNITY argument + if(NOT ARG_UNITY) + set(do_unity FALSE) + endif() + + # if target is excluded through the exclusion list + list(FIND UCM_UNITY_BUILD_EXCLUDE_TARGETS ${ARG_NAME} is_target_excluded) + if(NOT ${is_target_excluded} STREQUAL "-1") + set(do_unity FALSE) + endif() + + # unity build only for targets with > 1 source file (otherwise there will be an additional unnecessary target) + if(do_unity) # optimization + ucm_count_sources(${ARG_SOURCES} RESULT num_sources) + if(${num_sources} LESS 2) + set(do_unity FALSE) + endif() + endif() + + set(wanted_cotire ${do_unity}) + + # if cotire cannot be used + if(do_unity AND NOT ucm_with_cotire) + set(do_unity FALSE) + endif() + + # inform the developer that the current target might benefit from a unity build + if(NOT ARG_UNITY AND ${UCM_UNITY_BUILD}) + ucm_count_sources(${ARG_SOURCES} RESULT num_sources) + if(${num_sources} GREATER 1) + message(AUTHOR_WARNING "Target '${ARG_NAME}' may benefit from a unity build.\nIt has ${num_sources} sources - enable with UNITY flag") + endif() + endif() + + # prepare for the unity build + set(orig_target ${ARG_NAME}) + if(do_unity) + # the original target will be added with a different name than the requested + set(orig_target ${ARG_NAME}_ORIGINAL) + + # exclude requested files from unity build of the current target + foreach(excluded_file "${ARG_UNITY_EXCLUDED}") + set_source_files_properties(${excluded_file} PROPERTIES COTIRE_EXCLUDED TRUE) + endforeach() + endif() + + # add the original target + if(${ARG_TYPE} STREQUAL "EXECUTABLE") + add_executable(${orig_target} ${ARG_SOURCES}) + else() + add_library(${orig_target} ${ARG_TYPE} ${ARG_SOURCES}) + endif() + + if(do_unity) + # set the number of unity cpp files to be used for the unity target + if(NOT "${ARG_CPP_PER_UNITY}" STREQUAL "") + set_property(TARGET ${orig_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "${ARG_CPP_PER_UNITY}") + else() + set_property(TARGET ${orig_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "100") + endif() + + if(NOT "${ARG_PCH_FILE}" STREQUAL "") + set_target_properties(${orig_target} PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "${ARG_PCH_FILE}") + else() + set_target_properties(${orig_target} PROPERTIES COTIRE_ENABLE_PRECOMPILED_HEADER FALSE) + endif() + # add a unity target for the original one with the name intended for the original + set_target_properties(${orig_target} PROPERTIES COTIRE_UNITY_TARGET_NAME ${ARG_NAME}) + + # this is the library call that does the magic + cotire(${orig_target}) + set_target_properties(clean_cotire PROPERTIES FOLDER "CMakePredefinedTargets") + + # disable the original target and enable the unity one + get_target_property(unity_target_name ${orig_target} COTIRE_UNITY_TARGET_NAME) + set_target_properties(${orig_target} PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1) + set_target_properties(${unity_target_name} PROPERTIES EXCLUDE_FROM_ALL 0 EXCLUDE_FROM_DEFAULT_BUILD 0) + + # also set the name of the target output as the original one + set_target_properties(${unity_target_name} PROPERTIES OUTPUT_NAME ${ARG_NAME}) + if(UCM_NO_COTIRE_FOLDER) + # reset the folder property so all unity targets dont end up in a single folder in the solution explorer of VS + set_target_properties(${unity_target_name} PROPERTIES FOLDER "") + endif() + set_target_properties(all_unity PROPERTIES FOLDER "CMakePredefinedTargets") + elseif(NOT "${ARG_PCH_FILE}" STREQUAL "") + set(wanted_cotire TRUE) + if(ucm_with_cotire) + set_target_properties(${orig_target} PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE) + set_target_properties(${orig_target} PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "${ARG_PCH_FILE}") + cotire(${orig_target}) + set_target_properties(clean_cotire PROPERTIES FOLDER "CMakePredefinedTargets") + endif() + endif() + + # print a message if the target was requested to be cotired but it couldn't + if(wanted_cotire AND NOT ucm_with_cotire) + if(NOT COMMAND cotire) + message(AUTHOR_WARNING "Target \"${ARG_NAME}\" not cotired because cotire isn't loaded") + else() + message(AUTHOR_WARNING "Target \"${ARG_NAME}\" not cotired because cotire is older than the required version") + endif() + endif() +endmacro() diff --git a/libs/sqlite_orm-1.8.2/dependencies/CMakeLists.txt b/libs/sqlite_orm-1.8.2/dependencies/CMakeLists.txt new file mode 100644 index 0000000..2822439 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dependencies/CMakeLists.txt @@ -0,0 +1,28 @@ +include(FetchContent) + +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) + # FIND_PACKAGE_ARGS is available since 3.24 + if(CMAKE_VERSION VERSION_LESS 3.24) + FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.2.1 + ) + else() + FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.2.1 + # prefer find_package() over building from source + FIND_PACKAGE_ARGS 3 CONFIG + ) + endif() +endif() + +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) + # CMake <3.24: exposes targets only locally, but caches them. So call FetchContent_MakeAvailable again in the directory of usage + FetchContent_MakeAvailable(Catch2) +endif() + +# CMake <3.24: exposes targets only locally, but caches them. So call find_package again in the directory of usage +find_package(SQLite3 REQUIRED) diff --git a/libs/sqlite_orm-1.8.2/dev/alias.h b/libs/sqlite_orm-1.8.2/dev/alias.h new file mode 100644 index 0000000..eb5aad0 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/alias.h @@ -0,0 +1,196 @@ +#pragma once + +#include // std::enable_if, std::is_base_of, std::is_member_pointer, std::remove_const +#include // std::index_sequence, std::make_index_sequence +#include // std::string +#include // std::stringstream +#include // std::copy_n + +#include "functional/cxx_universal.h" // ::size_t +#include "functional/cxx_type_traits_polyfill.h" +#include "type_traits.h" +#include "alias_traits.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * This is a common built-in class used for character based table aliases. + * For convenience there exist public type aliases `alias_a`, `alias_b`, ... + * The easiest way to create a table alias is using `"z"_alias.for_()`. + */ + template + struct recordset_alias : alias_tag { + using type = T; + + static std::string get() { + return {A, X...}; + } + }; + + /** + * 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; + }; + + /* + * Encapsulates extracting the alias identifier of a non-alias. + */ + template + struct alias_extractor { + static std::string extract() { + return {}; + } + + static std::string as_alias() { + return {}; + } + }; + + /* + * Encapsulates extracting the alias identifier of an alias. + * + * `extract()` always returns the alias identifier. + * `as_alias()` is used in contexts where a table is aliased. + */ + template + struct alias_extractor> { + static std::string extract() { + std::stringstream ss; + ss << A::get(); + return ss.str(); + } + + // for column and regular table aliases -> alias identifier + template, A> = true> + static std::string as_alias() { + return alias_extractor::extract(); + } + }; + + /** + * Used to store alias for expression + */ + template + struct as_t { + using alias_type = T; + using expression_type = E; + + expression_type expression; + }; + + /** + * This is a common built-in class used for custom single-character column aliases. + * For convenience there exist type aliases `colalias_a`, `colalias_b`, ... + * The easiest way to create a column alias is using `"xyz"_col`. + */ + template + struct column_alias : alias_tag { + static std::string get() { + return {A, X...}; + } + }; + + template + struct alias_holder { + using type = T; + + alias_holder() = default; + }; + } + + /** + * @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'. + */ + template, bool> = true> + internal::alias_column_t alias_column(C c) { + using aliased_type = internal::type_t; + static_assert(std::is_same, aliased_type>::value, + "Column must be from aliased table"); + return {c}; + } + + /** + * Alias a column expression. + */ + template = true> + internal::as_t as(E expression) { + return {std::move(expression)}; + } + + template = true> + internal::alias_holder get() { + return {}; + } + + template + using alias_a = internal::recordset_alias; + template + using alias_b = internal::recordset_alias; + template + using alias_c = internal::recordset_alias; + template + using alias_d = internal::recordset_alias; + template + using alias_e = internal::recordset_alias; + template + using alias_f = internal::recordset_alias; + template + using alias_g = internal::recordset_alias; + template + using alias_h = internal::recordset_alias; + template + using alias_i = internal::recordset_alias; + template + using alias_j = internal::recordset_alias; + template + using alias_k = internal::recordset_alias; + template + using alias_l = internal::recordset_alias; + template + using alias_m = internal::recordset_alias; + template + using alias_n = internal::recordset_alias; + template + using alias_o = internal::recordset_alias; + template + using alias_p = internal::recordset_alias; + template + using alias_q = internal::recordset_alias; + template + using alias_r = internal::recordset_alias; + template + using alias_s = internal::recordset_alias; + template + using alias_t = internal::recordset_alias; + template + using alias_u = internal::recordset_alias; + template + using alias_v = internal::recordset_alias; + template + using alias_w = internal::recordset_alias; + template + using alias_x = internal::recordset_alias; + template + using alias_y = internal::recordset_alias; + template + using alias_z = internal::recordset_alias; + + using colalias_a = internal::column_alias<'a'>; + using colalias_b = internal::column_alias<'b'>; + using colalias_c = internal::column_alias<'c'>; + using colalias_d = internal::column_alias<'d'>; + using colalias_e = internal::column_alias<'e'>; + using colalias_f = internal::column_alias<'f'>; + using colalias_g = internal::column_alias<'g'>; + using colalias_h = internal::column_alias<'h'>; + using colalias_i = internal::column_alias<'i'>; +} diff --git a/libs/sqlite_orm-1.8.2/dev/alias_traits.h b/libs/sqlite_orm-1.8.2/dev/alias_traits.h new file mode 100644 index 0000000..a184959 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/alias_traits.h @@ -0,0 +1,51 @@ +#pragma once + +#include // std::remove_const, std::is_base_of, std::is_same + +#include "functional/cxx_universal.h" +#include "functional/cxx_type_traits_polyfill.h" +#include "type_traits.h" + +namespace sqlite_orm { + + /** @short Base class for a custom table alias, column alias or expression alias. + */ + struct alias_tag {}; + + namespace internal { + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_alias_v = std::is_base_of::value; + + template + using is_alias = polyfill::bool_constant>; + + /** @short Alias of a column in a record set, see `orm_column_alias`. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_column_alias_v = + polyfill::conjunction_v, polyfill::negation>>; + + template + using is_column_alias = is_alias; + + /** @short Alias of any type of record set, see `orm_recordset_alias`. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_recordset_alias_v = + polyfill::conjunction_v, polyfill::is_detected>; + + template + using is_recordset_alias = polyfill::bool_constant>; + + /** @short Alias of a concrete table, see `orm_table_alias`. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_table_alias_v = polyfill::conjunction_v< + is_recordset_alias, + polyfill::negation, std::remove_const_t>>>; + + template + using is_table_alias = polyfill::bool_constant>; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/arg_values.h b/libs/sqlite_orm-1.8.2/dev/arg_values.h new file mode 100644 index 0000000..9060460 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/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{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) { + sqlite3_value* value = this->values[index]; + return {value}; + } else { + throw std::system_error{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/libs/sqlite_orm-1.8.2/dev/arithmetic_tag.h b/libs/sqlite_orm-1.8.2/dev/arithmetic_tag.h new file mode 100644 index 0000000..86a714c --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/arithmetic_tag.h @@ -0,0 +1,20 @@ +#pragma once +#include + +namespace sqlite_orm { + + /** + * Helper classes used by statement_binder and row_extractor. + */ + struct int_or_smaller_tag {}; + struct bigint_tag {}; + struct real_tag {}; + + template + using arithmetic_tag_t = + std::conditional_t::value, + // Integer class + std::conditional_t, + // Floating-point class + real_tag>; +} diff --git a/libs/sqlite_orm-1.8.2/dev/ast/excluded.h b/libs/sqlite_orm-1.8.2/dev/ast/excluded.h new file mode 100644 index 0000000..faea0a3 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/ast/excluded.h @@ -0,0 +1,20 @@ +#pragma once + +#include // std::move + +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/libs/sqlite_orm-1.8.2/dev/ast/exists.h b/libs/sqlite_orm-1.8.2/dev/ast/exists.h new file mode 100644 index 0000000..b6d2a1c --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/ast/exists.h @@ -0,0 +1,33 @@ +#pragma once + +#include // std::move + +#include "../tags.h" + +namespace sqlite_orm { + namespace internal { + + template + struct exists_t : condition_t, negatable_t { + using expression_type = T; + using self = exists_t; + + expression_type expression; + + exists_t(expression_type expression_) : expression(std::move(expression_)) {} + }; + } + + /** + * 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 expression) { + return {std::move(expression)}; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/ast/group_by.h b/libs/sqlite_orm-1.8.2/dev/ast/group_by.h new file mode 100644 index 0000000..0ad6bd3 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/ast/group_by.h @@ -0,0 +1,74 @@ +#pragma once + +#include // std::tuple, std::make_tuple +#include // std::true_type, std::false_type +#include // std::forward, std::move + +#include "../functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { + + template + struct group_by_with_having { + using args_type = std::tuple; + using expression_type = T; + + args_type args; + expression_type expression; + }; + + /** + * GROUP BY pack holder. + */ + template + struct group_by_t { + using args_type = std::tuple; + + args_type args; + + template + group_by_with_having having(T expression) { + return {std::move(this->args), std::move(expression)}; + } + }; + + template + using is_group_by = polyfill::disjunction, + polyfill::is_specialization_of>; + + /** + * HAVING holder. + * T is having argument type. + */ + template + struct having_t { + using expression_type = T; + + expression_type expression; + }; + + template + using is_having = polyfill::is_specialization_of; + } + + /** + * 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)...)}; + } + + /** + * [Deprecation notice]: this function is deprecated and will be removed in v1.9. Please use `group_by(...).having(...)` instead. + * + * HAVING(expression). + * Example: storage.get_all(group_by(&Employee::name), having(greater_than(count(&Employee::name), 2))); + */ + template + [[deprecated("Use group_by(...).having(...) instead")]] internal::having_t having(T expression) { + return {std::move(expression)}; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/ast/into.h b/libs/sqlite_orm-1.8.2/dev/ast/into.h new file mode 100644 index 0000000..d2bba6a --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/ast/into.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { + + template + struct into_t { + using type = T; + }; + + template + using is_into = polyfill::is_specialization_of; + } + + template + internal::into_t into() { + return {}; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/ast/set.h b/libs/sqlite_orm-1.8.2/dev/ast/set.h new file mode 100644 index 0000000..0146acb --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/ast/set.h @@ -0,0 +1,108 @@ +#pragma once + +#include // std::tuple, std::tuple_size +#include // std::string +#include // std::vector +#include // std::stringstream +#include // std::false_type, std::true_type + +#include "../table_name_collector.h" + +namespace sqlite_orm { + + namespace internal { + + template + void iterate_ast(const T& t, L&& lambda); + + template + struct set_t { + using assigns_type = std::tuple; + + assigns_type assigns; + }; + + template + struct is_set : std::false_type {}; + + template + struct is_set> : std::true_type {}; + + struct dynamic_set_entry { + std::string serialized_value; + }; + + template + struct dynamic_set_t { + using context_t = C; + using entry_t = dynamic_set_entry; + using const_iterator = typename std::vector::const_iterator; + + dynamic_set_t(const context_t& context_) : context(context_), collector(this->context.db_objects) {} + + dynamic_set_t(const dynamic_set_t& other) = default; + dynamic_set_t(dynamic_set_t&& other) = default; + dynamic_set_t& operator=(const dynamic_set_t& other) = default; + dynamic_set_t& operator=(dynamic_set_t&& other) = default; + + template + void push_back(assign_t assign) { + auto newContext = this->context; + newContext.skip_table_name = true; + iterate_ast(assign, this->collector); + std::stringstream ss; + ss << serialize(assign.lhs, newContext) << ' ' << assign.serialize() << ' ' + << serialize(assign.rhs, context); + this->entries.push_back({ss.str()}); + } + + const_iterator begin() const { + return this->entries.begin(); + } + + const_iterator end() const { + return this->entries.end(); + } + + void clear() { + this->entries.clear(); + this->collector.table_names.clear(); + } + + std::vector entries; + context_t context; + table_name_collector collector; + }; + + template + struct is_set> : std::true_type {}; + + template + struct is_dynamic_set : std::false_type {}; + + template + struct is_dynamic_set> : std::true_type {}; + } + + /** + * SET keyword used in UPDATE ... SET queries. + * Args must have `assign_t` type. E.g. set(assign(&User::id, 5)) or set(c(&User::id) = 5) + */ + template + internal::set_t set(Args... args) { + using arg_tuple = std::tuple; + static_assert(std::tuple_size::value == + internal::count_tuple::value, + "set function accepts assign operators only"); + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * SET keyword used in UPDATE ... SET queries. It is dynamic version. It means use can add amount of arguments now known at compilation time but known at runtime. + */ + template + internal::dynamic_set_t> dynamic_set(const S& storage) { + internal::serializer_context_builder builder(storage); + return builder(); + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/ast/upsert_clause.h b/libs/sqlite_orm-1.8.2/dev/ast/upsert_clause.h new file mode 100644 index 0000000..3a3b269 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/ast/upsert_clause.h @@ -0,0 +1,68 @@ +#pragma once + +#if SQLITE_VERSION_NUMBER >= 3024000 +#include // std::tuple +#include // std::forward, std::move +#endif + +#include "../functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { +#if SQLITE_VERSION_NUMBER >= 3024000 + 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::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 + using is_upsert_clause = polyfill::is_specialization_of; +#else + template + struct is_upsert_clause : polyfill::bool_constant {}; +#endif + } + +#if SQLITE_VERSION_NUMBER >= 3024000 + /** + * 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)...)}; + } +#endif +} diff --git a/libs/sqlite_orm-1.8.2/dev/ast/where.h b/libs/sqlite_orm-1.8.2/dev/ast/where.h new file mode 100644 index 0000000..93e1572 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/ast/where.h @@ -0,0 +1,53 @@ +#pragma once + +#include // std::false_type, std::true_type +#include // std::move + +#include "../functional/cxx_universal.h" +#include "../functional/cxx_type_traits_polyfill.h" +#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 + SQLITE_ORM_INLINE_VAR constexpr bool is_where_v = polyfill::is_specialization_of_v; + + template + using is_where = polyfill::bool_constant>; + } + + /** + * 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/libs/sqlite_orm-1.8.2/dev/ast_iterator.h b/libs/sqlite_orm-1.8.2/dev/ast_iterator.h new file mode 100644 index 0000000..5dcc9bc --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/ast_iterator.h @@ -0,0 +1,682 @@ +#pragma once + +#include // std::vector +#include // std::reference_wrapper + +#include "tuple_helper/tuple_iteration.h" +#include "type_traits.h" +#include "conditions.h" +#include "alias.h" +#include "select_constraints.h" +#include "operators.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" +#include "ast/into.h" +#include "ast/group_by.h" +#include "ast/exists.h" +#include "ast/set.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * ast_iterator accepts any expression and a callable object + * which will be called for any node of provided expression. + * E.g. if we pass `where(is_equal(5, max(&User::id, 10))` then + * callable object will be called with 5, &User::id and 10. + * ast_iterator is used in finding literals to be bound to + * a statement, and to collect table names. + * + * Note that not all leaves of the expression tree are always visited: + * Column expressions can be more complex, but are passed as a whole to the callable. + * Examples are `column_pointer<>` and `alias_column_t<>`. + * + * To use `ast_iterator` call `iterate_ast(object, callable);` + * + * `T` is an ast element, e.g. where_t + */ + template + struct ast_iterator { + using node_type = T; + + /** + * L is a callable type. Mostly is a templated lambda + */ + template + void operator()(const T& t, L& lambda) const { + lambda(t); + } + }; + + /** + * Simplified API + */ + template + void iterate_ast(const T& t, L&& lambda) { + ast_iterator iterator; + iterator(t, lambda); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct ast_iterator, void> { + using node_type = as_optional_t; + + template + void operator()(const node_type& node, 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& expression, L& lambda) const { + iterate_ast(expression.get(), lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = group_by_t; + + template + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = excluded_t; + + template + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.expression, lambda); + } + }; + + template + struct ast_iterator> { + using node_type = T; + + template + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.actions, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = where_t; + + template + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.expression, lambda); + } + }; + + template + struct ast_iterator> { + using node_type = T; + + template + void operator()(const node_type& binaryCondition, L& lambda) const { + iterate_ast(binaryCondition.l, lambda); + iterate_ast(binaryCondition.r, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = binary_operator; + + template + void operator()(const node_type& binaryOperator, C& lambda) const { + iterate_ast(binaryOperator.lhs, lambda); + iterate_ast(binaryOperator.rhs, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = columns_t; + + template + void operator()(const node_type& cols, L& lambda) const { + iterate_ast(cols.columns, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = dynamic_in_t; + + template + void operator()(const node_type& in, C& lambda) const { + iterate_ast(in.left, lambda); + iterate_ast(in.argument, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = in_t; + + template + void operator()(const node_type& in, C& lambda) const { + iterate_ast(in.left, lambda); + iterate_ast(in.argument, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = std::vector; + + template + void operator()(const node_type& vec, L& lambda) const { + for(auto& i: vec) { + iterate_ast(i, lambda); + } + } + }; + + template<> + struct ast_iterator, void> { + using node_type = std::vector; + + template + void operator()(const node_type& vec, L& lambda) const { + lambda(vec); + } + }; + + template + struct ast_iterator> { + using node_type = T; + + template + void operator()(const node_type& c, L& lambda) const { + iterate_ast(c.left, lambda); + iterate_ast(c.right, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = into_t; + + template + void operator()(const node_type& /*node*/, L& /*lambda*/) const { + //.. + } + }; + + template + struct ast_iterator, void> { + using node_type = insert_raw_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = replace_raw_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = select_t; + + template + void operator()(const node_type& sel, L& lambda) const { + iterate_ast(sel.col, lambda); + iterate_ast(sel.conditions, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = get_all_t; + + template + void operator()(const node_type& get, L& lambda) const { + iterate_ast(get.conditions, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = get_all_pointer_t; + + template + void operator()(const node_type& get, L& lambda) const { + iterate_ast(get.conditions, lambda); + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct ast_iterator, void> { + using node_type = get_all_optional_t; + + template + void operator()(const node_type& get, L& lambda) const { + iterate_ast(get.conditions, lambda); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct ast_iterator, void> { + using node_type = update_all_t; + + template + void operator()(const node_type& u, L& lambda) const { + iterate_ast(u.set, lambda); + iterate_ast(u.conditions, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = remove_all_t; + + template + void operator()(const node_type& r, L& lambda) const { + iterate_ast(r.conditions, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = set_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.assigns, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = dynamic_set_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.entries, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = std::tuple; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_tuple(node, [&lambda](auto& v) { + iterate_ast(v, lambda); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = group_by_with_having; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + iterate_ast(node.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = having_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = cast_t; + + template + void operator()(const node_type& c, L& lambda) const { + iterate_ast(c.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = exists_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = like_t; + + template + void operator()(const node_type& lk, L& lambda) const { + iterate_ast(lk.arg, lambda); + iterate_ast(lk.pattern, lambda); + lk.arg3.apply([&lambda](auto& value) { + iterate_ast(value, lambda); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = glob_t; + + template + void operator()(const node_type& lk, L& lambda) const { + iterate_ast(lk.arg, lambda); + iterate_ast(lk.pattern, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = between_t; + + template + void operator()(const node_type& b, L& lambda) const { + iterate_ast(b.expr, lambda); + iterate_ast(b.b1, lambda); + iterate_ast(b.b2, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = named_collate; + + template + void operator()(const node_type& col, L& lambda) const { + iterate_ast(col.expr, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = negated_condition_t; + + template + void operator()(const node_type& neg, L& lambda) const { + iterate_ast(neg.c, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = is_null_t; + + template + void operator()(const node_type& i, L& lambda) const { + iterate_ast(i.t, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = is_not_null_t; + + template + void operator()(const node_type& i, L& lambda) const { + iterate_ast(i.t, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = function_call; + + template + void operator()(const node_type& f, L& lambda) const { + iterate_ast(f.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = built_in_function_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = built_in_aggregate_function_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = filtered_aggregate_function; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.function, lambda); + iterate_ast(node.where, lambda); + } + }; + + template + struct ast_iterator> { + using node_type = Join; + + template + void operator()(const node_type& join, L& lambda) const { + iterate_ast(join.constraint, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = on_t; + + template + void operator()(const node_type& on, L& lambda) const { + iterate_ast(on.arg, lambda); + } + }; + + // note: not strictly necessary as there's no binding support for USING; + // we provide it nevertheless, in line with on_t. + template + struct ast_iterator>> { + using node_type = T; + + template + void operator()(const node_type& o, L& lambda) const { + iterate_ast(o.column, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = simple_case_t; + + template + void operator()(const node_type& c, L& lambda) const { + c.case_expression.apply([&lambda](auto& c_) { + iterate_ast(c_, lambda); + }); + iterate_tuple(c.args, [&lambda](auto& pair) { + iterate_ast(pair.first, lambda); + iterate_ast(pair.second, lambda); + }); + c.else_expression.apply([&lambda](auto& el) { + iterate_ast(el, lambda); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = as_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = limit_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.lim, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = limit_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.lim, lambda); + a.off.apply([&lambda](auto& value) { + iterate_ast(value, lambda); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = limit_t; + + template + void operator()(const node_type& a, L& lambda) const { + a.off.apply([&lambda](auto& value) { + iterate_ast(value, lambda); + }); + iterate_ast(a.lim, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = distinct_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.value, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = all_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.value, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = bitwise_not_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.argument, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = values_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.tuple, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = dynamic_values_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.vector, lambda); + } + }; + + /** + * Column alias or literal: skipped + */ + template + struct ast_iterator, + polyfill::is_specialization_of, + is_column_alias>>> { + using node_type = T; + + template + void operator()(const node_type& /*node*/, L& /*lambda*/) const {} + }; + + template + struct ast_iterator, void> { + using node_type = order_by_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = collate_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expr, lambda); + } + }; + + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/backup.h b/libs/sqlite_orm-1.8.2/dev/backup.h new file mode 100644 index 0000000..73b6225 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/backup.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include // std::system_error +#include // std::string +#include +#include // std::move, std::exchange + +#include "error_code.h" +#include "connection_holder.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * A backup class. Don't construct it as is, call storage.make_backup_from or storage.make_backup_to instead. + * An instance of this class represents a wrapper around sqlite3_backup pointer. Use this class + * to have maximum control on a backup operation. In case you need a single backup in one line you + * can skip creating a backup_t instance and just call storage.backup_from or storage.backup_to function. + */ + struct backup_t { + backup_t(connection_ref to_, + const std::string& zDestName, + connection_ref from_, + const std::string& zSourceName, + std::unique_ptr holder_) : + handle(sqlite3_backup_init(to_.get(), zDestName.c_str(), from_.get(), zSourceName.c_str())), + holder(std::move(holder_)), to(to_), from(from_) { + if(!this->handle) { + throw std::system_error{orm_error_code::failed_to_init_a_backup}; + } + } + + backup_t(backup_t&& other) : + handle(std::exchange(other.handle, nullptr)), holder(std::move(other.holder)), to(other.to), + from(other.from) {} + + ~backup_t() { + if(this->handle) { + (void)sqlite3_backup_finish(this->handle); + } + } + + /** + * Calls sqlite3_backup_step with pages argument + */ + int step(int pages) { + return sqlite3_backup_step(this->handle, pages); + } + + /** + * Returns sqlite3_backup_remaining result + */ + int remaining() const { + return sqlite3_backup_remaining(this->handle); + } + + /** + * Returns sqlite3_backup_pagecount result + */ + int pagecount() const { + return sqlite3_backup_pagecount(this->handle); + } + + protected: + sqlite3_backup* handle = nullptr; + std::unique_ptr holder; + connection_ref to; + connection_ref from; + }; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/carray.h b/libs/sqlite_orm-1.8.2/dev/carray.h new file mode 100644 index 0000000..4e71887 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/carray.h @@ -0,0 +1,79 @@ +#pragma once + +/* + * Note: This feature needs constexpr variables with external linkage. + * which can be achieved before C++17's inline variables, but differs from compiler to compiler. + * Hence we make it only available for compilers supporting inline variables. + */ + +#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +#include // std::integral_constant +#include // std::move + +#include "functional/cxx_universal.h" +#include "pointer_value.h" + +namespace sqlite_orm { + + inline constexpr const char carray_pvt_name[] = "carray"; + using carray_pvt = std::integral_constant; + + template + using carray_pointer_arg = pointer_arg; + template + using carray_pointer_binding = pointer_binding; + template + using static_carray_pointer_binding = static_pointer_binding; + + /** + * Wrap a pointer of type 'carray' and its deleter function for binding it to a statement. + * + * Unless the deleter yields a nullptr 'xDestroy' function the ownership of the pointed-to-object + * is transferred to the pointer binding, which will delete it through + * the deleter when the statement finishes. + */ + template + auto bindable_carray_pointer(P* p, D d) noexcept -> pointer_binding { + return bindable_pointer(p, std::move(d)); + } + + /** + * Wrap a pointer of type 'carray' for binding it to a statement. + * + * Note: 'Static' means that ownership of the pointed-to-object won't be transferred + * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. + */ + template + auto statically_bindable_carray_pointer(P* p) noexcept -> static_pointer_binding { + return statically_bindable_pointer(p); + } + + /** + * Generalized form of the 'remember' SQL function that is a pass-through for values + * (it returns its argument unchanged using move semantics) but also saves the + * value that is passed through into a bound variable. + */ + template + struct note_value_fn { + P operator()(P&& value, carray_pointer_arg

pv) const { + if(P* observer = pv) { + *observer = value; + } + return std::move(value); + } + + static constexpr const char* name() { + return "note_value"; + } + }; + + /** + * remember(V, $PTR) extension function https://sqlite.org/src/file/ext/misc/remember.c + */ + struct remember_fn : note_value_fn { + static constexpr const char* name() { + return "remember"; + } + }; +} +#endif diff --git a/libs/sqlite_orm-1.8.2/dev/collate_argument.h b/libs/sqlite_orm-1.8.2/dev/collate_argument.h new file mode 100644 index 0000000..7208866 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/collate_argument.h @@ -0,0 +1,14 @@ +#pragma once + +namespace sqlite_orm { + + namespace internal { + + enum class collate_argument { + binary, + nocase, + rtrim, + }; + } + +} diff --git a/libs/sqlite_orm-1.8.2/dev/column.h b/libs/sqlite_orm-1.8.2/dev/column.h new file mode 100644 index 0000000..61f7448 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/column.h @@ -0,0 +1,184 @@ +#pragma once + +#include // std::tuple +#include // std::string +#include // std::unique_ptr +#include // std::is_same, std::is_member_object_pointer + +#include "functional/cxx_universal.h" +#include "functional/cxx_type_traits_polyfill.h" +#include "tuple_helper/tuple_traits.h" +#include "tuple_helper/tuple_filter.h" +#include "type_traits.h" +#include "member_traits/member_traits.h" +#include "type_is_nullable.h" +#include "constraints.h" + +namespace sqlite_orm { + + namespace internal { + + struct column_identifier { + + /** + * Column name. + */ + std::string name; + }; + + struct empty_setter {}; + + /* + * Encapsulates object member pointers that are used as column fields, + * and whose object is mapped to storage. + * + * G is a member object pointer or member function pointer + * S is a member function pointer or `empty_setter` + */ + template + struct column_field { + using member_pointer_t = G; + using setter_type = S; + using object_type = member_object_type_t; + using field_type = member_field_type_t; + + /** + * Member pointer used to read a field value. + * If it is a object member pointer it is also used to write a field value. + */ + const member_pointer_t member_pointer; + + /** + * Setter member function to write a field value + */ + SQLITE_ORM_NOUNIQUEADDRESS + const setter_type setter; + + /** + * Simplified interface for `NOT NULL` constraint + */ + constexpr bool is_not_null() const { + return !type_is_nullable::value; + } + }; + + /* + * Encapsulates a tuple of column constraints. + * + * Op... is a constraints pack, e.g. primary_key_t, unique_t etc + */ + template + struct column_constraints { + using constraints_type = std::tuple; + + SQLITE_ORM_NOUNIQUEADDRESS + constraints_type constraints; + + /** + * Checks whether contraints are of trait `Trait` + */ + template class Trait> + constexpr bool is() const { + return tuple_has::value; + } + + constexpr bool is_generated() const { +#if SQLITE_VERSION_NUMBER >= 3031000 + return is(); +#else + return false; +#endif + } + + /** + * Simplified interface for `DEFAULT` constraint + * @return string representation of default value if it exists otherwise nullptr + */ + std::unique_ptr default_value() const; + }; + + /** + * Column definition. + * + * It is a composition of orthogonal information stored in different base classes. + */ + template + struct column_t : column_identifier, column_field, column_constraints { +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + column_t(std::string name, G memberPointer, S setter, std::tuple op) : + column_identifier{std::move(name)}, column_field{memberPointer, setter}, + column_constraints{std::move(op)} {} +#endif + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_column_v = polyfill::is_specialization_of_v; + + template + using is_column = polyfill::bool_constant>; + + template + using col_index_sequence_with_field_type = + filter_tuple_sequence_t::template fn, + field_type_t, + filter_tuple_sequence_t>; + + template class TraitFn> + using col_index_sequence_with = filter_tuple_sequence_t::template fn, + constraints_type_t, + filter_tuple_sequence_t>; + + template class TraitFn> + using col_index_sequence_excluding = filter_tuple_sequence_t::template fn, + constraints_type_t, + filter_tuple_sequence_t>; + } + + /** + * Column builder function. You should use it to create columns instead of constructor + */ + template = true> + internal::column_t make_column(std::string name, M m, Op... constraints) { + static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::move(name), m, {}, std::make_tuple(constraints...)}); + } + + /** + * Column builder function with setter and getter. You should use it to create columns instead of constructor + */ + template = true, + internal::satisfies = true> + internal::column_t make_column(std::string name, S setter, G getter, Op... constraints) { + static_assert(std::is_same, internal::getter_field_type_t>::value, + "Getter and setter must get and set same data type"); + static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), getter, setter, std::make_tuple(constraints...)}); + } + + /** + * Column builder function with getter and setter (reverse order). You should use it to create columns instead of + * constructor + */ + template = true, + internal::satisfies = true> + internal::column_t make_column(std::string name, G getter, S setter, Op... constraints) { + static_assert(std::is_same, internal::getter_field_type_t>::value, + "Getter and setter must get and set same data type"); + static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), getter, setter, std::make_tuple(constraints...)}); + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/column_names_getter.h b/libs/sqlite_orm-1.8.2/dev/column_names_getter.h new file mode 100644 index 0000000..b35a4e3 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/column_names_getter.h @@ -0,0 +1,118 @@ +#pragma once + +#include // std::is_base_of +#include // std::string +#include // std::vector +#include // std::reference_wrapper +#include +#include // std::move + +#include "tuple_helper/tuple_traits.h" +#include "tuple_helper/tuple_iteration.h" +#include "error_code.h" +#include "mapped_type_proxy.h" +#include "alias_traits.h" +#include "select_constraints.h" +#include "storage_lookup.h" // pick_table +#include "serializer_context.h" +#include "util.h" + +namespace sqlite_orm { + + namespace internal { + + template + std::string serialize(const T&, const serializer_context&); + + template + std::vector& collect_table_column_names(std::vector& collectedExpressions, + bool definedOrder, + const Ctx& context) { + if(definedOrder) { + auto& table = pick_table>(context.db_objects); + collectedExpressions.reserve(collectedExpressions.size() + table.count_columns_amount()); + table.for_each_column([qualified = !context.skip_table_name, + &tableName = table.name, + &collectedExpressions](const column_identifier& column) { + if(is_alias_v) { + collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + "." + + quote_identifier(column.name)); + } else if(qualified) { + collectedExpressions.push_back(quote_identifier(tableName) + "." + + quote_identifier(column.name)); + } else { + collectedExpressions.push_back(quote_identifier(column.name)); + } + }); + } else { + collectedExpressions.reserve(collectedExpressions.size() + 1); + if(is_alias_v) { + collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + ".*"); + } else if(!context.skip_table_name) { + const basic_table& table = pick_table>(context.db_objects); + collectedExpressions.push_back(quote_identifier(table.name) + ".*"); + } else { + collectedExpressions.emplace_back("*"); + } + } + + return collectedExpressions; + } + + /** @short Column expression collector. + */ + struct column_names_getter { + /** + * The default implementation simply serializes the passed argument. + */ + template + std::vector& operator()(const E& t, const Ctx& context) { + auto columnExpression = serialize(t, context); + if(columnExpression.empty()) { + throw std::system_error{orm_error_code::column_not_found}; + } + collectedExpressions.reserve(collectedExpressions.size() + 1); + collectedExpressions.push_back(std::move(columnExpression)); + return collectedExpressions; + } + + template + std::vector& operator()(const std::reference_wrapper& expression, const Ctx& context) { + return (*this)(expression.get(), context); + } + + template + std::vector& operator()(const asterisk_t& expression, const Ctx& context) { + return collect_table_column_names(collectedExpressions, expression.defined_order, context); + } + + template + std::vector& operator()(const object_t& expression, const Ctx& context) { + return collect_table_column_names(collectedExpressions, expression.defined_order, context); + } + + template + std::vector& operator()(const columns_t& cols, const Ctx& context) { + collectedExpressions.reserve(collectedExpressions.size() + cols.count); + iterate_tuple(cols.columns, [this, &context](auto& colExpr) { + (*this)(colExpr, context); + }); + // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order + if(mpl::instantiate, + typename columns_t::columns_type>::value && + collectedExpressions.capacity() > collectedExpressions.size()) { + collectedExpressions.shrink_to_fit(); + } + return collectedExpressions; + } + + std::vector collectedExpressions; + }; + + template + std::vector get_column_names(const T& t, const Ctx& context) { + column_names_getter serializer; + return serializer(t, context); + } + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/column_pointer.h b/libs/sqlite_orm-1.8.2/dev/column_pointer.h new file mode 100644 index 0000000..90a1e6a --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/column_pointer.h @@ -0,0 +1,62 @@ +#pragma once + +#include // std::string +#include // std::move + +#include "functional/cxx_core_features.h" +#include "conditions.h" +#include "alias_traits.h" + +namespace sqlite_orm { + namespace internal { + /** + * This class is used to store explicit mapped type T and its column descriptor (member pointer/getter/setter). + * Is useful when mapped type is derived from other type and base class has members mapped to a storage. + */ + template + struct column_pointer { + using self = column_pointer; + using type = T; + using field_type = F; + + field_type field; + + template + is_equal_t operator==(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + is_not_equal_t operator!=(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + lesser_than_t operator<(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + lesser_or_equal_t operator<=(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + greater_than_t operator>(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + greater_or_equal_t operator>=(R rhs) const { + return {*this, std::move(rhs)}; + } + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_column_pointer_v = polyfill::is_specialization_of_v; + + template + using is_column_pointer = polyfill::bool_constant>; + + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/column_result.h b/libs/sqlite_orm-1.8.2/dev/column_result.h new file mode 100644 index 0000000..b5d2fd7 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/column_result.h @@ -0,0 +1,289 @@ +#pragma once + +#include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of +#include // std::tuple +#include // std::reference_wrapper + +#include "functional/cxx_universal.h" +#include "tuple_helper/tuple_traits.h" +#include "tuple_helper/tuple_fy.h" +#include "tuple_helper/tuple_filter.h" +#include "type_traits.h" +#include "member_traits/member_traits.h" +#include "mapped_type_proxy.h" +#include "core_functions.h" +#include "select_constraints.h" +#include "operators.h" +#include "rowid.h" +#include "alias.h" +#include "storage_traits.h" +#include "function.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * Obtains the result type of expressions that form the columns of a select statement. + * + * This is a proxy class used to define what type must have result type depending on select + * arguments (member pointer, aggregate functions, etc). Below you can see specializations + * for different types. E.g. specialization for internal::length_t has `type` int cause + * LENGTH returns INTEGER in sqlite. Every column_result_t must have `type` type that equals + * c++ SELECT return type for T + * DBOs - db_objects_tuple type + * T - C++ type + * SFINAE - sfinae argument + */ + template + struct column_result_t; + + template + using column_result_of_t = typename column_result_t::type; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct column_result_t, void> { + using type = std::optional>; + }; + + template + struct column_result_t, void> { + using type = std::optional; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t> : member_field_type {}; + + template + struct column_result_t, void> { + using type = R; + }; + + template + 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, Rest...>, void> { + using type = std::unique_ptr>; + }; + + template + struct column_result_t, S, X>, void> { + using type = std::unique_ptr>; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t { + using type = nullptr_t; + }; + + template + struct column_result_t { + using type = int; + }; + + template + struct column_result_t, void> : column_result_t {}; + + template + struct column_result_t, void> : column_result_t {}; + + template + struct column_result_t, void> { + using type = std::string; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t { + using type = int64; + }; + + template + struct column_result_t { + using type = int64; + }; + + template + struct column_result_t { + using type = int64; + }; + + template + struct column_result_t, void> { + using type = int64; + }; + + template + struct column_result_t, void> { + using type = int64; + }; + + template + struct column_result_t, void> { + using type = int64; + }; + + template + struct column_result_t, void> : column_result_t {}; + + template + struct column_result_t, void> : column_result_t {}; + + template + struct column_result_t, void> { + using type = tuple_cat_t>>...>; + }; + + template + struct column_result_t> : column_result_t {}; + + template + struct column_result_t> { + using left_result = column_result_of_t; + using right_result = column_result_of_t; + static_assert(std::is_same::value, + "Compound subselect queries must return same types"); + using type = left_result; + }; + + template + struct column_result_t> { + using type = typename T::result_type; + }; + + /** + * Result for the most simple queries like `SELECT 1` + */ + template + struct column_result_t> { + using type = T; + }; + + /** + * Result for the most simple queries like `SELECT 'ototo'` + */ + template + struct column_result_t { + using type = std::string; + }; + + template + struct column_result_t { + using type = std::string; + }; + + template + struct column_result_t, void> : column_result_t> {}; + + template + struct column_result_t, void> + : storage_traits::storage_mapped_columns> {}; + + template + struct column_result_t, void> { + using type = T; + }; + + template + struct column_result_t, void> { + using type = T; + }; + + template + struct column_result_t, void> { + using type = R; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> : column_result_t {}; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/conditions.h b/libs/sqlite_orm-1.8.2/dev/conditions.h new file mode 100644 index 0000000..cc5e58c --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/conditions.h @@ -0,0 +1,1292 @@ +#pragma once + +#include // std::string +#include // std::enable_if, std::is_same, std::remove_const +#include // std::vector +#include // std::tuple +#include // std::stringstream + +#include "functional/cxx_universal.h" +#include "functional/cxx_type_traits_polyfill.h" +#include "is_base_of_template.h" +#include "type_traits.h" +#include "collate_argument.h" +#include "constraints.h" +#include "optional_container.h" +#include "serializer_context.h" +#include "tags.h" +#include "alias_traits.h" +#include "expression.h" +#include "type_printer.h" +#include "literal.h" + +namespace sqlite_orm { + + namespace internal { + + struct limit_string { + operator std::string() const { + return "LIMIT"; + } + }; + + /** + * Stores LIMIT/OFFSET info + */ + template + struct limit_t : limit_string { + T lim; + optional_container off; + + limit_t() = default; + + limit_t(decltype(lim) lim_) : lim(std::move(lim_)) {} + + limit_t(decltype(lim) lim_, decltype(off) off_) : lim(std::move(lim_)), off(std::move(off_)) {} + }; + + template + struct is_limit : std::false_type {}; + + template + struct is_limit> : std::true_type {}; + + /** + * Stores OFFSET only info + */ + template + struct offset_t { + T off; + }; + + template + using is_offset = polyfill::is_specialization_of; + + /** + * Collated something + */ + template + struct collate_t : public condition_t { + T expr; + collate_argument argument; + + collate_t(T expr_, collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} + + operator std::string() const { + return collate_constraint_t{this->argument}; + } + }; + + struct named_collate_base { + std::string name; + + operator std::string() const { + return "COLLATE " + this->name; + } + }; + + /** + * Collated something with custom collate function + */ + template + struct named_collate : named_collate_base { + T expr; + + named_collate(T expr_, std::string name_) : named_collate_base{std::move(name_)}, expr(std::move(expr_)) {} + }; + + struct negated_condition_string { + operator std::string() const { + return "NOT"; + } + }; + + /** + * Result of not operator + */ + 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 + * 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; + + 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_)) {} + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_of_template_v; + + template + using is_binary_condition = polyfill::bool_constant>; + + struct and_condition_string { + operator std::string() const { + return "AND"; + } + }; + + /** + * Result of and operator + */ + template + 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"; + } + }; + + /** + * Result of or operator + */ + template + 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 "="; + } + }; + + /** + * = and == operators object + */ + template + struct is_equal_t : binary_condition, negatable_t { + using self = is_equal_t; + + 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}; + } + + named_collate collate(std::string name) const { + return {*this, std::move(name)}; + } + + template + named_collate collate() const { + std::stringstream ss; + ss << C::name() << std::flush; + return {*this, ss.str()}; + } + }; + + struct is_not_equal_string { + operator std::string() const { + return "!="; + } + }; + + /** + * != operator object + */ + template + struct is_not_equal_t : binary_condition, negatable_t { + using self = is_not_equal_t; + + 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 greater_than_string { + operator std::string() const { + return ">"; + } + }; + + /** + * > operator object. + */ + template + struct greater_than_t : binary_condition, negatable_t { + using self = greater_than_t; + + 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 greater_or_equal_string { + operator std::string() const { + return ">="; + } + }; + + /** + * >= operator object. + */ + template + struct greater_or_equal_t : binary_condition, negatable_t { + using self = greater_or_equal_t; + + 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 lesser_than_string { + operator std::string() const { + return "<"; + } + }; + + /** + * < operator object. + */ + template + struct lesser_than_t : binary_condition, negatable_t { + using self = lesser_than_t; + + 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 lesser_or_equal_string { + operator std::string() const { + return "<="; + } + }; + + /** + * <= operator object. + */ + template + struct lesser_or_equal_t : binary_condition, negatable_t { + using self = lesser_or_equal_t; + + 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 in_base { + bool negative = false; // used in not_in + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + in_base(bool negative) : negative{negative} {} +#endif + }; + + /** + * IN operator object. + */ + template + struct dynamic_in_t : condition_t, in_base, negatable_t { + using self = dynamic_in_t; + + L left; // left expression + A argument; // 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 left_, decltype(argument) argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} + }; + + struct is_null_string { + operator std::string() const { + return "IS NULL"; + } + }; + + /** + * IS NULL operator object. + */ + template + struct is_null_t : is_null_string, negatable_t { + using self = is_null_t; + + T t; + + is_null_t(T t_) : t(std::move(t_)) {} + }; + + struct is_not_null_string { + operator std::string() const { + return "IS NOT NULL"; + } + }; + + /** + * IS NOT NULL operator object. + */ + template + struct is_not_null_t : is_not_null_string, negatable_t { + using self = is_not_null_t; + + T t; + + is_not_null_t(T t_) : t(std::move(t_)) {} + }; + + struct order_by_base { + int asc_desc = 0; // 1: asc, -1: desc + std::string _collate_argument; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + order_by_base() = default; + + order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : + asc_desc(asc_desc_), _collate_argument(std::move(_collate_argument_)) {} +#endif + }; + + struct order_by_string { + operator std::string() const { + return "ORDER BY"; + } + }; + + /** + * ORDER BY argument holder. + */ + template + struct order_by_t : order_by_base, order_by_string { + using expression_type = O; + using self = order_by_t; + + expression_type expression; + + order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} + + self asc() const { + auto res = *this; + res.asc_desc = 1; + return res; + } + + self desc() const { + auto res = *this; + res.asc_desc = -1; + return res; + } + + self collate_binary() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); + return res; + } + + self collate_nocase() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); + return res; + } + + self collate_rtrim() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); + return res; + } + + self collate(std::string name) const { + auto res = *this; + res._collate_argument = std::move(name); + return res; + } + + template + self collate() const { + std::stringstream ss; + ss << C::name() << std::flush; + return this->collate(ss.str()); + } + }; + + /** + * ORDER BY pack holder. + */ + template + struct multi_order_by_t : order_by_string { + using args_type = std::tuple; + + args_type args; + + multi_order_by_t(args_type args_) : args{std::move(args_)} {} + }; + + struct dynamic_order_by_entry_t : order_by_base { + std::string name; + + dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) : + order_by_base{asc_desc_, std::move(collate_argument_)}, name(std::move(name_)) {} + }; + + /** + * C - serializer 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; + + 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.expression, newContext); + this->entries.emplace_back(std::move(columnName), + order_by.asc_desc, + std::move(order_by._collate_argument)); + } + + const_iterator begin() const { + return this->entries.begin(); + } + + const_iterator end() const { + return this->entries.end(); + } + + void clear() { + this->entries.clear(); + } + + protected: + std::vector entries; + context_t context; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_order_by_v = + polyfill::disjunction_v, + polyfill::is_specialization_of, + polyfill::is_specialization_of>; + + template + using is_order_by = polyfill::bool_constant>; + + struct between_string { + operator std::string() const { + return "BETWEEN"; + } + }; + + /** + * BETWEEN operator object. + */ + 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; + + between_t(expression_type expr_, lower_type b1_, upper_type b2_) : + expr(std::move(expr_)), b1(std::move(b1_)), b2(std::move(b2_)) {} + }; + + struct like_string { + operator std::string() const { + return "LIKE"; + } + }; + + /** + * 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; + + arg_t arg; + pattern_t pattern; + optional_container arg3; // not escape cause escape exists as a function here + + 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 { + optional_container newArg3{std::move(c)}; + return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; + } + }; + + struct glob_string { + operator std::string() const { + return "GLOB"; + } + }; + + template + struct glob_t : condition_t, glob_string, internal::negatable_t { + using self = glob_t; + using arg_t = A; + using pattern_t = T; + + arg_t arg; + pattern_t pattern; + + glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {} + }; + + struct cross_join_string { + operator std::string() const { + return "CROSS JOIN"; + } + }; + + /** + * CROSS JOIN holder. + * T is joined type which represents any mapped table. + */ + template + struct cross_join_t : cross_join_string { + using type = T; + }; + + struct natural_join_string { + operator std::string() const { + return "NATURAL JOIN"; + } + }; + + /** + * NATURAL JOIN holder. + * T is joined type which represents any mapped table. + */ + template + struct natural_join_t : natural_join_string { + using type = T; + }; + + struct left_join_string { + operator std::string() const { + return "LEFT JOIN"; + } + }; + + /** + * 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; + + on_type constraint; + + left_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct join_string { + operator std::string() const { + return "JOIN"; + } + }; + + /** + * 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; + + on_type constraint; + + join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct left_outer_join_string { + operator std::string() const { + return "LEFT OUTER JOIN"; + } + }; + + /** + * 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; + + on_type constraint; + + left_outer_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct on_string { + operator std::string() const { + return "ON"; + } + }; + + /** + * 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; + + arg_type arg; + + on_t(arg_type arg_) : arg(std::move(arg_)) {} + }; + + /** + * USING argument holder. + */ + template + struct using_t { + column_pointer column; + + operator std::string() const { + return "USING"; + } + }; + + struct inner_join_string { + operator std::string() const { + return "INNER JOIN"; + } + }; + + /** + * 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; + + on_type constraint; + + inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + 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 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 from_t { + using tuple_type = std::tuple; + }; + + template + using is_from = polyfill::is_specialization_of; + + template + using is_constrained_join = polyfill::is_detected; + } + + /** + * Explicit FROM function. Usage: + * `storage.select(&User::id, from());` + */ + template + internal::from_t from() { + static_assert(sizeof...(Tables) > 0, ""); + return {}; + } + + template = true> + internal::negated_condition_t operator!(T arg) { + return {std::move(arg)}; + } + + // Deliberately put operators for `expression_t` into the internal namespace + // to facilitate ADL (Argument Dependent Lookup) + namespace internal { + /** + * Cute operators for columns + */ + template + lesser_than_t operator<(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + lesser_than_t operator<(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + lesser_or_equal_t operator<=(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + lesser_or_equal_t operator<=(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + greater_than_t operator>(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + greater_than_t operator>(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + greater_or_equal_t operator>=(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + greater_or_equal_t operator>=(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + is_equal_t operator==(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + is_equal_t operator==(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + is_not_equal_t operator!=(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + is_not_equal_t operator!=(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + conc_t operator||(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + conc_t operator||(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + conc_t operator||(expression_t l, expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } + + template = true> + conc_t operator||(E expr, R r) { + return {std::move(expr), std::move(r)}; + } + + template = true> + conc_t operator||(L l, E expr) { + return {std::move(l), std::move(expr)}; + } + + template + add_t operator+(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + add_t operator+(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + add_t operator+(expression_t l, expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } + + template + sub_t operator-(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + sub_t operator-(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + sub_t operator-(expression_t l, expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } + + template + mul_t operator*(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + mul_t operator*(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + mul_t operator*(expression_t l, expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } + + template + div_t operator/(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + div_t operator/(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + div_t operator/(expression_t l, expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } + + template + mod_t operator%(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + mod_t operator%(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + mod_t operator%(expression_t l, expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } + } + + template + internal::using_t using_(F O::*p) { + return {p}; + } + template + internal::using_t using_(internal::column_pointer cp) { + return {std::move(cp)}; + } + + template + internal::on_t on(T t) { + return {std::move(t)}; + } + + template + internal::cross_join_t cross_join() { + return {}; + } + + template + internal::natural_join_t natural_join() { + return {}; + } + + template + internal::left_join_t left_join(O o) { + return {std::move(o)}; + } + + template + internal::join_t join(O o) { + return {std::move(o)}; + } + + template + internal::left_outer_join_t left_outer_join(O o) { + return {std::move(o)}; + } + + template + internal::inner_join_t inner_join(O o) { + return {std::move(o)}; + } + + template + internal::offset_t offset(T off) { + return {std::move(off)}; + } + + template + internal::limit_t limit(T lim) { + return {std::move(lim)}; + } + + template = true> + internal::limit_t limit(O off, T lim) { + return {std::move(lim), {std::move(off)}}; + } + + template + internal::limit_t limit(T lim, internal::offset_t offt) { + return {std::move(lim), {std::move(offt.off)}}; + } + + template, + std::is_base_of>, + bool> = true> + 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, + std::is_base_of>, + bool> = true> + 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 + internal::is_not_null_t is_not_null(T t) { + return {std::move(t)}; + } + + 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, column alias or expression + * + * Examples: + * storage.select(&User::name, order_by(&User::id)) + * storage.select(as(&User::name), order_by(get())) + */ + template> = true> + internal::order_by_t order_by(O o) { + return {std::move(o)}; + } + + /** + * ORDER BY positional ordinal + * + * Examples: + * storage.select(&User::name, order_by(1)) + */ + template> = true> + 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::serializer_context_builder builder(storage); + return builder(); + } + + /** + * 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)}}; + } + + /** + * CAST(X AS type). + * Example: cast(&User::id) + */ + template + internal::cast_t cast(E e) { + return {std::move(e)}; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/connection_holder.h b/libs/sqlite_orm-1.8.2/dev/connection_holder.h new file mode 100644 index 0000000..0631adf --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/connection_holder.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include // std::string + +#include "error_code.h" + +namespace sqlite_orm { + + namespace internal { + + struct connection_holder { + + connection_holder(std::string filename_) : filename(std::move(filename_)) {} + + void retain() { + if(1 == ++this->_retain_count) { + auto rc = sqlite3_open(this->filename.c_str(), &this->db); + if(rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + } + + void release() { + if(0 == --this->_retain_count) { + auto rc = sqlite3_close(this->db); + if(rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + } + + sqlite3* get() const { + return this->db; + } + + int retain_count() const { + return this->_retain_count; + } + + const std::string filename; + + protected: + sqlite3* db = nullptr; + std::atomic_int _retain_count{}; + }; + + struct connection_ref { + connection_ref(connection_holder& holder_) : holder(holder_) { + this->holder.retain(); + } + + connection_ref(const connection_ref& other) : holder(other.holder) { + this->holder.retain(); + } + + connection_ref(connection_ref&& other) : holder(other.holder) { + this->holder.retain(); + } + + ~connection_ref() { + this->holder.release(); + } + + sqlite3* get() const { + return this->holder.get(); + } + + protected: + connection_holder& holder; + }; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/constraints.h b/libs/sqlite_orm-1.8.2/dev/constraints.h new file mode 100644 index 0000000..37ef840 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/constraints.h @@ -0,0 +1,560 @@ +#pragma once + +#include // std::system_error +#include // std::ostream +#include // std::string +#include // std::tuple, std::make_tuple +#include // std::is_base_of, std::false_type, std::true_type + +#include "functional/cxx_universal.h" +#include "functional/cxx_type_traits_polyfill.h" +#include "functional/mpl.h" +#include "tuple_helper/same_or_void.h" +#include "tuple_helper/tuple_traits.h" +#include "tuple_helper/tuple_filter.h" +#include "type_traits.h" +#include "collate_argument.h" +#include "error_code.h" +#include "table_type_of.h" +#include "type_printer.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * AUTOINCREMENT constraint class. + */ + struct autoincrement_t {}; + + enum class conflict_clause_t { + rollback, + abort, + fail, + ignore, + replace, + }; + + struct primary_key_base { + enum class order_by { + unspecified, + ascending, + descending, + }; + struct { + order_by asc_option = order_by::unspecified; + conflict_clause_t conflict_clause = conflict_clause_t::rollback; + bool conflict_clause_is_on = false; + } options; + }; + + template + struct primary_key_with_autoincrement { + using primary_key_type = T; + + primary_key_type primary_key; + + primary_key_with_autoincrement(primary_key_type primary_key_) : primary_key(primary_key_) {} + }; + + /** + * PRIMARY KEY constraint class. + * Cs is parameter pack which contains columns (member pointers and/or function pointers). Can be empty when + * used within `make_column` function. + */ + template + struct primary_key_t : primary_key_base { + using self = primary_key_t; + using order_by = primary_key_base::order_by; + using columns_tuple = std::tuple; + + columns_tuple columns; + + primary_key_t(decltype(columns) columns) : columns(std::move(columns)) {} + + self asc() const { + auto res = *this; + res.options.asc_option = order_by::ascending; + return res; + } + + self desc() const { + auto res = *this; + res.options.asc_option = order_by::descending; + return res; + } + + primary_key_with_autoincrement autoincrement() const { + return {*this}; + } + + self on_conflict_rollback() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::rollback; + return res; + } + + self on_conflict_abort() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::abort; + return res; + } + + self on_conflict_fail() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::fail; + return res; + } + + self on_conflict_ignore() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::ignore; + return res; + } + + self on_conflict_replace() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::replace; + return res; + } + }; + + struct unique_base { + operator std::string() const { + return "UNIQUE"; + } + }; + + /** + * UNIQUE constraint class. + */ + template + struct unique_t : unique_base { + using columns_tuple = std::tuple; + + columns_tuple columns; + + unique_t(columns_tuple columns_) : columns(std::move(columns_)) {} + }; + + /** + * DEFAULT constraint class. + * T is a value type. + */ + template + struct default_t { + using value_type = T; + + value_type value; + + operator std::string() const { + return "DEFAULT"; + } + }; + +#if SQLITE_VERSION_NUMBER >= 3006019 + + /** + * 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 + struct foreign_key_t; + + enum class foreign_key_action { + none, // not specified + no_action, + restrict_, + set_null, + set_default, + cascade, + }; + + inline std::ostream& operator<<(std::ostream& os, foreign_key_action action) { + switch(action) { + case foreign_key_action::no_action: + os << "NO ACTION"; + break; + case foreign_key_action::restrict_: + os << "RESTRICT"; + break; + case foreign_key_action::set_null: + os << "SET NULL"; + break; + case foreign_key_action::set_default: + os << "SET DEFAULT"; + break; + case foreign_key_action::cascade: + os << "CASCADE"; + break; + case foreign_key_action::none: + break; + } + return os; + } + + struct on_update_delete_base { + const bool update; // true if update and false if delete + + operator std::string() const { + if(this->update) { + return "ON UPDATE"; + } else { + return "ON DELETE"; + } + } + }; + + /** + * F - foreign key class + */ + template + struct on_update_delete_t : on_update_delete_base { + using foreign_key_type = F; + + 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_) {} + + foreign_key_action _action = foreign_key_action::none; + + 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; + } + + 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; + } + + 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; + } + + 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; + } + + 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; + } + + operator bool() const { + return this->_action != foreign_key_action::none; + } + }; + + template + bool operator==(const on_update_delete_t& lhs, const on_update_delete_t& rhs) { + return lhs._action == rhs._action; + } + + template + struct foreign_key_t, std::tuple> { + using columns_type = std::tuple; + 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; + + /** + * Holds obect type of all source columns. + */ + using source_type = typename same_or_void...>::type; + + 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 + bool operator==(const foreign_key_t& lhs, const foreign_key_t& rhs) { + return lhs.columns == rhs.columns && lhs.references == rhs.references && lhs.on_update == rhs.on_update && + lhs.on_delete == rhs.on_delete; + } + + /** + * 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; + + tuple_type columns; + + template + foreign_key_t, std::tuple> references(Rs... refs) { + return {std::move(this->columns), std::make_tuple(std::forward(refs)...)}; + } + }; +#endif + + struct collate_constraint_t { + collate_argument argument = collate_argument::binary; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + collate_constraint_t(collate_argument argument) : argument{argument} {} +#endif + + operator std::string() const { + return "COLLATE " + this->string_from_collate_argument(this->argument); + } + + static std::string string_from_collate_argument(collate_argument argument) { + switch(argument) { + case collate_argument::binary: + return "BINARY"; + case collate_argument::nocase: + return "NOCASE"; + case collate_argument::rtrim: + return "RTRIM"; + } + throw std::system_error{orm_error_code::invalid_collate_argument_enum}; + } + }; + + template + struct check_t { + using expression_type = T; + + expression_type expression; + }; + + struct basic_generated_always { + enum class storage_type { + not_specified, + virtual_, + stored, + }; + + bool full = true; + storage_type storage = storage_type::not_specified; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + basic_generated_always(bool full, storage_type storage) : full{full}, storage{storage} {} +#endif + }; + + template + struct generated_always_t : basic_generated_always { + using expression_type = T; + + expression_type expression; + + generated_always_t(expression_type expression_, bool full, storage_type storage) : + basic_generated_always{full, storage}, expression(std::move(expression_)) {} + + generated_always_t virtual_() { + return {std::move(this->expression), this->full, storage_type::virtual_}; + } + + generated_always_t stored() { + return {std::move(this->expression), this->full, storage_type::stored}; + } + }; + + } + + namespace internal { + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = polyfill::is_specialization_of_v; + + template + using is_foreign_key = polyfill::bool_constant>; + + template + struct is_primary_key : std::false_type {}; + + template + struct is_primary_key> : std::true_type {}; + + template + struct is_primary_key> : std::true_type {}; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = is_primary_key::value; + + template + using is_generated_always = polyfill::is_specialization_of; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = is_generated_always::value; + + template + using is_autoincrement = std::is_same; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_autoincrement_v = is_autoincrement::value; + + /** + * PRIMARY KEY INSERTABLE traits. + */ + template + struct is_primary_key_insertable + : polyfill::disjunction< + mpl::instantiate, + check_if_tuple_has_template, + check_if_tuple_has_template>, + constraints_type_t>, + std::is_base_of>>> { + + static_assert(tuple_has>::value, "an unexpected type was passed"); + }; + + template + using is_constraint = + mpl::instantiate, + check_if, + check_if, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_type, +#if SQLITE_VERSION_NUMBER >= 3031000 + check_if, +#endif + // dummy tail because of SQLITE_VERSION_NUMBER checks above + mpl::always>, + T>; + } + +#if SQLITE_VERSION_NUMBER >= 3031000 + template + internal::generated_always_t generated_always_as(T expression) { + return {std::move(expression), true, internal::basic_generated_always::storage_type::not_specified}; + } + + template + internal::generated_always_t as(T expression) { + return {std::move(expression), false, internal::basic_generated_always::storage_type::not_specified}; + } +#endif +#if SQLITE_VERSION_NUMBER >= 3006019 + + /** + * FOREIGN KEY constraint construction function that takes member pointer as argument + * Available in SQLite 3.6.19 or higher + */ + template + internal::foreign_key_intermediate_t foreign_key(Cs... columns) { + return {std::make_tuple(std::forward(columns)...)}; + } +#endif + + /** + * UNIQUE constraint builder function. + */ + template + internal::unique_t unique(Args... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + inline internal::unique_t<> unique() { + return {{}}; + } + + /** + * AUTOINCREMENT keyword. [Deprecation notice] Use `primary_key().autoincrement()` instead of using this function. + * This function will be removed in 1.9 + */ + [[deprecated("Use primary_key().autoincrement()` instead")]] inline internal::autoincrement_t autoincrement() { + return {}; + } + + template + internal::primary_key_t primary_key(Cs... cs) { + return {std::make_tuple(std::forward(cs)...)}; + } + + inline internal::primary_key_t<> primary_key() { + return {{}}; + } + + template + internal::default_t default_value(T t) { + return {std::move(t)}; + } + + inline internal::collate_constraint_t collate_nocase() { + return {internal::collate_argument::nocase}; + } + + inline internal::collate_constraint_t collate_binary() { + return {internal::collate_argument::binary}; + } + + inline internal::collate_constraint_t collate_rtrim() { + return {internal::collate_argument::rtrim}; + } + + template + internal::check_t check(T t) { + return {std::move(t)}; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/core_functions.h b/libs/sqlite_orm-1.8.2/dev/core_functions.h new file mode 100644 index 0000000..5461ada --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/core_functions.h @@ -0,0 +1,2090 @@ +#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 "functional/cxx_type_traits_polyfill.h" +#include "conditions.h" +#include "is_base_of_template.h" +#include "tuple_helper/tuple_filter.h" +#include "serialize_result_type.h" +#include "operators.h" +#include "ast/into.h" + +namespace sqlite_orm { + + using int64 = sqlite_int64; + using uint64 = sqlite_uint64; + + namespace internal { + + 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 size_t args_size = std::tuple_size::value; + + args_type args; + + built_in_function_t(args_type&& args_) : args(std::move(args_)) {} + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_built_in_function_v = is_base_of_template_v; + + template + using is_built_in_function = polyfill::bool_constant>; + + template + struct filtered_aggregate_function { + using function_type = F; + using where_expression = W; + + function_type function; + where_expression where; + }; + + template + struct where_t; + + template + struct built_in_aggregate_function_t : built_in_function_t { + using super = built_in_function_t; + + using super::super; + + template + filtered_aggregate_function, W> filter(where_t wh) { + return {*this, std::move(wh.expression)}; + } + }; + + 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 nullif_string { + serialize_result_type serialize() const { + return "NULLIF"; + } + }; + + 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; + + template + filtered_aggregate_function, W> filter(where_t wh) { + return {*this, std::move(wh.expression)}; + } + }; + + /** + * 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 json_group_object_string { + serialize_result_type serialize() const { + return "JSON_GROUP_OBJECT"; + } + }; +#endif // SQLITE_ENABLE_JSON1 + + template + using field_type_or_type_t = polyfill::detected_or_t>; + } + + /** + * Cute operators for core functions + */ + template = true> + internal::lesser_than_t operator<(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template = true> + internal::lesser_or_equal_t operator<=(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template = true> + internal::greater_than_t operator>(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template = true> + internal::greater_or_equal_t operator>=(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template = true> + internal::is_equal_t operator==(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template = true> + 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::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::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::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::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::built_in_function_t changes() { + return {{}}; + } + + /** + * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim + */ + template + 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::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::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::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::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::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::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::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::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::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::is_into>::value == 0, bool> = true> + internal::built_in_function_t 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::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::built_in_function_t round(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + +#if SQLITE_VERSION_NUMBER >= 3007016 + + /** + * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char + */ + template + 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::built_in_function_t random() { + return {{}}; + } + +#endif + + /** + * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce + */ + template + auto coalesce(Args... args) + -> internal::built_in_function_t::value, + std::common_type...>, + polyfill::type_identity>::type, + internal::coalesce_string, + Args...> { + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * IFNULL(X,Y) function https://www.sqlite.org/lang_corefunc.html#ifnull + */ + template + auto ifnull(X x, Y y) -> internal::built_in_function_t< + typename std::conditional_t< // choose R or common type + std::is_void::value, + std::common_type, internal::field_type_or_type_t>, + polyfill::type_identity>::type, + internal::ifnull_string, + X, + Y> { + return {std::make_tuple(std::move(x), std::move(y))}; + } + + /** + * NULLIF(X,Y) function https://www.sqlite.org/lang_corefunc.html#nullif + */ +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + /** + * NULLIF(X,Y) using common return type of X and Y + */ + template>, + polyfill::is_detected, + internal::field_type_or_type_t>>, + bool> = true> + auto nullif(X x, Y y) { + if constexpr(std::is_void_v) { + using F = internal::built_in_function_t< + std::optional, internal::field_type_or_type_t>>, + internal::nullif_string, + X, + Y>; + + return F{std::make_tuple(std::move(x), std::move(y))}; + } else { + using F = internal::built_in_function_t; + + return F{std::make_tuple(std::move(x), std::move(y))}; + } + } +#else + template + internal::built_in_function_t nullif(X x, Y y) { + return {std::make_tuple(std::move(x), std::move(y))}; + } +#endif + + /** + * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + 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::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::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::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::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::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::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::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 + /** + * SOUNDEX(X) function https://www.sqlite.org/lang_corefunc.html#soundex + */ + template + internal::built_in_function_t soundex(X x) { + return {std::tuple{std::forward(x)}}; + } +#endif + + /** + * TOTAL(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t total(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * SUM(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t, internal::sum_string, X> sum(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * COUNT(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t count(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * COUNT(*) without FROM function. + */ + inline internal::count_asterisk_without_type count() { + return {}; + } + + /** + * COUNT(*) with FROM function. Specified type T will be serializeed as + * a from argument. + */ + template + internal::count_asterisk_t count() { + return {}; + } + + /** + * AVG(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t avg(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * MAX(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t, internal::max_string, X> max(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * MIN(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t, internal::min_string, X> min(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * MAX(X, Y, ...) scalar function. + * The return type is the type of the first argument. + */ + template + internal::built_in_function_t, internal::max_string, X, Y, Rest...> + max(X x, Y y, Rest... rest) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; + } + + /** + * MIN(X, Y, ...) scalar function. + * The return type is the type of the first argument. + */ + template + internal::built_in_function_t, internal::min_string, X, Y, Rest...> + min(X x, Y y, Rest... rest) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; + } + + /** + * GROUP_CONCAT(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t group_concat(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * GROUP_CONCAT(X, Y) aggregate function. + */ + template + internal::built_in_aggregate_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, + std::is_base_of>, + bool> = true> + internal::add_t operator+(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template, + std::is_base_of>, + bool> = true> + internal::sub_t operator-(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template, + std::is_base_of>, + bool> = true> + internal::mul_t operator*(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template, + std::is_base_of>, + bool> = true> + internal::div_t operator/(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template, + std::is_base_of>, + bool> = true> + internal::mod_t operator%(L l, R r) { + return {std::move(l), std::move(r)}; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/dbstat.h b/libs/sqlite_orm-1.8.2/dev/dbstat.h new file mode 100644 index 0000000..ccc9d01 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/dbstat.h @@ -0,0 +1,37 @@ +#pragma once + +#include // std::string + +#include "column.h" +#include "table.h" + +namespace sqlite_orm { +#ifdef SQLITE_ENABLE_DBSTAT_VTAB + struct dbstat { + std::string name; + std::string path; + int pageno = 0; + std::string pagetype; + int ncell = 0; + int payload = 0; + int unused = 0; + int mx_payload = 0; + int pgoffset = 0; + int pgsize = 0; + }; + + inline auto make_dbstat_table() { + return make_table("dbstat", + make_column("name", &dbstat::name), + make_column("path", &dbstat::path), + make_column("pageno", &dbstat::pageno), + make_column("pagetype", &dbstat::pagetype), + make_column("ncell", &dbstat::ncell), + make_column("payload", &dbstat::payload), + make_column("unused", &dbstat::unused), + make_column("mx_payload", &dbstat::mx_payload), + make_column("pgoffset", &dbstat::pgoffset), + make_column("pgsize", &dbstat::pgsize)); + } +#endif // SQLITE_ENABLE_DBSTAT_VTAB +} diff --git a/libs/sqlite_orm-1.8.2/dev/default_value_extractor.h b/libs/sqlite_orm-1.8.2/dev/default_value_extractor.h new file mode 100644 index 0000000..ae8d90e --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/default_value_extractor.h @@ -0,0 +1,28 @@ +#pragma once + +#include // std::string + +#include "constraints.h" +#include "serializer_context.h" +#include "storage_lookup.h" + +namespace sqlite_orm { + + namespace internal { + + template + std::string serialize(const T&, const serializer_context&); + + /** + * Serialize default value of a column's default valu + */ + template + std::string serialize_default_value(const default_t& dft) { + db_objects_tuple<> dbObjects; + serializer_context> context{dbObjects}; + return serialize(dft.value, context); + } + + } + +} diff --git a/libs/sqlite_orm-1.8.2/dev/error_code.h b/libs/sqlite_orm-1.8.2/dev/error_code.h new file mode 100644 index 0000000..ece9d1a --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/error_code.h @@ -0,0 +1,169 @@ +#pragma once + +#include +#include // std::error_code, std::system_error +#include // std::string +#include +#include // std::ostringstream +#include + +namespace sqlite_orm { + + /** @short Enables classifying sqlite error codes. + + @note We don't bother listing all possible values; + this also allows for compatibility with + 'Construction rules for enum class values (P0138R2)' + */ + enum class sqlite_errc {}; + + enum class orm_error_code { + not_found = 1, + type_is_not_mapped_to_storage, + trying_to_dereference_null_iterator, + too_many_tables_specified, + incorrect_set_fields_specified, + column_not_found, + table_has_no_primary_key_column, + cannot_start_a_transaction_within_a_transaction, + no_active_transaction, + incorrect_journal_mode_string, + invalid_collate_argument_enum, + 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, + value_is_null, + no_tables_specified, + }; + +} + +namespace std { + template<> + struct is_error_code_enum<::sqlite_orm::sqlite_errc> : true_type {}; + + template<> + struct is_error_code_enum<::sqlite_orm::orm_error_code> : true_type {}; +} + +namespace sqlite_orm { + + class orm_error_category : public std::error_category { + public: + const char* name() const noexcept override final { + return "ORM error"; + } + + std::string message(int c) const override final { + switch(static_cast(c)) { + case orm_error_code::not_found: + return "Not found"; + case orm_error_code::type_is_not_mapped_to_storage: + return "Type is not mapped to storage"; + case orm_error_code::trying_to_dereference_null_iterator: + return "Trying to dereference null iterator"; + case orm_error_code::too_many_tables_specified: + return "Too many tables specified"; + case orm_error_code::incorrect_set_fields_specified: + return "Incorrect set fields specified"; + case orm_error_code::column_not_found: + return "Column not found"; + case orm_error_code::table_has_no_primary_key_column: + return "Table has no primary key column"; + case orm_error_code::cannot_start_a_transaction_within_a_transaction: + return "Cannot start a transaction within a transaction"; + case orm_error_code::no_active_transaction: + return "No active transaction"; + case orm_error_code::invalid_collate_argument_enum: + return "Invalid collate_argument enum"; + case orm_error_code::failed_to_init_a_backup: + return "Failed to init a backup"; + case orm_error_code::unknown_member_value: + 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"; + case orm_error_code::value_is_null: + return "Value is null"; + case orm_error_code::no_tables_specified: + return "No tables specified"; + default: + return "unknown error"; + } + } + }; + + class sqlite_error_category : public std::error_category { + public: + const char* name() const noexcept override final { + return "SQLite error"; + } + + std::string message(int c) const override final { + return sqlite3_errstr(c); + } + }; + + 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() { + static sqlite_error_category res; + return res; + } + + inline std::error_code make_error_code(sqlite_errc ev) noexcept { + return {static_cast(ev), get_sqlite_error_category()}; + } + + inline std::error_code make_error_code(orm_error_code ev) noexcept { + return {static_cast(ev), get_orm_error_category()}; + } + + template + 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)...}); + stream << sqlite3_errmsg(db); + return stream.str(); + } + + template + [[noreturn]] void throw_error(sqlite3* db, T&&... args) { + throw std::system_error{sqlite_errc(sqlite3_errcode(db)), get_error_message(db, std::forward(args)...)}; + } + + inline std::system_error sqlite_to_system_error(int ev) { + return {sqlite_errc(ev)}; + } + + inline std::system_error sqlite_to_system_error(sqlite3* db) { + return {sqlite_errc(sqlite3_errcode(db)), sqlite3_errmsg(db)}; + } + + [[noreturn]] inline void throw_translated_sqlite_error(int ev) { + throw sqlite_to_system_error(ev); + } + + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3* db) { + throw sqlite_to_system_error(db); + } + + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt* stmt) { + throw sqlite_to_system_error(sqlite3_db_handle(stmt)); + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/expression.h b/libs/sqlite_orm-1.8.2/dev/expression.h new file mode 100644 index 0000000..b92fb00 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/expression.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include // std::move, std::forward +#include "functional/cxx_optional.h" + +#include "functional/cxx_universal.h" +#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=(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/libs/sqlite_orm-1.8.2/dev/expression_object_type.h b/libs/sqlite_orm-1.8.2/dev/expression_object_type.h new file mode 100644 index 0000000..3e54c1b --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/expression_object_type.h @@ -0,0 +1,137 @@ +#pragma once + +#include // std::decay +#include // std::reference_wrapper + +#include "prepared_statement.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct expression_object_type; + + template + struct expression_object_type> : std::decay {}; + + template + struct expression_object_type>> : std::decay {}; + + template + struct expression_object_type> : std::decay {}; + + template + struct expression_object_type>> : std::decay {}; + + 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 = T; + }; + + template + struct expression_object_type, Ids...>> { + using type = T; + }; + + template + struct expression_object_type> : std::decay {}; + + template + struct expression_object_type>> : std::decay {}; + + template + struct expression_object_type> { + 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> : std::decay {}; + + template + struct expression_object_type, Cols...>> : std::decay {}; + + template + struct get_ref_t { + + template + auto& operator()(O& t) const { + return t; + } + }; + + template + struct get_ref_t> { + + template + auto& operator()(O& t) const { + return t.get(); + } + }; + + template + auto& get_ref(T& t) { + using arg_type = std::decay_t; + get_ref_t g; + return g(t); + } + + template + struct get_object_t; + + template + struct get_object_t : get_object_t {}; + + template + auto& get_object(T& t) { + using expression_type = std::decay_t; + get_object_t obj; + return obj(t); + } + + template + struct get_object_t> { + using expression_type = replace_t; + + template + auto& operator()(O& e) const { + return get_ref(e.object); + } + }; + + template + struct get_object_t> { + using expression_type = insert_t; + + template + auto& operator()(O& e) const { + return get_ref(e.object); + } + }; + + template + struct get_object_t> { + using expression_type = update_t; + + template + auto& operator()(O& e) const { + return get_ref(e.object); + } + }; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/field_printer.h b/libs/sqlite_orm-1.8.2/dev/field_printer.h new file mode 100644 index 0000000..71d1eb2 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/field_printer.h @@ -0,0 +1,159 @@ +#pragma once + +#include // std::wstring_convert +#include // std::string +#include // std::stringstream +#include // std::vector +#include // std::shared_ptr, std::unique_ptr +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::codecvt_utf8_utf16 +#endif // SQLITE_ORM_OMITS_CODECVT +#include "functional/cxx_optional.h" + +#include "functional/cxx_universal.h" +#include "functional/cxx_type_traits_polyfill.h" +#include "is_std_ptr.h" + +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 + struct field_printer; + + namespace internal { + template + SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v{})>> = true + // Also see implementation note for `is_bindable_v` + ; + template + using is_printable = polyfill::bool_constant>; + } + + template + struct field_printer::value>> { + std::string operator()(const T& t) const { + std::stringstream ss; + ss << t; + return ss.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 ss; + ss << +t; + return ss.str(); + } + }; + + /** + * 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 ss; + ss << +t; + return ss.str(); + } + }; + + /** + * char is neither signed char nor unsigned char so it has its own specialization + */ + template<> + struct field_printer { + std::string operator()(const char& t) const { + std::stringstream ss; + ss << +t; + return ss.str(); + } + }; + + template + struct field_printer::value>> { + std::string operator()(std::string string) const { + return string; + } + }; + + 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(); + } + }; +#ifndef SQLITE_ORM_OMITS_CODECVT + /** + * Specialization for std::wstring (UTF-16 assumed). + */ + template + struct field_printer::value>> { + std::string operator()(const std::wstring& wideString) const { + std::wstring_convert> converter; + return converter.to_bytes(wideString); + } + }; +#endif // SQLITE_ORM_OMITS_CODECVT + template<> + struct field_printer { + std::string operator()(const 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< + T, + std::enable_if_t, + internal::is_printable>>>> { + using unqualified_type = std::remove_cv_t; + + std::string operator()(const T& t) const { + if(t) { + return field_printer()(*t); + } else { + return field_printer{}(nullptr); + } + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct field_printer< + T, + std::enable_if_t, + internal::is_printable>>>> { + using unqualified_type = std::remove_cv_t; + + std::string operator()(const T& t) const { + if(t.has_value()) { + return field_printer()(*t); + } else { + return field_printer{}(std::nullopt); + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} diff --git a/libs/sqlite_orm-1.8.2/dev/function.h b/libs/sqlite_orm-1.8.2/dev/function.h new file mode 100644 index 0000000..8356322 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/function.h @@ -0,0 +1,246 @@ +#pragma once + +#include +#include +#include // std::string +#include // std::tuple +#include // std::function +#include // std::min +#include // std::move, std::forward + +#include "functional/cxx_universal.h" +#include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + + struct arg_values; + + template + struct pointer_arg; + template + class pointer_binding; + + namespace internal { + + struct user_defined_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; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + user_defined_function_base(decltype(name) name_, + decltype(argumentsCount) argumentsCount_, + decltype(create) create_, + decltype(destroy) destroy_) : + name(std::move(name_)), + argumentsCount(argumentsCount_), create(std::move(create_)), destroy(destroy_) {} +#endif + }; + + struct user_defined_scalar_function_t : user_defined_function_base { + func_call run; + + user_defined_scalar_function_t(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(run) run_, + decltype(destroy) destroy_) : + user_defined_function_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, + run(std::move(run_)) {} + }; + + struct user_defined_aggregate_function_t : user_defined_function_base { + func_call step; + final_call finalCall; + + user_defined_aggregate_function_t(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(step) step_, + decltype(finalCall) finalCall_, + decltype(destroy) destroy_) : + user_defined_function_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, + step(std::move(step_)), finalCall(std::move(finalCall_)) {} + }; + + template + using scalar_call_function_t = decltype(&F::operator()); + + template + using aggregate_step_function_t = decltype(&F::step); + + template + using aggregate_fin_function_t = decltype(&F::fin); + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_function_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_function_v>> = + true; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_function_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_function_v< + F, + polyfill::void_t, + aggregate_fin_function_t, + std::enable_if_t>::value>, + std::enable_if_t>::value>>> = + true; + + template + struct member_function_arguments; + + template + struct member_function_arguments { + using member_function_type = R (O::*)(Args...) const; + using tuple_type = std::tuple...>; + using return_type = R; + }; + + template + struct member_function_arguments { + using member_function_type = R (O::*)(Args...); + using tuple_type = std::tuple...>; + using return_type = R; + }; + + template + struct callable_arguments_impl; + + template + struct callable_arguments_impl>> { + using args_tuple = typename member_function_arguments>::tuple_type; + using return_type = typename member_function_arguments>::return_type; + }; + + template + struct callable_arguments_impl>> { + 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 {}; + + template + struct function_call { + using function_type = F; + using args_tuple = std::tuple; + + args_tuple args; + }; + + template + struct unpacked_arg { + using type = T; + }; + template + struct unpacked_arg> { + using type = typename callable_arguments::return_type; + }; + template + using unpacked_arg_t = typename unpacked_arg::type; + + template + SQLITE_ORM_CONSTEVAL bool expected_pointer_value() { + static_assert(polyfill::always_false_v, "Expected a pointer value for I-th argument"); + return false; + } + + template + constexpr bool is_same_pvt_v = expected_pointer_value(); + + // Always allow binding nullptr to a pointer argument + template + constexpr bool is_same_pvt_v> = true; + +#if __cplusplus >= 201703L // C++17 or later + template + SQLITE_ORM_CONSTEVAL bool assert_same_pointer_type() { + constexpr bool valid = Binding == PointerArg; + static_assert(valid, "Pointer value types of I-th argument do not match"); + return valid; + } + + template + constexpr bool + is_same_pvt_v> = + assert_same_pointer_type(); +#else + template + SQLITE_ORM_CONSTEVAL bool assert_same_pointer_type() { + constexpr bool valid = Binding::value == PointerArg::value; + static_assert(valid, "Pointer value types of I-th argument do not match"); + return valid; + } + + template + constexpr bool + is_same_pvt_v> = + assert_same_pointer_type(); +#endif + + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::false_type) { + return true; + } + + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::true_type) { + return is_same_pvt_v; + } + + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_types(polyfill::index_constant) { + return true; + } + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_types(polyfill::index_constant) { + using func_arg_t = std::tuple_element_t; + using passed_arg_t = unpacked_arg_t>; + +#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED + constexpr bool valid = validate_pointer_value_type, + unpacked_arg_t>>( + polyfill::bool_constant < (polyfill::is_specialization_of_v) || + (polyfill::is_specialization_of_v) > {}); + + return validate_pointer_value_types(polyfill::index_constant{}) && valid; +#else + return validate_pointer_value_types(polyfill::index_constant{}) && + validate_pointer_value_type, + unpacked_arg_t>>( + polyfill::bool_constant < (polyfill::is_specialization_of_v) || + (polyfill::is_specialization_of_v) > {}); +#endif + } + } + + /** + * 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 && + internal::validate_pointer_value_types( + polyfill::index_constant(functionArgsCount, argsCount) - 1>{})) || + std::is_same>::value, + "Number of arguments does not match"); + return {std::make_tuple(std::forward(args)...)}; + } + +} diff --git a/libs/sqlite_orm-1.8.2/dev/functional/config.h b/libs/sqlite_orm-1.8.2/dev/functional/config.h new file mode 100644 index 0000000..7bb1c25 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/functional/config.h @@ -0,0 +1,21 @@ +#pragma once + +#include "cxx_universal.h" + +#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +#define SQLITE_ORM_INLINE_VAR inline +#else +#define SQLITE_ORM_INLINE_VAR +#endif + +#if SQLITE_ORM_HAS_CPP_ATTRIBUTE(no_unique_address) >= 201803L +#define SQLITE_ORM_NOUNIQUEADDRESS [[no_unique_address]] +#else +#define SQLITE_ORM_NOUNIQUEADDRESS +#endif + +#ifdef SQLITE_ORM_CONSTEVAL_SUPPORTED +#define SQLITE_ORM_CONSTEVAL consteval +#else +#define SQLITE_ORM_CONSTEVAL constexpr +#endif diff --git a/libs/sqlite_orm-1.8.2/dev/functional/cxx_compiler_quirks.h b/libs/sqlite_orm-1.8.2/dev/functional/cxx_compiler_quirks.h new file mode 100644 index 0000000..7ddb5e7 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/functional/cxx_compiler_quirks.h @@ -0,0 +1,42 @@ +#pragma once + +/* + * This header defines macros for circumventing compiler quirks on which sqlite_orm depends. + * May amend cxx_core_features.h + */ + +#ifdef __clang__ +#define SQLITE_ORM_DO_PRAGMA(...) _Pragma(#__VA_ARGS__) +#endif + +#ifdef __clang__ +#define SQLITE_ORM_CLANG_SUPPRESS(warnoption, ...) \ + SQLITE_ORM_DO_PRAGMA(clang diagnostic push) \ + SQLITE_ORM_DO_PRAGMA(clang diagnostic ignored warnoption) \ + __VA_ARGS__ \ + SQLITE_ORM_DO_PRAGMA(clang diagnostic pop) + +#else +#define SQLITE_ORM_CLANG_SUPPRESS(warnoption, ...) __VA_ARGS__ +#endif + +// clang has the bad habit of diagnosing missing brace-init-lists when constructing aggregates with base classes. +// This is a false positive, since the C++ standard is quite clear that braces for nested or base objects may be omitted, +// see https://en.cppreference.com/w/cpp/language/aggregate_initialization: +// "The braces around the nested initializer lists may be elided (omitted), +// in which case as many initializer clauses as necessary are used to initialize every member or element of the corresponding subaggregate, +// and the subsequent initializer clauses are used to initialize the following members of the object." +// In this sense clang should only warn about missing field initializers. +// Because we know what we are doing, we suppress the diagnostic message +#define SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(...) SQLITE_ORM_CLANG_SUPPRESS("-Wmissing-braces", __VA_ARGS__) + +#if defined(_MSC_VER) && (_MSC_VER < 1920) +#define SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION +#endif + +// clang 10 chokes on concepts that don't depend on template parameters; +// when it tries to instantiate an expression in a requires expression, which results in an error, +// the compiler reports an error instead of dismissing the templated function. +#if defined(SQLITE_ORM_CONCEPTS_SUPPORTED) && (defined(__clang__) && (__clang_major__ == 10)) +#define SQLITE_ORM_BROKEN_NONTEMPLATE_CONCEPTS +#endif diff --git a/libs/sqlite_orm-1.8.2/dev/functional/cxx_core_features.h b/libs/sqlite_orm-1.8.2/dev/functional/cxx_core_features.h new file mode 100644 index 0000000..c837493 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/functional/cxx_core_features.h @@ -0,0 +1,67 @@ +#pragma once + +/* + * This header detects core C++ language features on which sqlite_orm depends. + * May be updated/overwritten by cxx_compiler_quirks.h + */ + +#ifdef __has_cpp_attribute +#define SQLITE_ORM_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr) +#else +#define SQLITE_ORM_HAS_CPP_ATTRIBUTE(attr) 0L +#endif + +#ifdef __has_include +#define SQLITE_ORM_HAS_INCLUDE(file) __has_include(file) +#else +#define SQLITE_ORM_HAS_INCLUDE(file) 0L +#endif + +#if __cpp_aggregate_nsdmi >= 201304L +#define SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED +#endif + +#if __cpp_constexpr >= 201304L +#define SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED +#endif + +#if __cpp_noexcept_function_type >= 201510L +#define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED +#endif + +#if __cpp_aggregate_bases >= 201603L +#define SQLITE_ORM_AGGREGATE_BASES_SUPPORTED +#endif + +#if __cpp_fold_expressions >= 201603L +#define SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED +#endif + +#if __cpp_inline_variables >= 201606L +#define SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +#endif + +#if __cpp_if_constexpr >= 201606L +#define SQLITE_ORM_IF_CONSTEXPR_SUPPORTED +#endif + +#if __cpp_inline_variables >= 201606L +#define SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +#endif + +#if __cpp_generic_lambdas >= 201707L +#define SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED +#else +#endif + +#if __cpp_consteval >= 201811L +#define SQLITE_ORM_CONSTEVAL_SUPPORTED +#endif + +#if __cpp_aggregate_paren_init >= 201902L +#define SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED +#endif + +#if __cpp_concepts >= 201907L +#define SQLITE_ORM_CONCEPTS_SUPPORTED +#endif diff --git a/libs/sqlite_orm-1.8.2/dev/functional/cxx_functional_polyfill.h b/libs/sqlite_orm-1.8.2/dev/functional/cxx_functional_polyfill.h new file mode 100644 index 0000000..5380f2f --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/functional/cxx_functional_polyfill.h @@ -0,0 +1,82 @@ +#pragma once +#include +#if __cpp_lib_invoke < 201411L +#include // std::enable_if, std::is_member_object_pointer, std::is_member_function_pointer +#endif +#include // std::forward + +#if __cpp_lib_invoke < 201411L +#include "cxx_type_traits_polyfill.h" +#endif +#include "../member_traits/member_traits.h" + +namespace sqlite_orm { + namespace internal { + namespace polyfill { + // C++20 or later (unfortunately there's no feature test macro). + // Stupidly, clang says C++20, but `std::identity` was only implemented in libc++ 13 and libstd++-v3 10 + // (the latter is used on Linux). + // gcc got it right and reports C++20 only starting with v10. + // The check here doesn't care and checks the library versions in use. + // + // Another way of detection would be the constrained algorithms feature macro __cpp_lib_ranges +#if(__cplusplus >= 202002L) && \ + ((!_LIBCPP_VERSION || _LIBCPP_VERSION >= 13) && (!_GLIBCXX_RELEASE || _GLIBCXX_RELEASE >= 10)) + using std::identity; +#else + struct identity { + template + constexpr T&& operator()(T&& v) const noexcept { + return std::forward(v); + } + + using is_transparent = int; + }; +#endif + +#if __cpp_lib_invoke >= 201411L + using std::invoke; +#else + // pointer-to-data-member+object + template, + std::enable_if_t::value, bool> = true> + decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { + return std::forward(object).*callable; + } + + // pointer-to-member-function+object + template, + std::enable_if_t::value, bool> = true> + decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { + return (std::forward(object).*callable)(std::forward(args)...); + } + + // pointer-to-member+reference-wrapped object (expect `reference_wrapper::*`) + template>, + std::reference_wrapper>>, + bool> = true> + decltype(auto) invoke(Callable&& callable, std::reference_wrapper wrapper, Args&&... args) { + return invoke(std::forward(callable), wrapper.get(), std::forward(args)...); + } + + // functor + template + decltype(auto) invoke(Callable&& callable, Args&&... args) { + return std::forward(callable)(std::forward(args)...); + } +#endif + } + } + + namespace polyfill = internal::polyfill; +} diff --git a/libs/sqlite_orm-1.8.2/dev/functional/cxx_optional.h b/libs/sqlite_orm-1.8.2/dev/functional/cxx_optional.h new file mode 100644 index 0000000..627a4fb --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/functional/cxx_optional.h @@ -0,0 +1,11 @@ +#pragma once + +#include "cxx_core_features.h" + +#if SQLITE_ORM_HAS_INCLUDE() +#include +#endif + +#if __cpp_lib_optional >= 201606L +#define SQLITE_ORM_OPTIONAL_SUPPORTED +#endif diff --git a/libs/sqlite_orm-1.8.2/dev/functional/cxx_string_view.h b/libs/sqlite_orm-1.8.2/dev/functional/cxx_string_view.h new file mode 100644 index 0000000..9fedcce --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/functional/cxx_string_view.h @@ -0,0 +1,11 @@ +#pragma once + +#include "cxx_core_features.h" + +#if SQLITE_ORM_HAS_INCLUDE() +#include +#endif + +#if __cpp_lib_string_view >= 201606L +#define SQLITE_ORM_STRING_VIEW_SUPPORTED +#endif diff --git a/libs/sqlite_orm-1.8.2/dev/functional/cxx_type_traits_polyfill.h b/libs/sqlite_orm-1.8.2/dev/functional/cxx_type_traits_polyfill.h new file mode 100644 index 0000000..ab839f5 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/functional/cxx_type_traits_polyfill.h @@ -0,0 +1,145 @@ +#pragma once +#include + +#include "cxx_universal.h" + +namespace sqlite_orm { + namespace internal { + namespace polyfill { +#if __cpp_lib_void_t >= 201411L + using std::void_t; +#else + template + using void_t = void; +#endif + +#if __cpp_lib_bool_constant >= 201505L + using std::bool_constant; +#else + template + using bool_constant = std::integral_constant; +#endif + +#if __cpp_lib_logical_traits >= 201510L && __cpp_lib_type_trait_variable_templates >= 201510L + using std::conjunction; + using std::conjunction_v; + using std::disjunction; + using std::disjunction_v; + using std::negation; + using std::negation_v; +#else + template + struct conjunction : std::true_type {}; + template + struct conjunction : B1 {}; + template + struct conjunction : std::conditional_t, B1> {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool conjunction_v = conjunction::value; + + template + struct disjunction : std::false_type {}; + template + struct disjunction : B1 {}; + template + struct disjunction : std::conditional_t> {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool disjunction_v = disjunction::value; + + template + struct negation : bool_constant {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool negation_v = negation::value; +#endif + +#if __cpp_lib_remove_cvref >= 201711L + using std::remove_cvref, std::remove_cvref_t; +#else + template + struct remove_cvref : std::remove_cv> {}; + + template + using remove_cvref_t = typename remove_cvref::type; +#endif + +#if __cpp_lib_type_identity >= 201806L + using std::type_identity, std::type_identity_t; +#else + template + struct type_identity { + using type = T; + }; + + template + using type_identity_t = typename type_identity::type; +#endif + +#if 0 // __cpp_lib_detect >= 0L // library fundamentals TS v2, [meta.detect] + using std::nonesuch; + using std::detector; + using std::is_detected, std::is_detected_v; + using std::detected, std::detected_t; + using std::detected_or, std::detected_or_t; +#else + struct nonesuch { + ~nonesuch() = delete; + nonesuch(const nonesuch&) = delete; + void operator=(const nonesuch&) = delete; + }; + + template class Op, class... Args> + struct detector { + using value_t = std::false_type; + using type = Default; + }; + + template class Op, class... Args> + struct detector>, Op, Args...> { + using value_t = std::true_type; + using type = Op; + }; + + template class Op, class... Args> + using is_detected = typename detector::value_t; + + template class Op, class... Args> + using detected = detector; + + template class Op, class... Args> + using detected_t = typename detector::type; + + template class Op, class... Args> + using detected_or = detector; + + template class Op, class... Args> + using detected_or_t = typename detected_or::type; + + template class Op, class... Args> + SQLITE_ORM_INLINE_VAR constexpr bool is_detected_v = is_detected::value; +#endif + +#if 0 // proposed but not pursued + using std::is_specialization_of, std::is_specialization_of_t, std::is_specialization_of_v; +#else + // is_specialization_of: https://github.com/cplusplus/papers/issues/812 + + template class Primary> + SQLITE_ORM_INLINE_VAR constexpr bool is_specialization_of_v = false; + + template class Primary, class... Types> + SQLITE_ORM_INLINE_VAR constexpr bool is_specialization_of_v, Primary> = true; + + template class Primary> + struct is_specialization_of : bool_constant> {}; +#endif + + template + SQLITE_ORM_INLINE_VAR constexpr bool always_false_v = false; + + template + using index_constant = std::integral_constant; + } + } + + namespace polyfill = internal::polyfill; +} diff --git a/libs/sqlite_orm-1.8.2/dev/functional/cxx_universal.h b/libs/sqlite_orm-1.8.2/dev/functional/cxx_universal.h new file mode 100644 index 0000000..dd2b4dc --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/functional/cxx_universal.h @@ -0,0 +1,20 @@ +#pragma once + +/* + * This header makes central C++ functionality on which sqlite_orm depends universally available: + * - alternative operator representations + * - ::size_t, ::ptrdiff_t, ::nullptr_t + * - C++ core language feature macros + * - macros for dealing with compiler quirks + */ + +#include // alternative operator representations +#include // sqlite_orm is using size_t, ptrdiff_t, nullptr_t everywhere, pull it in early + +// earlier clang versions didn't make nullptr_t available in the global namespace via stddef.h, +// though it should have according to C++ documentation (see https://en.cppreference.com/w/cpp/types/nullptr_t#Notes). +// actually it should be available when including stddef.h +using std::nullptr_t; + +#include "cxx_core_features.h" +#include "cxx_compiler_quirks.h" diff --git a/libs/sqlite_orm-1.8.2/dev/functional/finish_macros.h b/libs/sqlite_orm-1.8.2/dev/functional/finish_macros.h new file mode 100644 index 0000000..ecb4d90 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/functional/finish_macros.h @@ -0,0 +1,6 @@ +#pragma once + +#if defined(_MSC_VER) +__pragma(pop_macro("max")) +__pragma(pop_macro("min")) +#endif // defined(_MSC_VER) diff --git a/libs/sqlite_orm-1.8.2/dev/functional/index_sequence_util.h b/libs/sqlite_orm-1.8.2/dev/functional/index_sequence_util.h new file mode 100644 index 0000000..df20396 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/functional/index_sequence_util.h @@ -0,0 +1,34 @@ +#pragma once + +#include // std::index_sequence, std::make_index_sequence + +#include "../functional/cxx_universal.h" + +namespace sqlite_orm { + namespace internal { + /** + * Get the first value of an index_sequence. + */ + template + SQLITE_ORM_CONSTEVAL size_t first_index_sequence_value(std::index_sequence) { + return I; + } + + template + struct flatten_idxseq { + using type = std::index_sequence<>; + }; + + template + struct flatten_idxseq> { + using type = std::index_sequence; + }; + + template + struct flatten_idxseq, std::index_sequence, Seq...> + : flatten_idxseq, Seq...> {}; + + template + using flatten_idxseq_t = typename flatten_idxseq::type; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/functional/mpl.h b/libs/sqlite_orm-1.8.2/dev/functional/mpl.h new file mode 100644 index 0000000..55d3cd0 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/functional/mpl.h @@ -0,0 +1,306 @@ +#pragma once + +/* + * Symbols for 'template metaprogramming' (compile-time template programming), + * inspired by the MPL of Aleksey Gurtovoy and David Abrahams. + * + * Currently, the focus is on facilitating advanced type filtering, + * such as filtering columns by constraints having various traits. + * Hence it contains only a very small subset of a full MPL. + * + * Two key concepts are critical to understanding: + * 1. A 'metafunction' is a class template that represents a function invocable at compile-time. + * 2. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming. + * More precisely, it's a class with a nested metafunction called "fn" + * Correspondingly, a metafunction class invocation is defined as invocation of its nested "fn" metafunction. + * 3. A 'metafunction operation' is an alias template that represents a function whose instantiation already yields a type. + * + * Conventions: + * - "Fn" is the name for a metafunction template template parameter. + * - "FnCls" is the name for a metafunction class template parameter. + * - "_fn" is a suffix for a type that accepts metafunctions and turns them into metafunction classes. + * - "higher order" denotes a metafunction that operates on another metafunction (i.e. takes it as an argument). + */ + +#include // std::false_type, std::true_type + +#include "cxx_universal.h" +#include "cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { + namespace mpl { + template class Fn> + struct indirectly_test_metafunction; + + /* + * Determines whether a class template has a nested metafunction `fn`. + * + * Implementation note: the technique of specialiazing on the inline variable must come first because + * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_metafunction_class_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_metafunction_class_v>> = + true; + + template + struct is_metafunction_class : polyfill::bool_constant> {}; + + /* + * Invoke metafunction. + */ + template class Fn, class... Args> + using invoke_fn_t = typename Fn::type; + +#ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template class Op, class... Args> + struct wrap_op { + using type = Op; + }; + + /* + * Invoke metafunction operation. + * + * Note: legacy compilers need an extra layer of indirection, otherwise type replacement may fail + * if alias template `Op` has a dependent expression in it. + */ + template class Op, class... Args> + using invoke_op_t = typename wrap_op::type; +#else + /* + * Invoke metafunction operation. + */ + template class Op, class... Args> + using invoke_op_t = Op; +#endif + + /* + * Invoke metafunction class by invoking its nested metafunction. + */ + template + using invoke_t = typename FnCls::template fn::type; + + /* + * Instantiate metafunction class' nested metafunction. + */ + template + using instantiate = typename FnCls::template fn; + + /* + * Wrap given type such that `typename T::type` is valid. + */ + template + struct type_wrap : polyfill::type_identity {}; + template + struct type_wrap> : T {}; + + /* + * Turn metafunction into a metafunction class. + * + * Invocation of the nested metafunction `fn` is SFINAE-friendly (detection idiom). + * This is necessary because `fn` is a proxy to the originally quoted metafunction, + * and the instantiation of the metafunction might be an invalid expression. + */ + template class Fn> + struct quote_fn { + template class, class...> + struct invoke_fn; + + template class F, class... Args> + struct invoke_fn>, F, Args...> { + using type = type_wrap>; + }; + + template + using fn = typename invoke_fn::type; + }; + + /* + * Indirection wrapper for higher-order metafunctions, + * specialized on the argument indexes where metafunctions appear. + */ + template + struct higherorder; + + template<> + struct higherorder<0u> { + /* + * Turn higher-order metafunction into a metafunction class. + */ + template class Fn, class... Args2> class HigherFn> + struct quote_fn { + template + struct fn : HigherFn {}; + }; + }; + + /* + * Metafunction class that extracts the nested metafunction of its metafunction class argument, + * quotes the extracted metafunction and passes it on to the next metafunction class + * (kind of the inverse of quoting). + */ + template + struct pass_extracted_fn_to { + template + struct fn : FnCls::template fn {}; + + // extract, quote, pass on + template class Fn, class... Args> + struct fn> : FnCls::template fn> {}; + }; + + /* + * Metafunction class that invokes the specified metafunction operation, + * and passes its result on to the next metafunction class. + */ + template class Op, class FnCls> + struct pass_result_to { + // call Op, pass on its result + template + struct fn : FnCls::template fn> {}; + }; + + /* + * Bind arguments at the front of a metafunction class. + * Metafunction class equivalent to std::bind_front(). + */ + template + struct bind_front { + template + struct fn : FnCls::template fn {}; + }; + + /* + * Bind arguments at the back of a metafunction class. + * Metafunction class equivalent to std::bind_back() + */ + template + struct bind_back { + template + struct fn : FnCls::template fn {}; + }; + + /* + * Metafunction class equivalent to polyfill::always_false. + * It ignores arguments passed to the metafunction, + * and always returns the given type. + */ + template + struct always { + template + struct fn : type_wrap {}; + }; + + /* + * Unary metafunction class equivalent to std::type_identity. + */ + struct identity { + template + struct fn : type_wrap {}; + }; + + /* + * Metafunction class equivalent to std::negation. + */ + template + struct not_ { + template + struct fn : polyfill::negation> {}; + }; + + /* + * Metafunction class equivalent to std::conjunction + */ + template + struct conjunction { + template + struct fn : polyfill::conjunction...> {}; + }; + + /* + * Metafunction class equivalent to std::disjunction. + */ + template + struct disjunction { + template + struct fn : polyfill::disjunction...> {}; + }; + +#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + /* + * Metafunction equivalent to std::conjunction. + */ + template class... TraitFn> + using conjunction_fn = conjunction...>; + + /* + * Metafunction equivalent to std::disjunction. + */ + template class... TraitFn> + using disjunction_fn = disjunction...>; +#else + template class... TraitFn> + struct conjunction_fn : conjunction...> {}; + + template class... TraitFn> + struct disjunction_fn : disjunction...> {}; +#endif + + /* + * Convenience template alias for binding arguments at the front of a metafunction. + */ + template class Fn, class... Bound> + using bind_front_fn = bind_front, Bound...>; + + /* + * Convenience template alias for binding arguments at the back of a metafunction. + */ + template class Fn, class... Bound> + using bind_back_fn = bind_back, Bound...>; + + /* + * Convenience template alias for binding a metafunction at the front of a higher-order metafunction. + */ + template class Fn, class... Args2> class HigherFn, + template + class BoundFn, + class... Bound> + using bind_front_higherorder_fn = + bind_front::quote_fn, quote_fn, Bound...>; + } + } + + namespace mpl = internal::mpl; + + // convenience metafunction classes + namespace internal { + /* + * Trait metafunction class that checks if a type has the specified trait. + */ + template class TraitFn> + using check_if = mpl::quote_fn; + + /* + * Trait metafunction class that checks if a type doesn't have the specified trait. + */ + template class TraitFn> + using check_if_not = mpl::not_>; + + /* + * Trait metafunction class that checks if a type is the same as the specified type. + */ + template + using check_if_is_type = mpl::bind_front_fn; + + /* + * Trait metafunction class that checks if a type's template matches the specified template + * (similar to `is_specialization_of`). + */ + template class Template> + using check_if_is_template = + mpl::pass_extracted_fn_to>>; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/functional/start_macros.h b/libs/sqlite_orm-1.8.2/dev/functional/start_macros.h new file mode 100644 index 0000000..1874f02 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/functional/start_macros.h @@ -0,0 +1,8 @@ +#pragma once + +#if defined(_MSC_VER) +__pragma(push_macro("min")) +#undef min +__pragma(push_macro("max")) +#undef max +#endif // defined(_MSC_VER) diff --git a/libs/sqlite_orm-1.8.2/dev/functional/static_magic.h b/libs/sqlite_orm-1.8.2/dev/functional/static_magic.h new file mode 100644 index 0000000..9801072 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/functional/static_magic.h @@ -0,0 +1,75 @@ +#pragma once + +#ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED +#include // std::false_type, std::true_type, std::integral_constant +#endif +#include // std::forward + +namespace sqlite_orm { + + // got from here + // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co + namespace internal { + + template + decltype(auto) empty_callable() { + static auto res = [](auto&&...) -> R { + return R(); + }; + return (res); + } + +#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + template + decltype(auto) static_if([[maybe_unused]] T&& trueFn, [[maybe_unused]] F&& falseFn) { + if constexpr(B) { + return std::forward(trueFn); + } else { + return std::forward(falseFn); + } + } + + template + decltype(auto) static_if([[maybe_unused]] T&& trueFn) { + if constexpr(B) { + return std::forward(trueFn); + } else { + return empty_callable(); + } + } + + template + void call_if_constexpr([[maybe_unused]] L&& lambda, [[maybe_unused]] Args&&... args) { + if constexpr(B) { + lambda(std::forward(args)...); + } + } +#else + template + decltype(auto) static_if(std::true_type, T&& trueFn, const F&) { + return std::forward(trueFn); + } + + template + decltype(auto) static_if(std::false_type, const T&, F&& falseFn) { + return std::forward(falseFn); + } + + template + decltype(auto) static_if(T&& trueFn, F&& falseFn) { + return static_if(std::integral_constant{}, std::forward(trueFn), std::forward(falseFn)); + } + + template + decltype(auto) static_if(T&& trueFn) { + return static_if(std::integral_constant{}, std::forward(trueFn), empty_callable()); + } + + template + void call_if_constexpr(L&& lambda, Args&&... args) { + static_if(std::forward(lambda))(std::forward(args)...); + } +#endif + } + +} diff --git a/libs/sqlite_orm-1.8.2/dev/get_prepared_statement.h b/libs/sqlite_orm-1.8.2/dev/get_prepared_statement.h new file mode 100644 index 0000000..dbd415d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/get_prepared_statement.h @@ -0,0 +1,175 @@ +#pragma once + +#include // std::is_same, std::decay, std::remove_reference +#include // std::get + +#include "functional/cxx_universal.h" // ::size_t +#include "functional/static_magic.h" +#include "prepared_statement.h" +#include "ast_iterator.h" +#include "node_tuple.h" +#include "expression_object_type.h" + +namespace sqlite_orm { + + template + auto& get(internal::prepared_statement_t>& statement) { + return std::get(statement.expression.range); + } + + template + const auto& get(const internal::prepared_statement_t>& statement) { + return std::get(statement.expression.range); + } + + template + auto& get(internal::prepared_statement_t>& statement) { + return std::get(statement.expression.range); + } + + template + const auto& get(const internal::prepared_statement_t>& statement) { + return std::get(statement.expression.range); + } + + template + auto& get(internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } + + template + const auto& get(const internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } + + template + auto& get(internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } + + template + const auto& get(const internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + auto& get(internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } + + template + const auto& get(const internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + auto& get(internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } + + template + const auto& get(const internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } + + template + 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.expression.object); + } + + template + 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.expression.object); + } + + template + 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.expression.obj); + } + + template + 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.expression.obj); + } + + template + 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.expression.object); + } + + template + 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.expression.object); + } + + template + 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.expression.object); + } + + template + 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.expression.object); + } + + template + const auto& get(const internal::prepared_statement_t& statement) { + using statement_type = std::decay_t; + using expression_type = typename statement_type::expression_type; + using node_tuple = internal::node_tuple_t; + using bind_tuple = internal::bindable_filter_t; + using result_type = std::tuple_element_t(N), bind_tuple>; + const result_type* result = nullptr; + internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { + using node_type = std::decay_t; + if(internal::is_bindable_v) { + ++index; + } + if(index == N) { + internal::call_if_constexpr::value>( + [](auto& r, auto& n) { + r = &n; + }, + result, + node); + } + }); + return internal::get_ref(*result); + } + + template + auto& get(internal::prepared_statement_t& statement) { + using statement_type = std::decay_t; + using expression_type = typename statement_type::expression_type; + using node_tuple = internal::node_tuple_t; + using bind_tuple = internal::bindable_filter_t; + using result_type = std::tuple_element_t(N), bind_tuple>; + result_type* result = nullptr; + + internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { + using node_type = std::decay_t; + if(internal::is_bindable_v) { + ++index; + } + if(index == N) { + internal::call_if_constexpr::value>( + [](auto& r, auto& n) { + r = const_cast>(&n); + }, + result, + node); + } + }); + return internal::get_ref(*result); + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/implementations/column_definitions.h b/libs/sqlite_orm-1.8.2/dev/implementations/column_definitions.h new file mode 100644 index 0000000..e98d99d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/implementations/column_definitions.h @@ -0,0 +1,38 @@ +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * this file is also used to provide definitions of interface methods 'hitting the database'. + */ +#pragma once + +#include // std::make_unique + +#include "../functional/cxx_core_features.h" +#include "../functional/static_magic.h" +#include "../functional/index_sequence_util.h" +#include "../tuple_helper/tuple_filter.h" +#include "../tuple_helper/tuple_traits.h" +#include "../default_value_extractor.h" +#include "../column.h" + +namespace sqlite_orm { + namespace internal { + + template + std::unique_ptr column_constraints::default_value() const { + using default_op_index_sequence = + filter_tuple_sequence_t::template fn>; + + std::unique_ptr value; + call_if_constexpr( + [&value](auto& constraints, auto op_index_sequence) { + using default_op_index_sequence = decltype(op_index_sequence); + constexpr size_t opIndex = first_index_sequence_value(default_op_index_sequence{}); + value = std::make_unique(serialize_default_value(get(constraints))); + }, + this->constraints, + default_op_index_sequence{}); + return value; + } + + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/implementations/storage_definitions.h b/libs/sqlite_orm-1.8.2/dev/implementations/storage_definitions.h new file mode 100644 index 0000000..62cdfb9 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/implementations/storage_definitions.h @@ -0,0 +1,144 @@ +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * this file is also used to separate implementation details from the main header file, + * e.g. usage of the dbstat table. + */ +#pragma once +#include // std::is_same +#include +#include // std::reference_wrapper, std::cref +#include // std::find_if, std::ranges::find + +#include "../dbstat.h" +#include "../type_traits.h" +#include "../util.h" +#include "../serializing_util.h" +#include "../storage.h" + +namespace sqlite_orm { + namespace internal { + + template + template> + sync_schema_result storage_t::sync_table(const Table& table, sqlite3* db, bool preserve) { +#ifdef SQLITE_ENABLE_DBSTAT_VTAB + if(std::is_same, dbstat>::value) { + return sync_schema_result::already_in_sync; + } +#endif // SQLITE_ENABLE_DBSTAT_VTAB + auto res = sync_schema_result::already_in_sync; + bool attempt_to_preserve = true; + + auto schema_stat = this->schema_status(table, db, preserve, &attempt_to_preserve); + if(schema_stat != sync_schema_result::already_in_sync) { + if(schema_stat == sync_schema_result::new_table_created) { + this->create_table(db, table.name, table); + res = sync_schema_result::new_table_created; + } else { + if(schema_stat == sync_schema_result::old_columns_removed || + schema_stat == sync_schema_result::new_columns_added || + schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { + + // get table info provided in `make_table` call.. + auto storageTableInfo = table.get_table_info(); + + // now get current table info from db using `PRAGMA table_xinfo` query.. + auto dbTableInfo = this->pragma.table_xinfo(table.name); // should include generated columns + + // this vector will contain pointers to columns that gotta be added.. + std::vector columnsToAdd; + + this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + + if(schema_stat == sync_schema_result::old_columns_removed) { +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + for(auto& tableInfo: dbTableInfo) { + this->drop_column(db, table.name, tableInfo.name); + } + res = sync_schema_result::old_columns_removed; +#else + // extra table columns than storage columns + this->backup_table(db, table, {}); + res = sync_schema_result::old_columns_removed; +#endif + } + + if(schema_stat == sync_schema_result::new_columns_added) { + for(const table_xinfo* colInfo: columnsToAdd) { + table.for_each_column([this, colInfo, &tableName = table.name, db](auto& column) { + if(column.name != colInfo->name) { + return; + } + this->add_column(db, tableName, column); + }); + } + res = sync_schema_result::new_columns_added; + } + + if(schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { + + auto storageTableInfo = table.get_table_info(); + this->add_generated_cols(columnsToAdd, storageTableInfo); + + // remove extra columns and generated columns + this->backup_table(db, table, columnsToAdd); + res = sync_schema_result::new_columns_added_and_old_columns_removed; + } + } else if(schema_stat == sync_schema_result::dropped_and_recreated) { + // now get current table info from db using `PRAGMA table_xinfo` query.. + auto dbTableInfo = this->pragma.table_xinfo(table.name); // should include generated columns + auto storageTableInfo = table.get_table_info(); + + // this vector will contain pointers to columns that gotta be added.. + std::vector columnsToAdd; + + this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + + this->add_generated_cols(columnsToAdd, storageTableInfo); + + if(preserve && attempt_to_preserve) { + this->backup_table(db, table, columnsToAdd); + } else { + this->drop_create_with_loss(db, table); + } + res = schema_stat; + } + } + } + return res; + } + + template + template + void storage_t::copy_table( + sqlite3* db, + const std::string& sourceTableName, + const std::string& destinationTableName, + const Table& table, + const std::vector& columnsToIgnore) const { // must ignore generated columns + std::vector> columnNames; + columnNames.reserve(table.count_columns_amount()); + table.for_each_column([&columnNames, &columnsToIgnore](const column_identifier& column) { + auto& columnName = column.name; +#if __cpp_lib_ranges >= 201911L + auto columnToIgnoreIt = std::ranges::find(columnsToIgnore, columnName, &table_xinfo::name); +#else + auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), + columnsToIgnore.end(), + [&columnName](const table_xinfo* tableInfo) { + return columnName == tableInfo->name; + }); +#endif + if(columnToIgnoreIt == columnsToIgnore.end()) { + columnNames.push_back(cref(columnName)); + } + }); + + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(destinationTableName) << " (" + << streaming_identifiers(columnNames) << ") " + << "SELECT " << streaming_identifiers(columnNames) << " FROM " << streaming_identifier(sourceTableName) + << std::flush; + perform_void_exec(db, ss.str()); + } + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/implementations/table_definitions.h b/libs/sqlite_orm-1.8.2/dev/implementations/table_definitions.h new file mode 100644 index 0000000..3634cc8 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/implementations/table_definitions.h @@ -0,0 +1,53 @@ +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * this file is also used to provide definitions of interface methods 'hitting the database'. + */ +#pragma once +#include // std::decay_t +#include // std::move +#include // std::find_if, std::ranges::find + +#include "../type_printer.h" +#include "../column.h" +#include "../table.h" + +namespace sqlite_orm { + namespace internal { + + template + std::vector table_t::get_table_info() const { + std::vector res; + res.reserve(size_t(filter_tuple_sequence_t::size())); + this->for_each_column([&res](auto& column) { + using field_type = field_type_t>; + std::string dft; + if(auto d = column.default_value()) { + dft = std::move(*d); + } + res.emplace_back(-1, + column.name, + type_printer().print(), + column.is_not_null(), + dft, + column.template is(), + column.is_generated()); + }); + auto compositeKeyColumnNames = this->composite_key_columns_names(); + for(size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { + auto& columnName = compositeKeyColumnNames[i]; +#if __cpp_lib_ranges >= 201911L + auto it = std::ranges::find(res, columnName, &table_xinfo::name); +#else + auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_xinfo& ti) { + return ti.name == columnName; + }); +#endif + if(it != res.end()) { + it->pk = static_cast(i + 1); + } + } + return res; + } + + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/index.h b/libs/sqlite_orm-1.8.2/dev/index.h new file mode 100644 index 0000000..efa5a9f --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/index.h @@ -0,0 +1,71 @@ +#pragma once + +#include // std::tuple, std::make_tuple, std::declval, std::tuple_element_t +#include // std::string +#include // std::forward + +#include "functional/cxx_universal.h" +#include "tuple_helper/tuple_filter.h" +#include "indexed_column.h" +#include "table_type_of.h" + +namespace sqlite_orm { + + namespace internal { + + struct index_base { + std::string name; + bool unique = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + index_base(std::string name, bool unique) : name{std::move(name)}, unique{unique} {} +#endif + }; + + template + struct index_t : index_base { + using elements_type = std::tuple; + using object_type = void; + using table_mapped_type = T; + +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + index_t(std::string name_, bool unique_, elements_type elements_) : + index_base{std::move(name_), unique_}, elements(std::move(elements_)) {} +#endif + + elements_type elements; + }; + } + + template + internal::index_t()))...> make_index(std::string name, + Cols... cols) { + using cols_tuple = std::tuple; + static_assert(internal::count_tuple::value <= 1, + "amount of where arguments can be 0 or 1"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), false, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); + } + + template + internal::index_t>>, + decltype(internal::make_indexed_column(std::declval()))...> + make_index(std::string name, Cols... cols) { + using cols_tuple = std::tuple; + static_assert(internal::count_tuple::value <= 1, + "amount of where arguments can be 0 or 1"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), false, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); + } + + template + internal::index_t>>, + decltype(internal::make_indexed_column(std::declval()))...> + make_unique_index(std::string name, Cols... cols) { + using cols_tuple = std::tuple; + static_assert(internal::count_tuple::value <= 1, + "amount of where arguments can be 0 or 1"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), true, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/indexed_column.h b/libs/sqlite_orm-1.8.2/dev/indexed_column.h new file mode 100644 index 0000000..e8bd3f1 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/indexed_column.h @@ -0,0 +1,70 @@ +#pragma once + +#include // std::string +#include // std::move + +#include "functional/cxx_universal.h" +#include "ast/where.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct indexed_column_t { + using column_type = C; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + indexed_column_t(column_type _column_or_expression) : + column_or_expression(std::move(_column_or_expression)) {} +#endif + + column_type column_or_expression; + std::string _collation_name; + int _order = 0; // -1 = desc, 1 = asc, 0 = not specified + + indexed_column_t collate(std::string name) { + auto res = std::move(*this); + res._collation_name = std::move(name); + return res; + } + + indexed_column_t asc() { + auto res = std::move(*this); + res._order = 1; + return res; + } + + indexed_column_t desc() { + auto res = std::move(*this); + res._order = -1; + return res; + } + }; + + template + indexed_column_t make_indexed_column(C col) { + return {std::move(col)}; + } + + template + where_t make_indexed_column(where_t wher) { + return std::move(wher); + } + + template + indexed_column_t make_indexed_column(indexed_column_t col) { + return std::move(col); + } + } + + /** + * Use this function to specify indexed column inside `make_index` function call. + * Example: make_index("index_name", indexed_column(&User::id).asc()) + */ + template + internal::indexed_column_t indexed_column(C column_or_expression) { + return {std::move(column_or_expression)}; + } + +} diff --git a/libs/sqlite_orm-1.8.2/dev/interface_definitions.h b/libs/sqlite_orm-1.8.2/dev/interface_definitions.h new file mode 100644 index 0000000..d8fbdbd --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/interface_definitions.h @@ -0,0 +1,9 @@ +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * this file is also used to provide definitions of interface methods 'hitting the database'. + */ +#pragma once + +#include "implementations/column_definitions.h" +#include "implementations/table_definitions.h" +#include "implementations/storage_definitions.h" diff --git a/libs/sqlite_orm-1.8.2/dev/is_base_of_template.h b/libs/sqlite_orm-1.8.2/dev/is_base_of_template.h new file mode 100644 index 0000000..788bb3f --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/is_base_of_template.h @@ -0,0 +1,38 @@ +#pragma once + +#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 + */ +#ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template class Base> + struct is_base_of_template_impl { + template + static constexpr std::true_type test(const Base&); + + static constexpr std::false_type test(...); + }; + + template class C> + using is_base_of_template = decltype(is_base_of_template_impl::test(std::declval())); +#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 + + template class C> + SQLITE_ORM_INLINE_VAR constexpr bool is_base_of_template_v = is_base_of_template::value; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/is_std_ptr.h b/libs/sqlite_orm-1.8.2/dev/is_std_ptr.h new file mode 100644 index 0000000..dc48b1d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/is_std_ptr.h @@ -0,0 +1,30 @@ +#pragma once +#include +#include + +namespace sqlite_orm { + + /** + * Specialization for optional type (std::shared_ptr / std::unique_ptr). + */ + template + struct is_std_ptr : std::false_type {}; + + template + struct is_std_ptr> : std::true_type { + using element_type = typename std::shared_ptr::element_type; + + static std::shared_ptr make(std::remove_cv_t&& v) { + return std::make_shared(std::move(v)); + } + }; + + template + struct is_std_ptr> : std::true_type { + using element_type = typename std::unique_ptr::element_type; + + static auto make(std::remove_cv_t&& v) { + return std::make_unique(std::move(v)); + } + }; +} diff --git a/libs/sqlite_orm-1.8.2/dev/iterator.h b/libs/sqlite_orm-1.8.2/dev/iterator.h new file mode 100644 index 0000000..932087e --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/iterator.h @@ -0,0 +1,101 @@ +#pragma once + +#include +#include // std::shared_ptr, std::unique_ptr, std::make_shared +#include // std::decay +#include // std::move +#include // std::input_iterator_tag +#include // std::system_error +#include // std::bind + +#include "functional/cxx_universal.h" +#include "statement_finalizer.h" +#include "error_code.h" +#include "object_from_column_builder.h" +#include "storage_lookup.h" +#include "util.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct iterator_t { + using view_type = V; + using value_type = typename view_type::mapped_type; + + protected: + /** + * shared_ptr is used over unique_ptr here + * so that the iterator can be copyable. + */ + std::shared_ptr stmt; + + // only null for the default constructed iterator + view_type* view = nullptr; + + /** + * shared_ptr is used over unique_ptr here + * so that the iterator can be copyable. + */ + std::shared_ptr current; + + void extract_value() { + auto& dbObjects = obtain_db_objects(this->view->storage); + this->current = std::make_shared(); + object_from_column_builder builder{*this->current, this->stmt.get()}; + pick_table(dbObjects).for_each_column(builder); + } + + void next() { + this->current.reset(); + if(sqlite3_stmt* stmt = this->stmt.get()) { + perform_step(stmt, std::bind(&iterator_t::extract_value, this)); + if(!this->current) { + this->stmt.reset(); + } + } + } + + public: + using difference_type = ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::input_iterator_tag; + + iterator_t(){}; + + iterator_t(statement_finalizer stmt_, view_type& view_) : stmt{std::move(stmt_)}, view{&view_} { + next(); + } + + const value_type& operator*() const { + if(!this->stmt || !this->current) { + throw std::system_error{orm_error_code::trying_to_dereference_null_iterator}; + } + return *this->current; + } + + const value_type* operator->() const { + return &(this->operator*()); + } + + iterator_t& operator++() { + next(); + return *this; + } + + void operator++(int) { + this->operator++(); + } + + bool operator==(const iterator_t& other) const { + return this->current == other.current; + } + + bool operator!=(const iterator_t& other) const { + return !(*this == other); + } + }; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/journal_mode.h b/libs/sqlite_orm-1.8.2/dev/journal_mode.h new file mode 100644 index 0000000..b72defd --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/journal_mode.h @@ -0,0 +1,73 @@ +#pragma once + +#include // std::back_inserter +#include // std::string +#include // std::unique_ptr +#include // std::array +#include // std::transform +#include // std::toupper + +#if defined(_WINNT_) +// DELETE is a macro defined in the Windows SDK (winnt.h) +#pragma push_macro("DELETE") +#undef DELETE +#endif + +namespace sqlite_orm { + + /** + * Caps case because of: + * 1) delete keyword; + * 2) https://www.sqlite.org/pragma.html#pragma_journal_mode original spelling + */ + enum class journal_mode : signed char { + DELETE = 0, + // An alternate enumeration value when using the Windows SDK that defines DELETE as a macro. + DELETE_ = DELETE, + TRUNCATE = 1, + PERSIST = 2, + MEMORY = 3, + WAL = 4, + OFF = 5, + }; + + namespace internal { + + inline const std::string& to_string(journal_mode j) { + static std::string res[] = { + "DELETE", + "TRUNCATE", + "PERSIST", + "MEMORY", + "WAL", + "OFF", + }; + return res[static_cast(j)]; + } + + 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))); + }); + static std::array all = {{ + journal_mode::DELETE, + journal_mode::TRUNCATE, + journal_mode::PERSIST, + journal_mode::MEMORY, + journal_mode::WAL, + journal_mode::OFF, + }}; + for(auto j: all) { + if(to_string(j) == upper_str) { + return std::make_unique(j); + } + } + return {}; + } + } +} + +#if defined(_WINNT_) +#pragma pop_macro("DELETE") +#endif diff --git a/libs/sqlite_orm-1.8.2/dev/limit_accessor.h b/libs/sqlite_orm-1.8.2/dev/limit_accessor.h new file mode 100644 index 0000000..e87adbf --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/limit_accessor.h @@ -0,0 +1,139 @@ +#pragma once + +#include +#include // std::map +#include // std::function +#include // std::shared_ptr + +#include "connection_holder.h" + +namespace sqlite_orm { + + namespace internal { + + struct limit_accessor { + using get_connection_t = std::function; + + limit_accessor(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} + + int length() { + return this->get(SQLITE_LIMIT_LENGTH); + } + + void length(int newValue) { + this->set(SQLITE_LIMIT_LENGTH, newValue); + } + + int sql_length() { + return this->get(SQLITE_LIMIT_SQL_LENGTH); + } + + void sql_length(int newValue) { + this->set(SQLITE_LIMIT_SQL_LENGTH, newValue); + } + + int column() { + return this->get(SQLITE_LIMIT_COLUMN); + } + + void column(int newValue) { + this->set(SQLITE_LIMIT_COLUMN, newValue); + } + + int expr_depth() { + return this->get(SQLITE_LIMIT_EXPR_DEPTH); + } + + void expr_depth(int newValue) { + this->set(SQLITE_LIMIT_EXPR_DEPTH, newValue); + } + + int compound_select() { + return this->get(SQLITE_LIMIT_COMPOUND_SELECT); + } + + void compound_select(int newValue) { + this->set(SQLITE_LIMIT_COMPOUND_SELECT, newValue); + } + + int vdbe_op() { + return this->get(SQLITE_LIMIT_VDBE_OP); + } + + void vdbe_op(int newValue) { + this->set(SQLITE_LIMIT_VDBE_OP, newValue); + } + + int function_arg() { + return this->get(SQLITE_LIMIT_FUNCTION_ARG); + } + + void function_arg(int newValue) { + this->set(SQLITE_LIMIT_FUNCTION_ARG, newValue); + } + + int attached() { + return this->get(SQLITE_LIMIT_ATTACHED); + } + + void attached(int newValue) { + this->set(SQLITE_LIMIT_ATTACHED, newValue); + } + + int like_pattern_length() { + return this->get(SQLITE_LIMIT_LIKE_PATTERN_LENGTH); + } + + void like_pattern_length(int newValue) { + this->set(SQLITE_LIMIT_LIKE_PATTERN_LENGTH, newValue); + } + + int variable_number() { + return this->get(SQLITE_LIMIT_VARIABLE_NUMBER); + } + + void variable_number(int newValue) { + this->set(SQLITE_LIMIT_VARIABLE_NUMBER, newValue); + } + + int trigger_depth() { + return this->get(SQLITE_LIMIT_TRIGGER_DEPTH); + } + + void trigger_depth(int newValue) { + this->set(SQLITE_LIMIT_TRIGGER_DEPTH, newValue); + } + +#if SQLITE_VERSION_NUMBER >= 3008007 + int worker_threads() { + return this->get(SQLITE_LIMIT_WORKER_THREADS); + } + + void worker_threads(int newValue) { + this->set(SQLITE_LIMIT_WORKER_THREADS, newValue); + } +#endif + + protected: + get_connection_t get_connection; + + friend struct storage_base; + + /** + * Stores limit set between connections. + */ + std::map limits; + + int get(int id) { + auto connection = this->get_connection(); + return sqlite3_limit(connection.get(), id, -1); + } + + void set(int id, int newValue) { + this->limits[id] = newValue; + auto connection = this->get_connection(); + sqlite3_limit(connection.get(), id, newValue); + } + }; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/literal.h b/libs/sqlite_orm-1.8.2/dev/literal.h new file mode 100644 index 0000000..65cb1b2 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/literal.h @@ -0,0 +1,17 @@ +#pragma once + +namespace sqlite_orm { + namespace internal { + + /* + * Protect an otherwise bindable element so that it is always serialized as a literal value. + */ + template + struct literal_holder { + using type = T; + + type value; + }; + + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/mapped_row_extractor.h b/libs/sqlite_orm-1.8.2/dev/mapped_row_extractor.h new file mode 100644 index 0000000..faf47ec --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/mapped_row_extractor.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include "object_from_column_builder.h" + +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 `make_row_extractor()`. + * 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_type = Table; + + V extract(sqlite3_stmt* stmt, int /*columnIndex*/) const { + V res; + object_from_column_builder builder{res, stmt}; + this->tableInfo.for_each_column(builder); + return res; + } + + const table_type& tableInfo; + }; + + } + +} diff --git a/libs/sqlite_orm-1.8.2/dev/mapped_type_proxy.h b/libs/sqlite_orm-1.8.2/dev/mapped_type_proxy.h new file mode 100644 index 0000000..fd914d0 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/mapped_type_proxy.h @@ -0,0 +1,25 @@ +#pragma once + +#include // std::remove_const + +#include "type_traits.h" +#include "alias_traits.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * If T is a recordset alias then the typename mapped_type_proxy::type is the unqualified aliased type, + * otherwise unqualified T. + */ + template + struct mapped_type_proxy : std::remove_const {}; + + template + struct mapped_type_proxy> : std::remove_const> {}; + + template + using mapped_type_proxy_t = typename mapped_type_proxy::type; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/member_traits/member_traits.h b/libs/sqlite_orm-1.8.2/dev/member_traits/member_traits.h new file mode 100644 index 0000000..652d58a --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/member_traits/member_traits.h @@ -0,0 +1,93 @@ +#pragma once + +#include // std::enable_if, std::is_function, std::true_type, std::false_type + +#include "../functional/cxx_universal.h" +#include "../functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { + // SFINAE friendly trait to get a member object pointer's field type + template + struct object_field_type {}; + + template + using object_field_type_t = typename object_field_type::type; + + template + struct object_field_type : std::enable_if::value, F> {}; + + // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified return type) + template + struct getter_field_type {}; + + template + using getter_field_type_t = typename getter_field_type::type; + + template + struct getter_field_type : getter_field_type {}; + + template + struct getter_field_type : polyfill::remove_cvref {}; + + template + struct getter_field_type : polyfill::remove_cvref {}; + +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct getter_field_type : polyfill::remove_cvref {}; + + template + struct getter_field_type : polyfill::remove_cvref {}; +#endif + + // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) + template + struct setter_field_type {}; + + template + using setter_field_type_t = typename setter_field_type::type; + + template + struct setter_field_type : setter_field_type {}; + + template + struct setter_field_type : polyfill::remove_cvref {}; + +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct setter_field_type : polyfill::remove_cvref {}; +#endif + + template + struct is_getter : std::false_type {}; + template + struct is_getter>> : std::true_type {}; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_getter_v = is_getter::value; + + template + struct is_setter : std::false_type {}; + template + struct is_setter>> : std::true_type {}; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_setter_v = is_setter::value; + + template + struct member_field_type : object_field_type, getter_field_type, setter_field_type {}; + + template + using member_field_type_t = typename member_field_type::type; + + template + struct member_object_type {}; + + template + struct member_object_type : polyfill::type_identity {}; + + template + using member_object_type_t = typename member_object_type::type; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/node_tuple.h b/libs/sqlite_orm-1.8.2/dev/node_tuple.h new file mode 100644 index 0000000..b737d1b --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/node_tuple.h @@ -0,0 +1,320 @@ +#pragma once + +#include // std::enable_if +#include // std::tuple +#include // std::pair +#include // std::reference_wrapper +#include "functional/cxx_optional.h" + +#include "functional/cxx_type_traits_polyfill.h" +#include "tuple_helper/tuple_filter.h" +#include "conditions.h" +#include "operators.h" +#include "select_constraints.h" +#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" +#include "ast/into.h" +#include "ast/group_by.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct node_tuple { + using type = std::tuple; + }; + + template + using node_tuple_t = typename node_tuple::type; + + template<> + struct node_tuple { + using type = std::tuple<>; + }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct node_tuple, void> : node_tuple {}; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple> {}; + + template + struct node_tuple, void> { + using args_tuple = node_tuple_t>; + using expression_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple> : node_tuple {}; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + /** + * Column alias + */ + template + struct node_tuple, void> : node_tuple {}; + + /** + * Column alias + */ + template + struct node_tuple, void> : node_tuple {}; + + /** + * Literal + */ + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple> { + using node_type = T; + using left_type = typename node_type::left_type; + using right_type = typename node_type::right_type; + using left_node_tuple = node_tuple_t; + using right_node_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using node_type = binary_operator; + using left_type = typename node_type::left_type; + using right_type = typename node_type::right_type; + using left_node_tuple = node_tuple_t; + using right_node_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using left_tuple = node_tuple_t; + using right_tuple = tuple_cat_t...>; + using type = tuple_cat_t; + }; + + template + struct node_tuple> { + using node_type = T; + using left_type = typename node_type::left_type; + using right_type = typename node_type::right_type; + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using columns_tuple = node_tuple_t; + using args_tuple = tuple_cat_t...>; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct node_tuple, Wargs...>, void> { + using set_tuple = tuple_cat_t...>; + using conditions_tuple = tuple_cat_t...>; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> { + using arg_tuple = node_tuple_t; + using pattern_tuple = node_tuple_t; + using escape_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using arg_tuple = node_tuple_t; + using pattern_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using expression_tuple = node_tuple_t; + using lower_tuple = node_tuple_t; + using upper_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + // note: not strictly necessary as there's no binding support for USING; + // we provide it nevertheless, in line with on_t. + template + struct node_tuple, void> : node_tuple> {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> { + using case_tuple = node_tuple_t; + using args_tuple = tuple_cat_t...>; + using else_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> { + using type = tuple_cat_t, node_tuple_t>; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t, node_tuple_t>; + }; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/object_from_column_builder.h b/libs/sqlite_orm-1.8.2/dev/object_from_column_builder.h new file mode 100644 index 0000000..a613262 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/object_from_column_builder.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include // std::is_member_object_pointer + +#include "functional/static_magic.h" +#include "row_extractor.h" + +namespace sqlite_orm { + + namespace internal { + + struct object_from_column_builder_base { + sqlite3_stmt* stmt = nullptr; + int index = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + object_from_column_builder_base(sqlite3_stmt* stmt) : stmt{stmt} {} +#endif + }; + + /** + * This is a cute lambda replacement which is used in several places. + */ + template + struct object_from_column_builder : object_from_column_builder_base { + using object_type = O; + + object_type& object; + + object_from_column_builder(object_type& object_, sqlite3_stmt* stmt_) : + object_from_column_builder_base{stmt_}, object(object_) {} + + template + void operator()(const column_field& column) { + auto value = row_extractor>().extract(this->stmt, this->index++); + static_if::value>( + [&value, &object = this->object](const auto& column) { + object.*column.member_pointer = std::move(value); + }, + [&value, &object = this->object](const auto& column) { + (object.*column.setter)(std::move(value)); + })(column); + } + }; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/operators.h b/libs/sqlite_orm-1.8.2/dev/operators.h new file mode 100644 index 0000000..238eb9e --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/operators.h @@ -0,0 +1,278 @@ +#pragma once + +#include // std::false_type, std::true_type +#include // std::move +#include "functional/cxx_optional.h" + +#include "tags.h" +#include "serialize_result_type.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * Inherit this class to support arithmetic types overloading + */ + struct arithmetic_t {}; + + template + struct binary_operator : Ds... { + using left_type = L; + using right_type = R; + + left_type lhs; + right_type rhs; + + binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} + }; + + struct conc_string { + serialize_result_type serialize() const { + return "||"; + } + }; + + /** + * Result of concatenation || operator + */ + template + using conc_t = binary_operator; + + struct add_string { + serialize_result_type serialize() const { + return "+"; + } + }; + + /** + * Result of addition + operator + */ + template + using add_t = binary_operator; + + struct sub_string { + serialize_result_type serialize() const { + return "-"; + } + }; + + /** + * Result of substitute - operator + */ + template + using sub_t = binary_operator; + + struct mul_string { + serialize_result_type serialize() const { + return "*"; + } + }; + + /** + * Result of multiply * operator + */ + template + using mul_t = binary_operator; + + struct div_string { + serialize_result_type serialize() const { + return "/"; + } + }; + + /** + * Result of divide / operator + */ + template + using div_t = binary_operator; + + struct mod_operator_string { + serialize_result_type serialize() const { + return "%"; + } + }; + + /** + * Result of mod % operator + */ + template + using mod_t = binary_operator; + + struct bitwise_shift_left_string { + serialize_result_type serialize() const { + return "<<"; + } + }; + + /** + * Result of bitwise shift left << operator + */ + template + using bitwise_shift_left_t = binary_operator; + + struct bitwise_shift_right_string { + serialize_result_type serialize() const { + return ">>"; + } + }; + + /** + * Result of bitwise shift right >> operator + */ + template + using bitwise_shift_right_t = binary_operator; + + struct bitwise_and_string { + serialize_result_type serialize() const { + return "&"; + } + }; + + /** + * Result of bitwise and & operator + */ + template + using bitwise_and_t = binary_operator; + + struct bitwise_or_string { + serialize_result_type serialize() const { + return "|"; + } + }; + + /** + * Result of bitwise or | operator + */ + template + using bitwise_or_t = binary_operator; + + struct bitwise_not_string { + serialize_result_type serialize() const { + return "~"; + } + }; + + /** + * Result of bitwise not ~ operator + */ + template + 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_)) {} + }; + + struct assign_string { + serialize_result_type serialize() 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; + + } + + /** + * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT + * name || '@gmail.com' FROM users + */ + template + internal::conc_t conc(L l, R r) { + return {std::move(l), std::move(r)}; + } + + /** + * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users + */ + template + internal::add_t add(L l, R r) { + return {std::move(l), std::move(r)}; + } + + /** + * 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)}; + } + + template + internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_and_t bitwise_and(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_or_t bitwise_or(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_not_t bitwise_not(T t) { + return {std::move(t)}; + } + + template + internal::assign_t assign(L l, R r) { + return {std::move(l), std::move(r)}; + } + +} diff --git a/libs/sqlite_orm-1.8.2/dev/optional_container.h b/libs/sqlite_orm-1.8.2/dev/optional_container.h new file mode 100644 index 0000000..adc6a10 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/optional_container.h @@ -0,0 +1,33 @@ +#pragma once + +namespace sqlite_orm { + + namespace internal { + + /** + * 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; + + type field; + + template + void apply(const L& l) const { + l(this->field); + } + }; + + template<> + struct optional_container { + using type = void; + + template + void apply(const L&) const { + //.. + } + }; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/order_by_serializer.h b/libs/sqlite_orm-1.8.2/dev/order_by_serializer.h new file mode 100644 index 0000000..f591cda --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/order_by_serializer.h @@ -0,0 +1,78 @@ +#pragma once + +#include // std::string +#include // std::stringstream + +namespace sqlite_orm { + + namespace internal { + + template + struct order_by_serializer; + + template + std::string serialize_order_by(const T& t, const Ctx& context) { + order_by_serializer serializer; + return serializer(t, context); + } + + template + struct order_by_serializer, void> { + using statement_type = order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + + ss << serialize(orderBy.expression, newContext); + if(!orderBy._collate_argument.empty()) { + ss << " COLLATE " << orderBy._collate_argument; + } + switch(orderBy.asc_desc) { + case 1: + ss << " ASC"; + break; + case -1: + ss << " DESC"; + break; + } + return ss.str(); + } + }; + + template + struct order_by_serializer, void> { + using statement_type = dynamic_order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx&) const { + std::stringstream ss; + ss << static_cast(orderBy) << " "; + int index = 0; + for(const dynamic_order_by_entry_t& entry: orderBy) { + if(index > 0) { + ss << ", "; + } + + ss << entry.name; + if(!entry._collate_argument.empty()) { + ss << " COLLATE " << entry._collate_argument; + } + switch(entry.asc_desc) { + case 1: + ss << " ASC"; + break; + case -1: + ss << " DESC"; + break; + } + ++index; + }; + return ss.str(); + } + }; + + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/pointer_value.h b/libs/sqlite_orm-1.8.2/dev/pointer_value.h new file mode 100644 index 0000000..36d37cd --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/pointer_value.h @@ -0,0 +1,173 @@ +#pragma once + +#include +#include +#include + +#include "functional/cxx_universal.h" +#include "xdestroy_handling.h" + +namespace sqlite_orm { + + /** + * Wraps a pointer and tags it with a pointer type, + * used for accepting function parameters, + * facilitating the 'pointer-passing interface'. + * + * Template parameters: + * - P: The value type, possibly const-qualified. + * - T: An integral constant string denoting the pointer type, e.g. `carray_pvt_name`. + * + */ + template + struct pointer_arg { + + static_assert(std::is_convertible::value, + "`std::integral_constant<>` must be convertible to `const char*`"); + + using tag = T; + P* p_; + + P* ptr() const noexcept { + return p_; + } + + operator P*() const noexcept { + return p_; + } + }; + + /** + * Pointer value with associated deleter function, + * used for returning or binding pointer values + * as part of facilitating the 'pointer-passing interface'. + * + * Template parameters: + * - D: The deleter for the pointer value; + * can be one of: + * - function pointer + * - integral function pointer constant + * - state-less (empty) deleter + * - non-capturing lambda + * - structure implicitly yielding a function pointer + * + * @note Use one of the factory functions to create a pointer binding, + * e.g. bindable_carray_pointer or statically_bindable_carray_pointer(). + * + * @example + * ``` + * int64 rememberedId; + * storage.select(func(&Object::id, statically_bindable_carray_pointer(&rememberedId))); + * ``` + */ + template + class pointer_binding { + + P* p_; + SQLITE_ORM_NOUNIQUEADDRESS + D d_; + + protected: + // Constructing pointer bindings must go through bindable_pointer() + template + friend auto bindable_pointer(P2*, D2) noexcept -> pointer_binding; + template + friend B bindable_pointer(typename B::qualified_type*, typename B::deleter_type) noexcept; + + // Construct from pointer and deleter. + // Transfers ownership of the passed in object. + pointer_binding(P* p, D d = {}) noexcept : p_{p}, d_{std::move(d)} {} + + public: + using qualified_type = P; + using tag = T; + using deleter_type = D; + + pointer_binding(const pointer_binding&) = delete; + pointer_binding& operator=(const pointer_binding&) = delete; + pointer_binding& operator=(pointer_binding&&) = delete; + + pointer_binding(pointer_binding&& other) noexcept : + p_{std::exchange(other.p_, nullptr)}, d_{std::move(other.d_)} {} + + ~pointer_binding() { + if(p_) { + if(auto xDestroy = get_xdestroy()) { + // note: C-casting `P* -> void*` like statement_binder> + xDestroy((void*)p_); + } + } + } + + P* ptr() const noexcept { + return p_; + } + + P* take_ptr() noexcept { + return std::exchange(p_, nullptr); + } + + xdestroy_fn_t get_xdestroy() const noexcept { + return obtain_xdestroy_for(d_, p_); + } + }; + + /** + * Template alias for a static pointer value binding. + * 'Static' means that ownership won't be transferred to sqlite, + * sqlite doesn't delete it, and sqlite assumes the object + * pointed to is valid throughout the lifetime of a statement. + */ + template + using static_pointer_binding = pointer_binding; +} + +namespace sqlite_orm { + + /** + * Wrap a pointer, its type and its deleter function for binding it to a statement. + * + * Unless the deleter yields a nullptr 'xDestroy' function the ownership of the pointed-to-object + * is transferred to the pointer binding, which will delete it through + * the deleter when the statement finishes. + */ + template + auto bindable_pointer(P* p, D d) noexcept -> pointer_binding { + return {p, std::move(d)}; + } + + template + auto bindable_pointer(std::unique_ptr p) noexcept -> pointer_binding { + return bindable_pointer(p.release(), p.get_deleter()); + } + + template + B bindable_pointer(typename B::qualified_type* p, typename B::deleter_type d = {}) noexcept { + return B{p, std::move(d)}; + } + + /** + * Wrap a pointer and its type for binding it to a statement. + * + * Note: 'Static' means that ownership of the pointed-to-object won't be transferred + * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. + */ + template + auto statically_bindable_pointer(P* p) noexcept -> static_pointer_binding { + return bindable_pointer(p, null_xdestroy_f); + } + + template + B statically_bindable_pointer(typename B::qualified_type* p, + typename B::deleter_type* /*exposition*/ = nullptr) noexcept { + return bindable_pointer(p); + } + + /** + * Forward a pointer value from an argument. + */ + template + auto rebind_statically(const pointer_arg& pv) noexcept -> static_pointer_binding { + return statically_bindable_pointer(pv.ptr()); + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/pragma.h b/libs/sqlite_orm-1.8.2/dev/pragma.h new file mode 100644 index 0000000..ba1b03e --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/pragma.h @@ -0,0 +1,227 @@ +#pragma once + +#include +#include // std::string +#include // std::function +#include // std::shared_ptr +#include // std::vector +#include + +#include "error_code.h" +#include "row_extractor.h" +#include "journal_mode.h" +#include "connection_holder.h" +#include "util.h" +#include "serializing_util.h" + +namespace sqlite_orm { + + namespace internal { + struct storage_base; + + template + int getPragmaCallback(void* data, int argc, char** argv, char** x) { + return extract_single_value(data, argc, argv, x); + } + + 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(std::move(rowString)); + } + return 0; + } + + 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); + } + + int busy_timeout() { + return this->get_pragma("busy_timeout"); + } + + sqlite_orm::journal_mode journal_mode() { + return this->get_pragma("journal_mode"); + } + + 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); + } + + /** + * https://www.sqlite.org/pragma.html#pragma_application_id + */ + int application_id() { + return this->get_pragma("application_id"); + } + + /** + * https://www.sqlite.org/pragma.html#pragma_application_id + */ + void application_id(int value) { + this->set_pragma("application_id", value); + } + + int synchronous() { + return this->get_pragma("synchronous"); + } + + void synchronous(int value) { + this->_synchronous = -1; + this->set_pragma("synchronous", value); + this->_synchronous = value; + } + + int user_version() { + return this->get_pragma("user_version"); + } + + void user_version(int value) { + this->set_pragma("user_version", value); + } + + int auto_vacuum() { + return this->get_pragma("auto_vacuum"); + } + + void auto_vacuum(int value) { + this->set_pragma("auto_vacuum", value); + } + + std::vector integrity_check() { + return this->get_pragma>("integrity_check"); + } + + template + std::vector integrity_check(T table_name) { + std::ostringstream ss; + ss << "integrity_check(" << table_name << ")" << std::flush; + return this->get_pragma>(ss.str()); + } + + std::vector integrity_check(int n) { + std::ostringstream ss; + ss << "integrity_check(" << n << ")" << std::flush; + return this->get_pragma>(ss.str()); + } + + // will include generated columns in response as opposed to table_info + std::vector table_xinfo(const std::string& tableName) const { + auto connection = this->get_connection(); + + std::vector result; + std::ostringstream ss; + ss << "PRAGMA " + "table_xinfo(" + << streaming_identifier(tableName) << ")" << std::flush; + perform_exec( + connection.get(), + ss.str(), + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(std::vector*)data; + if(argc) { + auto index = 0; + auto cid = std::atoi(argv[index++]); + std::string name = argv[index++]; + std::string type = argv[index++]; + bool notnull = !!std::atoi(argv[index++]); + std::string dflt_value = argv[index] ? argv[index] : ""; + ++index; + auto pk = std::atoi(argv[index++]); + auto hidden = std::atoi(argv[index++]); + res.emplace_back(cid, + std::move(name), + std::move(type), + notnull, + std::move(dflt_value), + pk, + hidden); + } + return 0; + }, + &result); + return result; + } + + std::vector table_info(const std::string& tableName) const { + auto connection = this->get_connection(); + + std::ostringstream ss; + ss << "PRAGMA " + "table_info(" + << streaming_identifier(tableName) << ")" << std::flush; + std::vector result; + perform_exec( + connection.get(), + ss.str(), + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(std::vector*)data; + if(argc) { + auto index = 0; + auto cid = std::atoi(argv[index++]); + std::string name = argv[index++]; + std::string type = argv[index++]; + bool notnull = !!std::atoi(argv[index++]); + std::string dflt_value = argv[index] ? argv[index] : ""; + ++index; + auto pk = std::atoi(argv[index++]); + res.emplace_back(cid, std::move(name), std::move(type), notnull, std::move(dflt_value), pk); + } + return 0; + }, + &result); + return result; + } + + 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(); + T result; + perform_exec(connection.get(), "PRAGMA " + name, getPragmaCallback, &result); + return result; + } + + /** + * 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 << std::flush; + 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 << " = " << to_string(value) << std::flush; + perform_void_exec(db, ss.str()); + } + }; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/prepared_statement.h b/libs/sqlite_orm-1.8.2/dev/prepared_statement.h new file mode 100644 index 0000000..b73853f --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/prepared_statement.h @@ -0,0 +1,753 @@ +#pragma once + +#include +#include // std::unique_ptr +#include // std::iterator_traits +#include // std::string +#include // std::integral_constant, std::declval +#include // std::pair + +#include "functional/cxx_universal.h" +#include "functional/cxx_type_traits_polyfill.h" +#include "functional/cxx_functional_polyfill.h" +#include "tuple_helper/tuple_filter.h" +#include "connection_holder.h" +#include "select_constraints.h" +#include "values.h" +#include "ast/upsert_clause.h" +#include "ast/set.h" + +namespace sqlite_orm { + + namespace internal { + + struct prepared_statement_base { + sqlite3_stmt* stmt = nullptr; + connection_ref con; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + prepared_statement_base(sqlite3_stmt* stmt, connection_ref con) : stmt{stmt}, con{std::move(con)} {} +#endif + + ~prepared_statement_base() { + sqlite3_finalize(this->stmt); + } + + std::string sql() const { + // note: sqlite3 internally checks for null before calling + // sqlite3_normalized_sql() or sqlite3_expanded_sql(), so check here, too, even if superfluous + if(const char* sql = sqlite3_sql(this->stmt)) { + return sql; + } else { + return {}; + } + } + +#if SQLITE_VERSION_NUMBER >= 3014000 + std::string expanded_sql() const { + // note: must check return value due to SQLITE_OMIT_TRACE + using char_ptr = std::unique_ptr>; + if(char_ptr sql{sqlite3_expanded_sql(this->stmt)}) { + return sql.get(); + } else { + return {}; + } + } +#endif +#if SQLITE_VERSION_NUMBER >= 3026000 and defined(SQLITE_ENABLE_NORMALIZE) + std::string normalized_sql() const { + if(const char* sql = sqlite3_normalized_sql(this->stmt)) { + return sql; + } else { + return {}; + } + } +#endif + +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::string_view column_name(int index) const { + return sqlite3_column_name(stmt, index); + } +#endif + }; + + template + struct prepared_statement_t : prepared_statement_base { + using expression_type = T; + + expression_type expression; + + prepared_statement_t(T expression_, sqlite3_stmt* stmt_, connection_ref con_) : + prepared_statement_base{stmt_, std::move(con_)}, expression(std::move(expression_)) {} + + prepared_statement_t(prepared_statement_t&& prepared_stmt) : + prepared_statement_base{prepared_stmt.stmt, std::move(prepared_stmt.con)}, + expression(std::move(prepared_stmt.expression)) { + prepared_stmt.stmt = nullptr; + } + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_prepared_statement_v = + polyfill::is_specialization_of_v; + + template + using is_prepared_statement = polyfill::bool_constant>; + + /** + * T - type of object to obtain from a database + */ + template + struct get_all_t { + using type = T; + using return_type = R; + + using conditions_type = std::tuple; + + conditions_type conditions; + }; + + template + struct get_all_pointer_t { + using type = T; + using return_type = R; + + using conditions_type = std::tuple; + + conditions_type conditions; + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct get_all_optional_t { + using type = T; + using return_type = R; + + using conditions_type = std::tuple; + + conditions_type conditions; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct update_all_t { + using set_type = S; + using conditions_type = std::tuple; + + static_assert(is_set::value, "update_all_t must have set or dynamic set as the first argument"); + + set_type set; + conditions_type conditions; + }; + + template + struct remove_all_t { + using type = T; + using conditions_type = std::tuple; + + conditions_type conditions; + }; + + template + struct get_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; + + template + struct get_pointer_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct get_optional_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct update_t { + using type = T; + + type object; + }; + + template + struct remove_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; + + template + struct insert_t { + using type = T; + + type object; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_insert_v = polyfill::is_specialization_of_v; + + template + using is_insert = polyfill::bool_constant>; + + template + struct insert_explicit { + using type = T; + using columns_type = columns_t; + + type obj; + columns_type columns; + }; + + template + struct replace_t { + using type = T; + + type object; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_replace_v = polyfill::is_specialization_of_v; + + template + using is_replace = polyfill::bool_constant>; + + template + struct insert_range_t { + using iterator_type = It; + using transformer_type = Projection; + using object_type = O; + + std::pair range; + transformer_type transformer; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_insert_range_v = polyfill::is_specialization_of_v; + + template + using is_insert_range = polyfill::bool_constant>; + + template + struct replace_range_t { + using iterator_type = It; + using transformer_type = Projection; + using object_type = O; + + std::pair range; + transformer_type transformer; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_replace_range_v = polyfill::is_specialization_of_v; + + template + using is_replace_range = polyfill::bool_constant>; + + template + struct insert_raw_t { + using args_tuple = std::tuple; + + args_tuple args; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_insert_raw_v = polyfill::is_specialization_of_v; + + template + using is_insert_raw = polyfill::bool_constant>; + + template + struct replace_raw_t { + using args_tuple = std::tuple; + + args_tuple args; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_replace_raw_v = polyfill::is_specialization_of_v; + + template + using is_replace_raw = polyfill::bool_constant>; + + struct default_values_t {}; + + template + using is_default_values = std::is_same; + + enum class conflict_action { + abort, + fail, + ignore, + replace, + rollback, + }; + + struct insert_constraint { + conflict_action action = conflict_action::abort; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + insert_constraint(conflict_action action) : action{action} {} +#endif + }; + + template + using is_insert_constraint = std::is_same; + } + + inline internal::insert_constraint or_rollback() { + return {internal::conflict_action::rollback}; + } + + inline internal::insert_constraint or_replace() { + return {internal::conflict_action::replace}; + } + + inline internal::insert_constraint or_ignore() { + return {internal::conflict_action::ignore}; + } + + inline internal::insert_constraint or_fail() { + return {internal::conflict_action::fail}; + } + + inline internal::insert_constraint or_abort() { + return {internal::conflict_action::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 {}; + } + + /** + * 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. + * The objects in the range are transformed using the specified projection, which defaults to identity projection. + * + * @example + * ``` + * std::vector users; + * users.push_back(User{1, "Leony"}); + * auto statement = storage.prepare(replace_range(users.begin(), users.end())); + * storage.execute(statement); + * ``` + * @example + * ``` + * std::vector> userPointers; + * userPointers.push_back(std::make_unique(1, "Eneli")); + * auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)); + * storage.execute(statement); + * ``` + */ + template + auto replace_range(It from, It to, Projection project = {}) { + using O = std::decay_t(), *std::declval()))>; + return internal::replace_range_t{{std::move(from), std::move(to)}, std::move(project)}; + } + + /* + * Create a replace range statement. + * Overload of `replace_range(It, It, Projection)` with explicit object type template parameter. + */ + template + internal::replace_range_t replace_range(It from, It to, Projection project = {}) { + return {{std::move(from), std::move(to)}, std::move(project)}; + } + + /** + * Create an insert range statement. + * The objects in the range are transformed using the specified projection, which defaults to identity projection. + * + * @example + * ``` + * std::vector users; + * users.push_back(User{1, "Leony"}); + * auto statement = storage.prepare(insert_range(users.begin(), users.end())); + * storage.execute(statement); + * ``` + * @example + * ``` + * std::vector> userPointers; + * userPointers.push_back(std::make_unique(1, "Eneli")); + * auto statement = storage.prepare(insert_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)); + * storage.execute(statement); + * ``` + */ + template + auto insert_range(It from, It to, Projection project = {}) { + using O = std::decay_t(), *std::declval()))>; + return internal::insert_range_t{{std::move(from), std::move(to)}, std::move(project)}; + } + + /* + * Create an insert range statement. + * Overload of `insert_range(It, It, Projection)` with explicit object type template parameter. + */ + template + internal::insert_range_t insert_range(It from, It to, Projection project = {}) { + return {{std::move(from), std::move(to)}, std::move(project)}; + } + + /** + * Create a replace statement. + * T is an object type mapped to a storage. + * Usage: storage.replace(myUserInstance); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.replace(std::ref(myUserInstance)); + */ + template + internal::replace_t replace(T obj) { + return {std::move(obj)}; + } + + /** + * Create an insert statement. + * T is an object type mapped to a storage. + * Usage: storage.insert(myUserInstance); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.insert(std::ref(myUserInstance)); + */ + template + internal::insert_t insert(T obj) { + return {std::move(obj)}; + } + + /** + * Create an explicit insert statement. + * T is an object type mapped to a storage. + * Cols is columns types aparameter pack. Must contain member pointers + * Usage: storage.insert(myUserInstance, columns(&User::id, &User::name)); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.insert(std::ref(myUserInstance), columns(&User::id, &User::name)); + */ + template + internal::insert_explicit insert(T obj, internal::columns_t cols) { + return {std::move(obj), std::move(cols)}; + } + + /** + * Create a remove statement + * T is an object type mapped to a storage. + * Usage: remove(5); + */ + template + internal::remove_t remove(Ids... ids) { + std::tuple idsTuple{std::forward(ids)...}; + return {std::move(idsTuple)}; + } + + /** + * Create an update statement. + * T is an object type mapped to a storage. + * Usage: storage.update(myUserInstance); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.update(std::ref(myUserInstance)); + */ + template + internal::update_t update(T obj) { + return {std::move(obj)}; + } + + /** + * Create a get statement. + * T is an object type mapped to a storage. + * Usage: get(5); + */ + template + internal::get_t get(Ids... ids) { + std::tuple idsTuple{std::forward(ids)...}; + return {std::move(idsTuple)}; + } + + /** + * Create a get pointer statement. + * T is an object type mapped to a storage. + * Usage: get_pointer(5); + */ + template + internal::get_pointer_t get_pointer(Ids... ids) { + std::tuple idsTuple{std::forward(ids)...}; + return {std::move(idsTuple)}; + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * Create a get optional statement. + * T is an object type mapped to a storage. + * Usage: get_optional(5); + */ + template + internal::get_optional_t get_optional(Ids... ids) { + std::tuple idsTuple{std::forward(ids)...}; + return {std::move(idsTuple)}; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + /** + * Create a remove all statement. + * T is an object type mapped to a storage. + * Usage: storage.remove_all(...); + */ + template + internal::remove_all_t remove_all(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {std::move(conditions)}; + } + + /** + * Create a get all statement. + * T is an object type mapped to a storage. + * Usage: storage.get_all(...); + */ + template + internal::get_all_t, Args...> get_all(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {std::move(conditions)}; + } + + /** + * Create a get all statement. + * T is an object type mapped to a storage. + * R is a container type. std::vector is default + * Usage: storage.get_all(...); + */ + template + internal::get_all_t get_all(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {std::move(conditions)}; + } + + /** + * Create an update all statement. + * Usage: storage.update_all(set(...), ...); + */ + template + internal::update_all_t update_all(S set, Wargs... wh) { + static_assert(internal::is_set::value, "first argument in update_all can be either set or dynamic_set"); + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(wh)...}; + return {std::move(set), std::move(conditions)}; + } + + /** + * Create a get all pointer statement. + * T is an object type mapped to a storage. + * Usage: storage.get_all_pointer(...); + */ + template + internal::get_all_pointer_t>, Args...> get_all_pointer(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {std::move(conditions)}; + } + /** + * Create a get all pointer statement. + * T is an object type mapped to a storage. + * R is a container return type. std::vector> is default + * Usage: storage.get_all_pointer(...); + */ + template + internal::get_all_pointer_t get_all_pointer(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {std::move(conditions)}; + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * Create a get all optional statement. + * T is an object type mapped to a storage. + * Usage: storage.get_all_optional(...); + */ + template + internal::get_all_optional_t>, Args...> get_all_optional(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {std::move(conditions)}; + } + + /** + * Create a get all optional statement. + * T is an object type mapped to a storage. + * R is a container return type. std::vector> is default + * Usage: storage.get_all_optional(...); + */ + template + internal::get_all_optional_t get_all_optional(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {std::move(conditions)}; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} diff --git a/libs/sqlite_orm-1.8.2/dev/row_extractor.h b/libs/sqlite_orm-1.8.2/dev/row_extractor.h new file mode 100644 index 0000000..ed60e59 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/row_extractor.h @@ -0,0 +1,340 @@ +#pragma once + +#include +#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if +#include // atof, atoi, atoll +#include // std::system_error +#include // std::string, std::wstring +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::wstring_convert, std::codecvt_utf8_utf16 +#endif // SQLITE_ORM_OMITS_CODECVT +#include // std::vector +#include // strlen +#include +#include // std::copy +#include // std::back_inserter +#include // std::tuple, std::tuple_size, std::tuple_element + +#include "functional/cxx_universal.h" +#include "arithmetic_tag.h" +#include "pointer_value.h" +#include "journal_mode.h" +#include "error_code.h" +#include "is_std_ptr.h" + +namespace sqlite_orm { + + /** + * Helper class used to cast values from argv to V class + * which depends from column type. + * + */ + template + struct row_extractor { + // used in sqlite3_exec (select) + V extract(const char* row_value) const = delete; + + // used in sqlite_column (iteration, get_all) + V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; + + // used in user defined functions + V extract(sqlite3_value* value) const = delete; + }; + + template + int extract_single_value(void* data, int argc, char** argv, char**) { + auto& res = *(R*)data; + if(argc) { + res = row_extractor{}.extract(argv[0]); + } + return 0; + } + + /** + * Specialization for the 'pointer-passing interface'. + * + * @note The 'pointer-passing' interface doesn't support (and in fact prohibits) + * extracting pointers from columns. + */ + template + struct row_extractor, void> { + using V = pointer_arg; + + V extract(sqlite3_value* value) const { + return {(P*)sqlite3_value_pointer(value, T::value)}; + } + }; + + /** + * Undefine using pointer_binding<> for querying values + */ + template + struct row_extractor, void>; + + /** + * Specialization for arithmetic types. + */ + template + struct row_extractor::value>> { + 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_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&) const { + return static_cast(atoi(row_value)); + } + + V extract(sqlite3_stmt* stmt, int columnIndex, const int_or_smaller_tag&) const { + return static_cast(sqlite3_column_int(stmt, columnIndex)); + } + + 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&) const { + return static_cast(sqlite3_column_int64(stmt, columnIndex)); + } + + 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&) 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)); + } + }; + + /** + * Specialization for std::string. + */ + template<> + struct row_extractor { + std::string extract(const char* row_value) const { + if(row_value) { + return row_value; + } else { + return {}; + } + } + + 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 {}; + } + } + }; +#ifndef SQLITE_ORM_OMITS_CODECVT + /** + * Specialization for std::wstring. + */ + template<> + struct row_extractor { + std::wstring extract(const char* row_value) const { + if(row_value) { + std::wstring_convert> converter; + return converter.from_bytes(row_value); + } else { + return {}; + } + } + + 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); + } else { + 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 + + template + struct row_extractor::value>> { + using unqualified_type = std::remove_cv_t; + + V extract(const char* row_value) const { + if(row_value) { + return is_std_ptr::make(row_extractor().extract(row_value)); + } else { + return {}; + } + } + + 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)); + } else { + 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 + template + struct row_extractor>> { + using unqualified_type = std::remove_cv_t; + + V extract(const char* row_value) const { + if(row_value) { + return std::make_optional(row_extractor().extract(row_value)); + } else { + return std::nullopt; + } + } + + V 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)); + } else { + return std::nullopt; + } + } + + V 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 + + template<> + struct row_extractor { + nullptr_t extract(const char* /*row_value*/) const { + return nullptr; + } + + nullptr_t extract(sqlite3_stmt*, int /*columnIndex*/) const { + return nullptr; + } + + nullptr_t extract(sqlite3_value*) const { + return nullptr; + } + }; + /** + * Specialization for std::vector. + */ + template<> + struct row_extractor> { + std::vector extract(const char* row_value) const { + return {row_value, row_value + (row_value ? ::strlen(row_value) : 0)}; + } + + 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 {bytes, 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 {bytes, bytes + len}; + } + }; + + template + struct row_extractor> { + + std::tuple extract(char** argv) const { + return this->extract(argv, std::make_index_sequence{}); + } + + std::tuple extract(sqlite3_stmt* stmt, int /*columnIndex*/) const { + return this->extract(stmt, std::make_index_sequence{}); + } + + protected: + template + std::tuple extract(sqlite3_stmt* stmt, std::index_sequence) const { + return std::tuple{row_extractor{}.extract(stmt, Idx)...}; + } + + template + std::tuple extract(char** argv, std::index_sequence) const { + return std::tuple{row_extractor{}.extract(argv[Idx])...}; + } + }; + + /** + * Specialization for journal_mode. + */ + template<> + struct row_extractor { + 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); + } else { + throw std::system_error{orm_error_code::incorrect_journal_mode_string}; + } + } else { + throw std::system_error{orm_error_code::incorrect_journal_mode_string}; + } + } + + 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/libs/sqlite_orm-1.8.2/dev/row_extractor_builder.h b/libs/sqlite_orm-1.8.2/dev/row_extractor_builder.h new file mode 100644 index 0000000..8ebf796 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/row_extractor_builder.h @@ -0,0 +1,22 @@ +#pragma once + +#include "functional/cxx_universal.h" +#include "row_extractor.h" +#include "mapped_row_extractor.h" + +namespace sqlite_orm { + + namespace internal { + + template + row_extractor make_row_extractor(nullptr_t) { + return {}; + } + + template + mapped_row_extractor make_row_extractor(const Table* table) { + return {*table}; + } + } + +} diff --git a/libs/sqlite_orm-1.8.2/dev/rowid.h b/libs/sqlite_orm-1.8.2/dev/rowid.h new file mode 100644 index 0000000..a99fe6d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/rowid.h @@ -0,0 +1,69 @@ +#pragma once + +#include // std::string + +namespace sqlite_orm { + + namespace internal { + + struct rowid_t { + operator std::string() const { + return "rowid"; + } + }; + + struct oid_t { + operator std::string() const { + return "oid"; + } + }; + + struct _rowid_t { + operator std::string() const { + return "_rowid_"; + } + }; + + template + struct table_rowid_t : public rowid_t { + using type = T; + }; + + template + struct table_oid_t : public oid_t { + using type = T; + }; + template + struct table__rowid_t : public _rowid_t { + using type = T; + }; + + } + + inline internal::rowid_t rowid() { + return {}; + } + + inline internal::oid_t oid() { + return {}; + } + + inline internal::_rowid_t _rowid_() { + return {}; + } + + template + internal::table_rowid_t rowid() { + return {}; + } + + template + internal::table_oid_t oid() { + return {}; + } + + template + internal::table__rowid_t _rowid_() { + return {}; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/select_constraints.h b/libs/sqlite_orm-1.8.2/dev/select_constraints.h new file mode 100644 index 0000000..7b1f690 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/select_constraints.h @@ -0,0 +1,433 @@ +#pragma once + +#include // std::remove_const +#include // std::string +#include // std::move +#include // std::tuple, std::get, std::tuple_size +#include "functional/cxx_optional.h" + +#include "functional/cxx_universal.h" +#include "functional/cxx_type_traits_polyfill.h" +#include "is_base_of_template.h" +#include "tuple_helper/tuple_filter.h" +#include "optional_container.h" +#include "ast/where.h" +#include "ast/group_by.h" +#include "core_functions.h" +#include "alias_traits.h" +#include "column_pointer.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 : 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; + + columns_type columns; + bool distinct = false; + + static constexpr int count = std::tuple_size::value; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + columns_t(columns_type columns) : columns{std::move(columns)} {} +#endif + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_columns_v = polyfill::is_specialization_of_v; + + template + using is_columns = polyfill::bool_constant>; + + /** + * Subselect object type. + */ + template + struct select_t { + using return_type = T; + using conditions_type = std::tuple; + + return_type col; + conditions_type conditions; + bool highest_level = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + select_t(return_type col, conditions_type conditions) : + col{std::move(col)}, conditions{std::move(conditions)} {} +#endif + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_select_v = polyfill::is_specialization_of_v; + + template + using is_select = polyfill::bool_constant>; + + /** + * Base for UNION, UNION ALL, EXCEPT and INTERSECT + */ + template + struct compound_operator { + using left_type = L; + using right_type = R; + + left_type left; + right_type right; + + compound_operator(left_type l, right_type r) : left(std::move(l)), right(std::move(r)) { + this->left.highest_level = true; + this->right.highest_level = true; + } + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_of_template_v; + + template + using is_compound_operator = polyfill::bool_constant>; + + struct union_base { + bool all = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + union_base(bool all) : all{all} {} +#endif + + operator std::string() const { + if(!this->all) { + return "UNION"; + } else { + return "UNION ALL"; + } + } + }; + + /** + * UNION object type. + */ + template + struct union_t : public compound_operator, union_base { + using left_type = typename compound_operator::left_type; + using right_type = typename compound_operator::right_type; + + union_t(left_type l, right_type r, bool all_) : + compound_operator{std::move(l), std::move(r)}, union_base{all_} {} + + 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 : 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 "INTERSECT"; + } + }; + /** + * INTERSECT object type. + */ + template + 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; + }; + + /** + * Generic way to get DISTINCT value from any type. + */ + template + bool get_distinct(const T&) { + return false; + } + + template + bool get_distinct(const columns_t& cols) { + return cols.distinct; + } + + template + struct asterisk_t { + using type = T; + + bool defined_order = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + asterisk_t(bool definedOrder) : defined_order{definedOrder} {} +#endif + }; + + template + struct object_t { + using type = T; + + bool defined_order = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + object_t(bool definedOrder) : defined_order{definedOrder} {} +#endif + }; + + template + struct then_t { + using expression_type = T; + + expression_type expression; + }; + + template + struct simple_case_t { + using return_type = R; + using case_expression_type = T; + using args_type = std::tuple; + using else_expression_type = E; + + optional_container case_expression; + args_type args; + optional_container else_expression; + }; + + /** + * T is a case expression type + * E is else type (void is ELSE is omitted) + * Args... is a pack of WHEN expressions + */ + template + struct simple_case_builder { + using return_type = R; + using case_expression_type = T; + using args_type = std::tuple; + using else_expression_type = E; + + optional_container case_expression; + args_type args; + optional_container else_expression; + + template + simple_case_builder> when(W w, then_t t) { + using result_args_type = std::tuple>; + std::pair newPair{std::move(w), std::move(t.expression)}; + result_args_type result_args = std::tuple_cat(std::move(this->args), std::make_tuple(newPair)); + std::get::value - 1>(result_args) = std::move(newPair); + return {std::move(this->case_expression), std::move(result_args), std::move(this->else_expression)}; + } + + simple_case_t end() { + return {std::move(this->case_expression), std::move(args), std::move(this->else_expression)}; + } + + template + simple_case_builder else_(El el) { + return {{std::move(this->case_expression)}, std::move(args), {std::move(el)}}; + } + }; + + template + void validate_conditions() { + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 WHERE blocks"); + 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)}; + } + + template + internal::simple_case_builder case_(T t) { + return {{std::move(t)}}; + } + + template + internal::simple_case_builder case_() { + return {}; + } + + template + internal::distinct_t distinct(T t) { + return {std::move(t)}; + } + + template + internal::all_t all(T t) { + return {std::move(t)}; + } + + template + internal::columns_t distinct(internal::columns_t cols) { + cols.distinct = true; + return cols; + } + + template + internal::columns_t columns(Args... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * Use it like this: + * struct MyType : BaseType { ... }; + * storage.select(column(&BaseType::id)); + */ + template + internal::column_pointer column(F f) { + return {std::move(f)}; + } + + /** + * Public function for subselect query. Is useful in UNION queries. + */ + template + internal::select_t select(T t, Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + return {std::move(t), std::make_tuple(std::forward(args)...)}; + } + + /** + * Public function for UNION operator. + * lhs and rhs are subselect objects. + * Look through example in examples/union.cpp + */ + template + internal::union_t union_(L lhs, R rhs) { + return {std::move(lhs), std::move(rhs)}; + } + + /** + * Public function for EXCEPT operator. + * lhs and rhs are subselect objects. + * Look through example in examples/except.cpp + */ + template + internal::except_t except(L lhs, R rhs) { + return {std::move(lhs), std::move(rhs)}; + } + + template + internal::intersect_t intersect(L lhs, R rhs) { + return {std::move(lhs), std::move(rhs)}; + } + + /** + * Public function for UNION ALL operator. + * lhs and rhs are subselect objects. + * Look through example in examples/union.cpp + */ + template + internal::union_t union_all(L lhs, R rhs) { + return {std::move(lhs), std::move(rhs), true}; + } + + /** + * `SELECT * FROM T` expression that fetches results as tuples. + * T is a type mapped to a storage, or an alias of it. + * The `definedOrder` parameter denotes the expected order of result columns. + * The default is the implicit order as returned by SQLite, which may differ from the defined order + * if the schema of a table has been changed. + * By specifying the defined order, the columns are written out in the resulting select SQL string. + * + * In pseudo code: + * select(asterisk(false)) -> SELECT * from User + * select(asterisk(true)) -> SELECT id, name from User + * + * Example: auto rows = storage.select(asterisk()); + * // decltype(rows) is std::vector> + * Example: auto rows = storage.select(asterisk(true)); + * // decltype(rows) is std::vector> + * + * If you need to fetch results as objects instead of tuples please use `object()`. + */ + template + internal::asterisk_t asterisk(bool definedOrder = false) { + return {definedOrder}; + } + + /** + * `SELECT * FROM T` expression that fetches results as objects of type T. + * T is a type mapped to a storage, or an alias of it. + * + * Example: auto rows = storage.select(object()); + * // decltype(rows) is std::vector, where the User objects are constructed from columns in implicitly stored order + * Example: auto rows = storage.select(object(true)); + * // decltype(rows) is std::vector, where the User objects are constructed from columns in declared make_table order + * + * If you need to fetch results as tuples instead of objects please use `asterisk()`. + */ + template + internal::object_t object(bool definedOrder = false) { + return {definedOrder}; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/serialize_result_type.h b/libs/sqlite_orm-1.8.2/dev/serialize_result_type.h new file mode 100644 index 0000000..ac5f606 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/serialize_result_type.h @@ -0,0 +1,18 @@ +#pragma once + +#include "functional/cxx_string_view.h" +#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // std::string +#endif + +namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + using serialize_result_type = std::string_view; + using serialize_arg_type = std::string_view; +#else + using serialize_result_type = std::string; + using serialize_arg_type = const std::string&; +#endif + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/serializer_context.h b/libs/sqlite_orm-1.8.2/dev/serializer_context.h new file mode 100644 index 0000000..8536f3d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/serializer_context.h @@ -0,0 +1,38 @@ +#pragma once + +namespace sqlite_orm { + + namespace internal { + + struct serializer_context_base { + bool replace_bindable_with_question = false; + bool skip_table_name = true; + bool use_parentheses = true; + }; + + template + struct serializer_context : serializer_context_base { + using db_objects_type = DBOs; + + const db_objects_type& db_objects; + + serializer_context(const db_objects_type& dbObjects) : db_objects{dbObjects} {} + }; + + template + struct serializer_context_builder { + using storage_type = S; + using db_objects_type = typename storage_type::db_objects_type; + + serializer_context_builder(const storage_type& storage_) : storage{storage_} {} + + serializer_context operator()() const { + return {obtain_db_objects(this->storage)}; + } + + const storage_type& storage; + }; + + } + +} diff --git a/libs/sqlite_orm-1.8.2/dev/serializing_util.h b/libs/sqlite_orm-1.8.2/dev/serializing_util.h new file mode 100644 index 0000000..e3cc370 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/serializing_util.h @@ -0,0 +1,403 @@ +#pragma once + +#include // std::index_sequence +#include +#include +#include +#include +#include // std::exchange, std::tuple_size + +#include "functional/cxx_universal.h" // ::size_t +#include "functional/cxx_type_traits_polyfill.h" +#include "tuple_helper/tuple_iteration.h" +#include "error_code.h" +#include "serializer_context.h" +#include "serialize_result_type.h" +#include "util.h" + +namespace sqlite_orm { + namespace internal { + template + struct order_by_t; + + template + std::string serialize(const T&, const serializer_context&); + + template + std::string serialize_order_by(const T&, const Ctx&); + + inline void stream_sql_escaped(std::ostream& os, serialize_arg_type str, char char2Escape) { + for(size_t offset = 0, next; true; offset = next + 1) { + next = str.find(char2Escape, offset); + + if(next == str.npos) { + os.write(str.data() + offset, str.size() - offset); + break; + } + + os.write(str.data() + offset, next - offset + 1); + os.write(&char2Escape, 1); + } + } + + inline void stream_identifier(std::ostream& ss, + serialize_arg_type qualifier, + serialize_arg_type identifier, + serialize_arg_type alias) { + constexpr char quoteChar = '"'; + constexpr char qualified[] = {quoteChar, '.', '\0'}; + constexpr char aliased[] = {' ', quoteChar, '\0'}; + + // note: In practice, escaping double quotes in identifiers is arguably overkill, + // but since the SQLite grammar allows it, it's better to be safe than sorry. + + if(!qualifier.empty()) { + ss << quoteChar; + stream_sql_escaped(ss, qualifier, quoteChar); + ss << qualified; + } + { + ss << quoteChar; + stream_sql_escaped(ss, identifier, quoteChar); + ss << quoteChar; + } + if(!alias.empty()) { + ss << aliased; + stream_sql_escaped(ss, alias, quoteChar); + ss << quoteChar; + } + } + + inline void stream_identifier(std::ostream& ss, const std::string& identifier, const std::string& alias) { + return stream_identifier(ss, "", identifier, alias); + } + + inline void stream_identifier(std::ostream& ss, const std::string& identifier) { + return stream_identifier(ss, "", identifier, ""); + } + + template + void stream_identifier(std::ostream& ss, const Tpl& tpl, std::index_sequence) { + static_assert(sizeof...(Is) > 0 && sizeof...(Is) <= 3, ""); + return stream_identifier(ss, std::get(tpl)...); + } + + template>, bool> = true> + void stream_identifier(std::ostream& ss, const Tpl& tpl) { + return stream_identifier(ss, tpl, std::make_index_sequence::value>{}); + } + + enum class stream_as { + conditions_tuple, + actions_tuple, + expressions_tuple, + dynamic_expressions, + serialized, + identifier, + identifiers, + values_placeholders, + table_columns, + non_generated_columns, + field_values_excluding, + mapped_columns_expressions, + column_constraints, + }; + + template + struct streaming { + template + auto operator()(const Ts&... ts) const { + return std::forward_as_tuple(*this, ts...); + } + + template + constexpr std::index_sequence<1u + Idx...> offset_index(std::index_sequence) const { + return {}; + } + }; + constexpr streaming streaming_conditions_tuple{}; + constexpr streaming streaming_actions_tuple{}; + constexpr streaming streaming_expressions_tuple{}; + constexpr streaming streaming_dynamic_expressions{}; + constexpr streaming streaming_serialized{}; + constexpr streaming streaming_identifier{}; + constexpr streaming streaming_identifiers{}; + constexpr streaming streaming_values_placeholders{}; + constexpr streaming streaming_table_column_names{}; + constexpr streaming streaming_non_generated_column_names{}; + constexpr streaming streaming_field_values_excluding{}; + constexpr streaming streaming_mapped_columns_expressions{}; + constexpr streaming streaming_column_constraints{}; + + // serialize and stream a tuple of condition expressions; + // space + space-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, T, Ctx> tpl) { + const auto& conditions = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(conditions, [&ss, &context](auto& c) { + ss << " " << serialize(c, context); + }); + return ss; + } + + // serialize and stream a tuple of action expressions; + // space-separated + template + std::ostream& operator<<(std::ostream& ss, std::tuple&, T, Ctx> tpl) { + const auto& actions = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(actions, [&ss, &context, first = true](auto& action) mutable { + constexpr std::array sep = {" ", ""}; + ss << sep[std::exchange(first, false)] << serialize(action, context); + }); + return ss; + } + + // serialize and stream a tuple of expressions; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, T, Ctx> tpl) { + const auto& args = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(args, [&ss, &context, first = true](auto& arg) mutable { + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << serialize(arg, context); + }); + return ss; + } + + // serialize and stream multi_order_by arguments; + // comma-separated + template + std::ostream& operator<<( + std::ostream& ss, + std::tuple&, const std::tuple...>&, Ctx> tpl) { + const auto& args = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(args, [&ss, &context, first = true](auto& arg) mutable { + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << serialize_order_by(arg, context); + }); + return ss; + } + + // serialize and stream a vector of expressions; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, C, Ctx> tpl) { + const auto& args = get<1>(tpl); + auto& context = get<2>(tpl); + + constexpr std::array sep = {", ", ""}; + for(size_t i = 0, first = true; i < args.size(); ++i) { + ss << sep[std::exchange(first, false)] << serialize(args[i], context); + } + return ss; + } + + // stream a vector of already serialized strings; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, std::tuple&, C> tpl) { + const auto& strings = get<1>(tpl); + + constexpr std::array sep = {", ", ""}; + for(size_t i = 0, first = true; i < strings.size(); ++i) { + ss << sep[std::exchange(first, false)] << strings[i]; + } + return ss; + } + + // stream an identifier described by a variadic string pack, which is one of: + // 1. identifier + // 2. identifier, alias + // 3. qualifier, identifier, alias + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, Strings...> tpl) { + stream_identifier(ss, tpl, streaming_identifier.offset_index(std::index_sequence_for{})); + return ss; + } + + // stream a container of identifiers described by a string or a tuple, which is one of: + // 1. identifier + // 1. tuple(identifier) + // 2. tuple(identifier, alias), pair(identifier, alias) + // 3. tuple(qualifier, identifier, alias) + // + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, std::tuple&, C> tpl) { + const auto& identifiers = get<1>(tpl); + + constexpr std::array sep = {", ", ""}; + bool first = true; + for(auto& identifier: identifiers) { + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, identifier); + } + return ss; + } + + // stream placeholders as part of a values clause + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, Ts...> tpl) { + const size_t& columnsCount = get<1>(tpl); + const ptrdiff_t& valuesCount = get<2>(tpl); + + if(!valuesCount || !columnsCount) { + return ss; + } + + std::string result; + result.reserve((1 + (columnsCount * 1) + (columnsCount * 2 - 2) + 1) * valuesCount + (valuesCount * 2 - 2)); + + constexpr std::array sep = {", ", ""}; + for(ptrdiff_t i = 0, first = true; i < valuesCount; ++i) { + result += sep[std::exchange(first, false)]; + result += "("; + for(size_t i = 0, first = true; i < columnsCount; ++i) { + result += sep[std::exchange(first, false)]; + result += "?"; + } + result += ")"; + } + ss << result; + return ss; + } + + // stream a table's column identifiers, possibly qualified; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, Table, const bool&> tpl) { + const auto& table = get<1>(tpl); + const bool& qualified = get<2>(tpl); + + table.for_each_column([&ss, &tableName = qualified ? table.name : std::string{}, first = true]( + const column_identifier& column) mutable { + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, tableName, column.name, std::string{}); + }); + return ss; + } + + // stream a table's non-generated column identifiers, unqualified; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, Table> tpl) { + const auto& table = get<1>(tpl); + + table.template for_each_column_excluding( + [&ss, first = true](const column_identifier& column) mutable { + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, column.name); + }); + return ss; + } + + // stream a table's non-generated column identifiers, unqualified; + // comma-separated + template + std::ostream& + operator<<(std::ostream& ss, + std::tuple&, PredFnCls, L, Ctx, Obj> tpl) { + using check_if_excluded = polyfill::remove_cvref_t>; + auto& excluded = get<2>(tpl); + auto& context = get<3>(tpl); + auto& object = get<4>(tpl); + using object_type = polyfill::remove_cvref_t; + auto& table = pick_table(context.db_objects); + + table.template for_each_column_excluding(call_as_template_base( + [&ss, &excluded, &context, &object, first = true](auto& column) mutable { + if(excluded(column)) { + return; + } + + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] + << serialize(polyfill::invoke(column.member_pointer, object), context); + })); + return ss; + } + + // stream a tuple of mapped columns (which are member pointers or column pointers); + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, T, Ctx> tpl) { + const auto& columns = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(columns, [&ss, &context, first = true](auto& colRef) mutable { + const std::string* columnName = find_column_name(context.db_objects, colRef); + if(!columnName) { + throw std::system_error{orm_error_code::column_not_found}; + } + + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, *columnName); + }); + return ss; + } + + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, + const column_constraints&, + const bool&, + Ctx> tpl) { + const auto& column = get<1>(tpl); + const bool& isNotNull = get<2>(tpl); + auto& context = get<3>(tpl); + + using constraints_type = constraints_type_t>; + constexpr size_t constraintsCount = std::tuple_size::value; + if(constraintsCount) { + std::vector constraintsStrings; + constraintsStrings.reserve(constraintsCount); + int primaryKeyIndex = -1; + int autoincrementIndex = -1; + int tupleIndex = 0; + iterate_tuple(column.constraints, + [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context]( + auto& constraint) { + using constraint_type = std::decay_t; + constraintsStrings.push_back(serialize(constraint, context)); + if(is_primary_key_v) { + primaryKeyIndex = tupleIndex; + } else if(is_autoincrement_v) { + autoincrementIndex = tupleIndex; + } + ++tupleIndex; + }); + if(primaryKeyIndex != -1 && autoincrementIndex != -1 && autoincrementIndex < primaryKeyIndex) { + iter_swap(constraintsStrings.begin() + primaryKeyIndex, + constraintsStrings.begin() + autoincrementIndex); + } + for(auto& str: constraintsStrings) { + ss << str << ' '; + } + } + if(isNotNull) { + ss << "NOT NULL "; + } + + return ss; + } + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/statement_binder.h b/libs/sqlite_orm-1.8.2/dev/statement_binder.h new file mode 100644 index 0000000..b16fe3c --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/statement_binder.h @@ -0,0 +1,361 @@ +#pragma once + +#include +#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type, std::make_index_sequence, std::index_sequence +#include // std::default_delete +#include // std::string, std::wstring +#include // std::vector +#include // ::strncpy, ::strlen +#include "functional/cxx_string_view.h" +#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // ::wcsncpy, ::wcslen +#endif + +#include "functional/cxx_universal.h" +#include "functional/cxx_type_traits_polyfill.h" +#include "functional/cxx_functional_polyfill.h" +#include "is_std_ptr.h" +#include "tuple_helper/tuple_filter.h" +#include "error_code.h" +#include "arithmetic_tag.h" +#include "xdestroy_handling.h" +#include "pointer_value.h" + +namespace sqlite_orm { + + /** + * Helper class used for binding fields to sqlite3 statements. + */ + template + struct statement_binder; + + namespace internal { + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_bindable_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_bindable_v())>> = true + // note : msvc 14.0 needs the parentheses constructor, otherwise `is_bindable` isn't recognised. + // The strangest thing is that this is mutually exclusive with `is_printable_v`. + ; + + template + using is_bindable = polyfill::bool_constant>; + + } + + /** + * Specialization for 'pointer-passing interface'. + */ + template + struct statement_binder, void> { + using V = pointer_binding; + + // ownership of pointed-to-object is left untouched and remains at prepared statement's AST expression + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + // note: C-casting `P* -> void*`, internal::xdestroy_proxy() does the inverse + return sqlite3_bind_pointer(stmt, index, (void*)value.ptr(), T::value, null_xdestroy_f); + } + + // ownership of pointed-to-object is transferred to sqlite + void result(sqlite3_context* context, V& value) const { + // note: C-casting `P* -> void*`, + // row_extractor>::extract() and internal::xdestroy_proxy() do the inverse + sqlite3_result_pointer(context, (void*)value.take_ptr(), T::value, value.get_xdestroy()); + } + }; + + /** + * Specialization for arithmetic types. + */ + template + struct statement_binder::value>> { + + 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, int_or_smaller_tag) const { + return sqlite3_bind_int(stmt, index, static_cast(value)); + } + + void result(sqlite3_context* context, const V& value, int_or_smaller_tag) const { + sqlite3_result_int(context, static_cast(value)); + } + + int bind(sqlite3_stmt* stmt, int index, const V& value, bigint_tag) const { + return sqlite3_bind_int64(stmt, index, static_cast(value)); + } + + void result(sqlite3_context* context, const V& value, bigint_tag) const { + sqlite3_result_int64(context, static_cast(value)); + } + + int bind(sqlite3_stmt* stmt, int index, const V& value, real_tag) const { + return sqlite3_bind_double(stmt, index, static_cast(value)); + } + + void result(sqlite3_context* context, const V& value, real_tag) const { + sqlite3_result_double(context, static_cast(value)); + } + }; + + /** + * Specialization for std::string and C-string. + */ + template + struct statement_binder, + std::is_same +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + , + std::is_same +#endif + >>> { + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + auto stringData = this->string_data(value); + return sqlite3_bind_text(stmt, index, stringData.first, stringData.second, SQLITE_TRANSIENT); + } + + void result(sqlite3_context* context, const V& value) const { + auto stringData = this->string_data(value); + auto dataCopy = new char[stringData.second + 1]; + constexpr auto deleter = std::default_delete{}; + ::strncpy(dataCopy, stringData.first, stringData.second + 1); + sqlite3_result_text(context, dataCopy, stringData.second, obtain_xdestroy_for(deleter, dataCopy)); + } + + private: +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::pair string_data(const std::string_view& s) const { + return {s.data(), int(s.size())}; + } +#else + std::pair string_data(const std::string& s) const { + return {s.c_str(), int(s.size())}; + } + + std::pair string_data(const char* s) const { + return {s, int(::strlen(s))}; + } +#endif + }; + +#ifndef SQLITE_ORM_OMITS_CODECVT + template + struct statement_binder, + std::is_same +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + , + std::is_same +#endif + >>> { + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + auto stringData = this->string_data(value); + std::wstring_convert> converter; + std::string utf8Str = converter.to_bytes(stringData.first, stringData.first + stringData.second); + return statement_binder().bind(stmt, index, utf8Str); + } + + void result(sqlite3_context* context, const V& value) const { + auto stringData = this->string_data(value); + sqlite3_result_text16(context, stringData.first, stringData.second, nullptr); + } + + private: +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::pair string_data(const std::wstring_view& s) const { + return {s.data(), int(s.size())}; + } +#else + std::pair string_data(const std::wstring& s) const { + return {s.c_str(), int(s.size())}; + } + + std::pair string_data(const wchar_t* s) const { + return {s, int(::wcslen(s))}; + } +#endif + }; +#endif + + /** + * Specialization for nullptr_t. + */ + template<> + struct statement_binder { + int bind(sqlite3_stmt* stmt, int index, const nullptr_t&) const { + return sqlite3_bind_null(stmt, index); + } + + void result(sqlite3_context* context, const 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&) 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 + + template + struct statement_binder< + V, + std::enable_if_t::value && internal::is_bindable_v>>> { + using unqualified_type = std::remove_cv_t; + + 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); + } + } + }; + + /** + * Specialization for binary data (std::vector). + */ + template<> + struct statement_binder, void> { + int bind(sqlite3_stmt* stmt, int index, const std::vector& value) const { + if(!value.empty()) { + 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.empty()) { + sqlite3_result_blob(context, (const void*)&value.front(), int(value.size()), nullptr); + } else { + sqlite3_result_blob(context, "", 0, nullptr); + } + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_binder && + internal::is_bindable_v>>> { + using unqualified_type = std::remove_cv_t; + + 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, std::nullopt); + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + namespace internal { + + struct conditional_binder { + sqlite3_stmt* stmt = nullptr; + int index = 1; + + explicit conditional_binder(sqlite3_stmt* stmt) : stmt{stmt} {} + + template = true> + void operator()(const T& t) { + int rc = statement_binder{}.bind(this->stmt, this->index++, t); + if(SQLITE_OK != rc) { + throw_translated_sqlite_error(this->stmt); + } + } + + template = true> + void operator()(const T&) const {} + }; + + struct field_value_binder : conditional_binder { + using conditional_binder::conditional_binder; + using conditional_binder::operator(); + + template = true> + void operator()(const T&) const = delete; + + template + void operator()(const T* value) { + if(!value) { + throw std::system_error{orm_error_code::value_is_null}; + } + (*this)(*value); + } + }; + + struct tuple_value_binder { + sqlite3_stmt* stmt = nullptr; + + explicit tuple_value_binder(sqlite3_stmt* stmt) : stmt{stmt} {} + + template + void operator()(const Tpl& tpl, Projection project) const { + (*this)(tpl, + std::make_index_sequence::value>{}, + std::forward(project)); + } + + private: +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { + (this->bind(polyfill::invoke(project, std::get(tpl)), Idx), ...); + } +#else + template + void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { + this->bind(polyfill::invoke(project, std::get(tpl)), I); + (*this)(tpl, std::index_sequence{}, std::forward(project)); + } + + template + void operator()(const Tpl&, std::index_sequence<>, Projection) const {} +#endif + + template + void bind(const T& t, size_t idx) const { + int rc = statement_binder{}.bind(this->stmt, int(idx + 1), t); + if(SQLITE_OK != rc) { + throw_translated_sqlite_error(this->stmt); + } + } + + template + void bind(const T* value, size_t idx) const { + if(!value) { + throw std::system_error{orm_error_code::value_is_null}; + } + (*this)(*value, idx); + } + }; + + template + using bindable_filter_t = filter_tuple_t; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/statement_finalizer.h b/libs/sqlite_orm-1.8.2/dev/statement_finalizer.h new file mode 100644 index 0000000..d949500 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/statement_finalizer.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include // std::unique_ptr +#include // std::integral_constant + +namespace sqlite_orm { + + /** + * Guard class which finalizes `sqlite3_stmt` in dtor + */ + using statement_finalizer = + std::unique_ptr>; +} diff --git a/libs/sqlite_orm-1.8.2/dev/statement_serializer.h b/libs/sqlite_orm-1.8.2/dev/statement_serializer.h new file mode 100644 index 0000000..c8c96e4 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/statement_serializer.h @@ -0,0 +1,2035 @@ +#pragma once + +#include // std::stringstream +#include // std::string +#include // std::enable_if, std::remove_pointer +#include // std::vector +#include // std::iter_swap +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::codecvt_utf8_utf16 +#endif // SQLITE_ORM_OMITS_CODECVT +#include +#include +#include "functional/cxx_string_view.h" +#include "functional/cxx_optional.h" + +#include "functional/cxx_universal.h" +#include "functional/cxx_functional_polyfill.h" +#include "functional/mpl.h" +#include "tuple_helper/tuple_filter.h" +#include "ast/upsert_clause.h" +#include "ast/excluded.h" +#include "ast/group_by.h" +#include "ast/into.h" +#include "core_functions.h" +#include "constraints.h" +#include "conditions.h" +#include "column.h" +#include "indexed_column.h" +#include "function.h" +#include "prepared_statement.h" +#include "rowid.h" +#include "pointer_value.h" +#include "type_printer.h" +#include "field_printer.h" +#include "literal.h" +#include "table_name_collector.h" +#include "column_names_getter.h" +#include "order_by_serializer.h" +#include "serializing_util.h" +#include "statement_binder.h" +#include "values.h" +#include "triggers.h" +#include "table_type_of.h" +#include "index.h" +#include "util.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct statement_serializer; + + template + std::string serialize(const T& t, const serializer_context& context) { + statement_serializer serializer; + return serializer(t, context); + } + + /** + * Serializer for bindable types. + */ + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const T& statement, const Ctx& context) const { + if(context.replace_bindable_with_question) { + return "?"; + } else { + return this->do_serialize(statement); + } + } + + private: + template && !std::is_base_of::value +#ifndef SQLITE_ORM_OMITS_CODECVT + && !std::is_base_of::value +#endif + , + bool> = true> + std::string do_serialize(const X& c) const { + static_assert(std::is_same::value, ""); + + // implementation detail: utilizing field_printer + return field_printer{}(c); + } + + std::string do_serialize(const std::string& c) const { + // implementation detail: utilizing field_printer + return quote_string_literal(field_printer{}(c)); + } + + std::string do_serialize(const char* c) const { + return quote_string_literal(c); + } +#ifndef SQLITE_ORM_OMITS_CODECVT + std::string do_serialize(const std::wstring& c) const { + // implementation detail: utilizing field_printer + return quote_string_literal(field_printer{}(c)); + } + + std::string do_serialize(const wchar_t* c) const { + std::wstring_convert> converter; + return quote_string_literal(converter.to_bytes(c)); + } +#endif +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::string do_serialize(const std::string_view& c) const { + return quote_string_literal(std::string(c)); + } +#ifndef SQLITE_ORM_OMITS_CODECVT + std::string do_serialize(const std::wstring_view& c) const { + std::wstring_convert> converter; + return quote_string_literal(converter.to_bytes(c.data(), c.data() + c.size())); + } +#endif +#endif + /** + * Specialization for binary data (std::vector). + */ + std::string do_serialize(const std::vector& t) const { + return quote_blob_literal(field_printer>{}(t)); + } + + template + std::string do_serialize(const pointer_binding&) const { + // always serialize null (security reasons) + return field_printer{}(nullptr); + } + }; + + /** + * Serializer for literal values. + */ + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const T& literal, const Ctx& context) const { + static_assert(is_bindable_v>, "A literal value must be also bindable"); + + Ctx literalCtx = context; + literalCtx.replace_bindable_with_question = false; + statement_serializer> serializer{}; + return serializer(literal.value, literalCtx); + } + }; + + template + struct statement_serializer, void> { + using statement_type = filtered_aggregate_function; + + template + std::string operator()(const statement_type& statement, const Ctx& context) { + std::stringstream ss; + ss << serialize(statement.function, context); + ss << " FILTER (WHERE " << serialize(statement.where, context) << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = excluded_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "excluded."; + if(auto* columnName = find_column_name(context.db_objects, statement.expression)) { + ss << streaming_identifier(*columnName); + } else { + throw std::system_error{orm_error_code::column_not_found}; + } + return ss.str(); + } + }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializer, void> { + using statement_type = as_optional_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + return serialize(statement.value, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializer, void> { + using statement_type = std::reference_wrapper; + + template + std::string operator()(const statement_type& s, const Ctx& context) const { + return serialize(s.get(), context); + } + }; + + template + struct statement_serializer, void> { + using statement_type = alias_holder; + + template + std::string operator()(const statement_type&, const Ctx&) { + std::stringstream ss; + ss << streaming_identifier(T::get()); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = column_alias; + + template + std::string operator()(const statement_type&, const Ctx&) { + std::stringstream ss; + ss << streaming_identifier(statement_type::get()); + return ss.str(); + } + }; + + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "ON CONFLICT"; + iterate_tuple(statement.target_args, [&ss, &context](auto& value) { + using value_type = std::decay_t; + 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 { + auto updateContext = context; + updateContext.use_parentheses = false; + ss << " UPDATE " << streaming_actions_tuple(statement.actions, updateContext); + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = built_in_function_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << statement.serialize() << "(" << streaming_expressions_tuple(statement.args, context) << ")"; + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> + : statement_serializer, void> {}; + + template + struct statement_serializer, void> { + using statement_type = function_call; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << F::name() << "(" << streaming_expressions_tuple(statement.args, context) << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = as_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.expression, context) + " AS " << streaming_identifier(alias_extractor::extract()); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = alias_column_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + if(!context.skip_table_name) { + ss << streaming_identifier(alias_extractor::extract()) << "."; + } + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(c.column, newContext); + return ss.str(); + } + }; + + template + struct statement_serializer< + E, + std::enable_if_t, is_column_pointer>>> { + using statement_type = E; + + template + std::string operator()(const statement_type& e, const Ctx& context) const { + std::stringstream ss; + if(auto* columnName = find_column_name(context.db_objects, e)) { + ss << streaming_identifier( + !context.skip_table_name ? lookup_table_name>(context.db_objects) : "", + *columnName, + ""); + } else { + throw std::system_error{orm_error_code::column_not_found}; + } + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = rowid_t; + + template + std::string operator()(const statement_type& s, const Ctx&) const { + return static_cast(s); + } + }; + + template<> + struct statement_serializer { + using statement_type = oid_t; + + template + std::string operator()(const statement_type& s, const Ctx&) const { + return static_cast(s); + } + }; + + template<> + struct statement_serializer<_rowid_t, void> { + using statement_type = _rowid_t; + + template + std::string operator()(const statement_type& s, const Ctx&) const { + return static_cast(s); + } + }; + + template + struct statement_serializer, void> { + using statement_type = table_rowid_t; + + template + std::string operator()(const statement_type& s, const Ctx& context) const { + std::stringstream ss; + if(!context.skip_table_name) { + ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; + } + ss << static_cast(s); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = table_oid_t; + + template + std::string operator()(const statement_type& s, const Ctx& context) const { + std::stringstream ss; + if(!context.skip_table_name) { + ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; + } + ss << static_cast(s); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = table__rowid_t; + + template + std::string operator()(const statement_type& s, const Ctx& context) const { + std::stringstream ss; + if(!context.skip_table_name) { + ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; + } + ss << static_cast(s); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = binary_operator; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + auto lhs = serialize(statement.lhs, context); + auto rhs = serialize(statement.rhs, context); + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << lhs << " " << statement.serialize() << " " << rhs; + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = count_asterisk_t; + + template + std::string operator()(const statement_type&, const Ctx& context) const { + return serialize(count_asterisk_without_type{}, context); + } + }; + + template<> + struct statement_serializer { + using statement_type = count_asterisk_without_type; + + template + std::string operator()(const statement_type& c, const Ctx&) const { + std::stringstream ss; + auto functionName = c.serialize(); + ss << functionName << "(*)"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = distinct_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + auto expr = serialize(c.value, context); + ss << static_cast(c) << "(" << expr << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = all_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + auto expr = serialize(c.value, context); + ss << static_cast(c) << "(" << expr << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = cast_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(c) << " ("; + ss << serialize(c.expression, context) << " AS " << type_printer().print() << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.left, context) << " "; + ss << static_cast(c) << " "; + ss << serialize(c.right, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = simple_case_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << "CASE "; + c.case_expression.apply([&ss, context](auto& c_) { + ss << serialize(c_, context) << " "; + }); + 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) { + ss << "ELSE " << serialize(el, context) << " "; + }); + ss << "END"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = is_null_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.t, context) << " " << static_cast(c); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = is_not_null_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.t, context) << " " << static_cast(c); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = bitwise_not_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << statement.serialize() << " "; + auto cString = serialize(statement.argument, context); + ss << " (" << cString << " )"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = negated_condition_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(c) << " "; + auto cString = serialize(c.c, context); + ss << " (" << cString << " )"; + return ss.str(); + } + }; + + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + auto leftString = serialize(c.l, context); + auto rightString = serialize(c.r, context); + std::stringstream ss; + if(context.use_parentheses) { + ss << "("; + } + ss << leftString << " " << static_cast(c) << " " << rightString; + if(context.use_parentheses) { + ss << ")"; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = named_collate; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + auto newContext = context; + newContext.use_parentheses = false; + auto res = serialize(c.expr, newContext); + return res + " " + static_cast(c); + } + }; + + template + struct statement_serializer, void> { + using statement_type = collate_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + auto newContext = context; + newContext.use_parentheses = false; + auto res = serialize(c.expr, newContext); + return res + " " + static_cast(c); + } + }; + + template + struct statement_serializer, void> { + using statement_type = dynamic_in_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto leftString = serialize(statement.left, context); + ss << leftString << " "; + if(!statement.negative) { + ss << "IN"; + } else { + ss << "NOT IN"; + } + ss << " "; + if(is_compound_operator_v) { + ss << '('; + } + auto newContext = context; + newContext.use_parentheses = true; + ss << serialize(statement.argument, newContext); + if(is_compound_operator_v) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer>, void> { + using statement_type = dynamic_in_t>; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto leftString = serialize(statement.left, context); + ss << leftString << " "; + if(!statement.negative) { + ss << "IN"; + } else { + ss << "NOT IN"; + } + ss << " (" << streaming_dynamic_expressions(statement.argument, context) << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = in_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto leftString = serialize(statement.left, context); + ss << leftString << " "; + if(!statement.negative) { + ss << "IN"; + } else { + ss << "NOT IN"; + } + ss << " "; + using args_type = std::tuple; + constexpr bool theOnlySelect = + std::tuple_size::value == 1 && is_select_v>; + if(!theOnlySelect) { + ss << "("; + } + ss << streaming_expressions_tuple(statement.argument, context); + if(!theOnlySelect) { + ss << ")"; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = like_t; + + template + std::string operator()(const statement_type& c, const Ctx& 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) { + ss << " ESCAPE " << serialize(value, context); + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = glob_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.arg, context) << " "; + ss << static_cast(c) << " "; + ss << serialize(c.pattern, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = between_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + auto expr = serialize(c.expr, context); + ss << expr << " " << static_cast(c) << " "; + ss << serialize(c.b1, context); + ss << " AND "; + ss << serialize(c.b2, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = exists_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.use_parentheses = true; + ss << "EXISTS " << serialize(statement.expression, newContext); + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = autoincrement_t; + + template + std::string operator()(const statement_type&, const Ctx&) const { + return "AUTOINCREMENT"; + } + }; + + template<> + struct statement_serializer { + using statement_type = conflict_clause_t; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + switch(statement) { + case conflict_clause_t::rollback: + return "ROLLBACK"; + case conflict_clause_t::abort: + return "ABORT"; + case conflict_clause_t::fail: + return "FAIL"; + case conflict_clause_t::ignore: + return "IGNORE"; + case conflict_clause_t::replace: + return "REPLACE"; + } + return {}; + } + }; + + template + struct statement_serializer, void> { + using statement_type = primary_key_with_autoincrement; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + return serialize(statement.primary_key, context) + " AUTOINCREMENT"; + } + }; + + template + struct statement_serializer, void> { + using statement_type = primary_key_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "PRIMARY KEY"; + switch(statement.options.asc_option) { + case statement_type::order_by::ascending: + ss << " ASC"; + break; + case statement_type::order_by::descending: + ss << " DESC"; + break; + default: + break; + } + if(statement.options.conflict_clause_is_on) { + ss << " ON CONFLICT " << serialize(statement.options.conflict_clause, context); + } + using columns_tuple = typename statement_type::columns_tuple; + const size_t columnsCount = std::tuple_size::value; + if(columnsCount) { + ss << "(" << streaming_mapped_columns_expressions(statement.columns, context) << ")"; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = unique_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(c); + using columns_tuple = typename statement_type::columns_tuple; + const size_t columnsCount = std::tuple_size::value; + if(columnsCount) { + ss << "(" << streaming_mapped_columns_expressions(c.columns, context) << ")"; + } + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = collate_constraint_t; + + template + std::string operator()(const statement_type& c, const Ctx&) const { + return static_cast(c); + } + }; + + template + struct statement_serializer, void> { + using statement_type = default_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + return static_cast(c) + " (" + serialize(c.value, context) + ")"; + } + }; + + template + struct statement_serializer, std::tuple>, void> { + using statement_type = foreign_key_t, std::tuple>; + + template + std::string operator()(const statement_type& fk, const Ctx& context) const { + std::stringstream ss; + ss << "FOREIGN KEY(" << streaming_mapped_columns_expressions(fk.columns, context) << ") REFERENCES "; + { + using references_type_t = typename std::decay_t::references_type; + using first_reference_t = std::tuple_element_t<0, references_type_t>; + using first_reference_mapped_type = table_type_of_t; + auto refTableName = lookup_table_name(context.db_objects); + ss << streaming_identifier(refTableName); + } + ss << "(" << streaming_mapped_columns_expressions(fk.references, context) << ")"; + if(fk.on_update) { + ss << ' ' << static_cast(fk.on_update) << " " << fk.on_update._action; + } + if(fk.on_delete) { + ss << ' ' << static_cast(fk.on_delete) << " " << fk.on_delete._action; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = check_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "CHECK (" << serialize(statement.expression, context) << ")"; + return ss.str(); + } + }; +#if SQLITE_VERSION_NUMBER >= 3031000 + template + struct statement_serializer, void> { + using statement_type = generated_always_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if(statement.full) { + ss << "GENERATED ALWAYS "; + } + ss << "AS ("; + ss << serialize(statement.expression, context) << ")"; + switch(statement.storage) { + case basic_generated_always::storage_type::not_specified: + //.. + break; + case basic_generated_always::storage_type::virtual_: + ss << " VIRTUAL"; + break; + case basic_generated_always::storage_type::stored: + ss << " STORED"; + break; + } + return ss.str(); + } + }; +#endif + template + struct statement_serializer, void> { + using statement_type = column_t; + + template + std::string operator()(const statement_type& column, const Ctx& context) const { + using column_type = statement_type; + + std::stringstream ss; + ss << streaming_identifier(column.name) << " " << type_printer>().print() + << " " + << streaming_column_constraints( + call_as_template_base(polyfill::identity{})(column), + column.is_not_null(), + context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = remove_all_t; + + template + std::string operator()(const statement_type& rem, const Ctx& context) const { + auto& table = pick_table(context.db_objects); + + std::stringstream ss; + ss << "DELETE FROM " << streaming_identifier(table.name) + << streaming_conditions_tuple(rem.conditions, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = replace_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using expression_type = std::decay_t; + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + std::stringstream ss; + ss << "REPLACE INTO " << streaming_identifier(table.name) << " (" + << streaming_non_generated_column_names(table) << ")" + << " VALUES (" + << streaming_field_values_excluding(check_if{}, + empty_callable(), // don't exclude + context, + get_ref(statement.object)) + << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = insert_explicit; + + template + std::string operator()(const statement_type& ins, const Ctx& context) const { + constexpr size_t colsCount = std::tuple_size>::value; + static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); + using expression_type = std::decay_t; + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(table.name) << " "; + ss << "(" << streaming_mapped_columns_expressions(ins.columns.columns, context) << ") " + << "VALUES ("; + iterate_tuple(ins.columns.columns, + [&ss, &context, &object = get_ref(ins.obj), first = true](auto& memberPointer) mutable { + using member_pointer_type = std::decay_t; + static_assert(!is_setter_v, + "Unable to use setter within insert explicit"); + + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] + << serialize(polyfill::invoke(memberPointer, object), context); + }); + ss << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = update_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using expression_type = std::decay_t; + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + + std::stringstream ss; + ss << "UPDATE " << streaming_identifier(table.name) << " SET "; + table.template for_each_column_excluding>( + [&table, &ss, &context, &object = get_ref(statement.object), first = true](auto& column) mutable { + if(table.exists_in_composite_primary_key(column)) { + return; + } + + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << streaming_identifier(column.name) << " = " + << serialize(polyfill::invoke(column.member_pointer, object), context); + }); + ss << " WHERE "; + table.for_each_column( + [&table, &context, &ss, &object = get_ref(statement.object), first = true](auto& column) mutable { + if(!column.template is() && !table.exists_in_composite_primary_key(column)) { + return; + } + + constexpr std::array sep = {" AND ", ""}; + ss << sep[std::exchange(first, false)] << streaming_identifier(column.name) << " = " + << serialize(polyfill::invoke(column.member_pointer, object), context); + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = dynamic_set_t; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + std::stringstream ss; + ss << "SET "; + int index = 0; + for(const dynamic_set_entry& entry: statement) { + if(index > 0) { + ss << ", "; + } + ss << entry.serialized_value; + ++index; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = set_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "SET "; + auto leftContext = context; + leftContext.skip_table_name = true; + iterate_tuple(statement.assigns, [&ss, &context, &leftContext, first = true](auto& value) mutable { + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << serialize(value.lhs, leftContext) << ' ' + << value.serialize() << ' ' << serialize(value.rhs, context); + }); + return ss.str(); + } + }; + + template + std::set> collect_table_names(const set_t& set, const Ctx& ctx) { + auto collector = make_table_name_collector(ctx.db_objects); + iterate_ast(set, collector); + return std::move(collector.table_names); + } + + template + const std::set>& collect_table_names(const dynamic_set_t& set, + const Ctx&) { + return set.collector.table_names; + } + + template = true> + std::set> collect_table_names(const T& sel, const Ctx& ctx) { + auto collector = make_table_name_collector(ctx.db_objects); + iterate_ast(sel.col, collector); + iterate_ast(sel.conditions, collector); + return std::move(collector.table_names); + } + + template = true> + std::set> collect_table_names(const T& table, const Ctx&) { + return {{table.name, ""}}; + } + + template + struct statement_serializer, void> { + using statement_type = update_all_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + const auto& tableNames = collect_table_names(statement.set, context); + if(tableNames.empty()) { + throw std::system_error{orm_error_code::no_tables_specified}; + } + const std::string& tableName = tableNames.begin()->first; + + std::stringstream ss; + ss << "UPDATE " << streaming_identifier(tableName) << ' ' << serialize(statement.set, context) + << streaming_conditions_tuple(statement.conditions, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = insert_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + using is_without_rowid = typename std::decay_t::is_without_rowid; + + std::vector> columnNames; + table.template for_each_column_excluding< + mpl::conjunction>, + mpl::disjunction_fn>>( + [&table, &columnNames](auto& column) { + if(table.exists_in_composite_primary_key(column)) { + return; + } + + columnNames.push_back(cref(column.name)); + }); + const size_t columnNamesCount = columnNames.size(); + + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(table.name) << " "; + if(columnNamesCount) { + ss << "(" << streaming_identifiers(columnNames) << ")"; + } else { + ss << "DEFAULT"; + } + ss << " VALUES"; + if(columnNamesCount) { + ss << " (" + << streaming_field_values_excluding( + mpl::conjunction>, + mpl::disjunction_fn>{}, + [&table](auto& column) { + return table.exists_in_composite_primary_key(column); + }, + context, + get_ref(statement.object)) + << ")"; + } + + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = into_t; + + template + std::string operator()(const statement_type&, const Ctx& context) const { + auto& table = pick_table(context.db_objects); + + std::stringstream ss; + ss << "INTO " << streaming_identifier(table.name); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = columns_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + // note: pass `statement` itself + ss << streaming_serialized(get_column_names(statement, context)); + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer, is_replace_raw>>> { + using statement_type = T; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if(is_insert_raw_v) { + ss << "INSERT"; + } else { + ss << "REPLACE"; + } + iterate_tuple(statement.args, [&context, &ss](auto& value) { + using value_type = std::decay_t; + ss << ' '; + if(is_columns_v) { + auto newContext = context; + newContext.skip_table_name = true; + newContext.use_parentheses = true; + ss << serialize(value, newContext); + } else if(is_values_v || is_select_v) { + auto newContext = context; + newContext.use_parentheses = false; + ss << serialize(value, newContext); + } else { + ss << serialize(value, context); + } + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = remove_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + auto& table = pick_table(context.db_objects); + std::stringstream ss; + ss << "DELETE FROM " << streaming_identifier(table.name) << " " + << "WHERE "; + std::vector idsStrings; + idsStrings.reserve(std::tuple_size::value); + iterate_tuple(statement.ids, [&idsStrings, &context](auto& idValue) { + idsStrings.push_back(serialize(idValue, context)); + }); + table.for_each_primary_key_column([&table, &ss, &idsStrings, index = 0](auto& memberPointer) mutable { + auto* columnName = table.find_column_name(memberPointer); + if(!columnName) { + throw std::system_error{orm_error_code::column_not_found}; + } + + constexpr std::array sep = {" AND ", ""}; + ss << sep[index == 0] << streaming_identifier(*columnName) << " = " << idsStrings[index]; + ++index; + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = replace_range_t; + + template + std::string operator()(const statement_type& rep, const Ctx& context) const { + using expression_type = std::decay_t; + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + + std::stringstream ss; + ss << "REPLACE INTO " << streaming_identifier(table.name) << " (" + << streaming_non_generated_column_names(table) << ")"; + const auto valuesCount = std::distance(rep.range.first, rep.range.second); + const auto columnsCount = table.non_generated_columns_count(); + ss << " VALUES " << streaming_values_placeholders(columnsCount, valuesCount); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = insert_range_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + using is_without_rowid = typename std::decay_t::is_without_rowid; + + std::vector> columnNames; + table.template for_each_column_excluding< + mpl::conjunction>, + mpl::disjunction_fn>>( + [&table, &columnNames](auto& column) { + if(table.exists_in_composite_primary_key(column)) { + return; + } + + columnNames.push_back(cref(column.name)); + }); + const size_t valuesCount = std::distance(statement.range.first, statement.range.second); + const size_t columnNamesCount = columnNames.size(); + + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(table.name) << " "; + if(columnNamesCount) { + ss << "(" << streaming_identifiers(columnNames) << ")"; + } else { + ss << "DEFAULT"; + } + ss << " VALUES "; + if(columnNamesCount) { + ss << streaming_values_placeholders(columnNamesCount, valuesCount); + } else if(valuesCount != 1) { + throw std::system_error{orm_error_code::cannot_use_default_value}; + } + return ss.str(); + } + }; + + template + std::string serialize_get_all_impl(const T& get, const Ctx& context) { + using primary_type = type_t; + + auto& table = pick_table(context.db_objects); + auto tableNames = collect_table_names(table, context); + + std::stringstream ss; + ss << "SELECT " << streaming_table_column_names(table, true) << " FROM " + << streaming_identifiers(tableNames) << streaming_conditions_tuple(get.conditions, context); + return ss.str(); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializer, void> { + using statement_type = get_all_optional_t; + + template + std::string operator()(const statement_type& get, const Ctx& context) const { + return serialize_get_all_impl(get, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct statement_serializer, void> { + using statement_type = get_all_pointer_t; + + template + std::string operator()(const statement_type& get, const Ctx& context) const { + return serialize_get_all_impl(get, context); + } + }; + + template + struct statement_serializer, void> { + using statement_type = get_all_t; + + template + std::string operator()(const statement_type& get, const Ctx& context) const { + return serialize_get_all_impl(get, context); + } + }; + + template + std::string serialize_get_impl(const T&, const Ctx& context) { + using primary_type = type_t; + auto& table = pick_table(context.db_objects); + std::stringstream ss; + ss << "SELECT " << streaming_table_column_names(table, false) << " FROM " + << streaming_identifier(table.name) << " WHERE "; + + auto primaryKeyColumnNames = table.primary_key_column_names(); + if(primaryKeyColumnNames.empty()) { + throw std::system_error{orm_error_code::table_has_no_primary_key_column}; + } + + for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { + if(i > 0) { + ss << " AND "; + } + ss << streaming_identifier(primaryKeyColumnNames[i]) << " = ?"; + } + return ss.str(); + } + + template + struct statement_serializer, void> { + using statement_type = get_t; + + template + std::string operator()(const statement_type& get, const Ctx& context) const { + return serialize_get_impl(get, context); + } + }; + + template + struct statement_serializer, void> { + using statement_type = get_pointer_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + return serialize_get_impl(statement, context); + } + }; + + template<> + struct statement_serializer { + using statement_type = conflict_action; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + switch(statement) { + case conflict_action::replace: + return "REPLACE"; + case conflict_action::abort: + return "ABORT"; + case conflict_action::fail: + return "FAIL"; + case conflict_action::ignore: + return "IGNORE"; + case conflict_action::rollback: + return "ROLLBACK"; + } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = insert_constraint; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + return "OR " + serialize(statement.action, context); + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializer, void> { + using statement_type = get_optional_t; + + template + std::string operator()(const statement_type& get, const Ctx& context) const { + return serialize_get_impl(get, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializer, void> { + using statement_type = select_t; + + template + std::string operator()(const statement_type& sel, Ctx context) const { + context.skip_table_name = false; + + std::stringstream ss; + if(!is_compound_operator_v) { + if(!sel.highest_level && context.use_parentheses) { + ss << "("; + } + ss << "SELECT "; + } + if(get_distinct(sel.col)) { + ss << static_cast(distinct(0)) << " "; + } + ss << streaming_serialized(get_column_names(sel.col, context)); + using conditions_tuple = typename statement_type::conditions_type; + constexpr bool hasExplicitFrom = tuple_has::value; + if(!hasExplicitFrom) { + auto tableNames = collect_table_names(sel, context); + using joins_index_sequence = filter_tuple_sequence_t; + // deduplicate table names of constrained join statements + iterate_tuple(sel.conditions, joins_index_sequence{}, [&tableNames, &context](auto& join) { + using original_join_type = typename std::decay_t::type; + using cross_join_type = mapped_type_proxy_t; + std::pair tableNameWithAlias{ + lookup_table_name(context.db_objects), + alias_extractor::as_alias()}; + tableNames.erase(tableNameWithAlias); + }); + if(!tableNames.empty() && !is_compound_operator_v) { + ss << " FROM " << streaming_identifiers(tableNames); + } + } + ss << streaming_conditions_tuple(sel.conditions, context); + if(!is_compound_operator_v) { + if(!sel.highest_level && context.use_parentheses) { + ss << ")"; + } + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = indexed_column_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << serialize(statement.column_or_expression, context); + if(!statement._collation_name.empty()) { + ss << " COLLATE " << statement._collation_name; + } + if(statement._order) { + switch(statement._order) { + case -1: + ss << " DESC"; + break; + case 1: + ss << " ASC"; + break; + default: + throw std::system_error{orm_error_code::incorrect_order}; + } + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = index_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "CREATE "; + if(statement.unique) { + ss << "UNIQUE "; + } + using indexed_type = typename std::decay_t::table_mapped_type; + ss << "INDEX IF NOT EXISTS " << streaming_identifier(statement.name) << " ON " + << streaming_identifier(lookup_table_name(context.db_objects)); + std::vector columnNames; + std::string whereString; + iterate_tuple(statement.elements, [&columnNames, &context, &whereString](auto& value) { + using value_type = std::decay_t; + if(!is_where_v) { + auto newContext = context; + newContext.use_parentheses = false; + auto whereString = serialize(value, newContext); + columnNames.push_back(std::move(whereString)); + } else { + auto columnName = serialize(value, context); + whereString = std::move(columnName); + } + }); + ss << " (" << streaming_serialized(columnNames) << ")"; + if(!whereString.empty()) { + ss << ' ' << whereString; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = from_t; + + template + std::string operator()(const statement_type&, const Ctx& context) const { + using tuple = std::tuple; + + std::stringstream ss; + ss << "FROM "; + iterate_tuple([&context, &ss, first = true](auto* item) mutable { + using from_type = std::remove_pointer_t; + + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] + << streaming_identifier(lookup_table_name>(context.db_objects), + alias_extractor::as_alias()); + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = old_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "OLD."; + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(statement.expression, newContext); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = new_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "NEW."; + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(statement.expression, newContext); + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = raise_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + switch(statement.type) { + case raise_t::type_t::ignore: + return "RAISE(IGNORE)"; + + case raise_t::type_t::rollback: + return "RAISE(ROLLBACK, " + serialize(statement.message, context) + ")"; + + case raise_t::type_t::abort: + return "RAISE(ABORT, " + serialize(statement.message, context) + ")"; + + case raise_t::type_t::fail: + return "RAISE(FAIL, " + serialize(statement.message, context) + ")"; + } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = trigger_timing; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + switch(statement) { + case trigger_timing::trigger_before: + return "BEFORE"; + case trigger_timing::trigger_after: + return "AFTER"; + case trigger_timing::trigger_instead_of: + return "INSTEAD OF"; + } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = trigger_type; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + switch(statement) { + case trigger_type::trigger_delete: + return "DELETE"; + case trigger_type::trigger_insert: + return "INSERT"; + case trigger_type::trigger_update: + return "UPDATE"; + } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = trigger_type_base_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << serialize(statement.timing, context) << " " << serialize(statement.type, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = trigger_update_type_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << serialize(statement.timing, context) << " UPDATE OF " + << streaming_mapped_columns_expressions(statement.columns, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = trigger_base_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << serialize(statement.type_base, context); + ss << " ON " << streaming_identifier(lookup_table_name(context.db_objects)); + if(statement.do_for_each_row) { + ss << " FOR EACH ROW"; + } + statement.container_when.apply([&ss, &context](auto& value) { + ss << " WHEN " << serialize(value, context); + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = trigger_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "CREATE "; + + ss << "TRIGGER IF NOT EXISTS " << streaming_identifier(statement.name) << " " + << serialize(statement.base, context); + ss << " BEGIN "; + iterate_tuple(statement.elements, [&ss, &context](auto& element) { + using element_type = std::decay_t; + if(is_select_v) { + auto newContext = context; + newContext.use_parentheses = false; + ss << serialize(element, newContext); + } else { + ss << serialize(element, context); + } + ss << ";"; + }); + ss << " END"; + + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = where_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << statement.serialize() << " "; + auto whereString = serialize(statement.expression, context); + ss << '(' << whereString << ')'; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(orderBy) << " "; + ss << serialize_order_by(orderBy, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = dynamic_order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { + return serialize_order_by(orderBy, context); + } + }; + + template + struct statement_serializer, void> { + using statement_type = multi_order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(orderBy) << " " << streaming_expressions_tuple(orderBy.args, context); + return ss.str(); + } + }; + + template + struct statement_serializer< + Join, + std::enable_if_t, + polyfill::is_specialization_of>>> { + using statement_type = Join; + + template + std::string operator()(const statement_type& join, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(join) << " " + << streaming_identifier(lookup_table_name>(context.db_objects)); + return ss.str(); + } + }; + + template + struct statement_serializer> { + using statement_type = Join; + + template + std::string operator()(const statement_type& join, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(join) << " " + << streaming_identifier(lookup_table_name>>(context.db_objects), + alias_extractor>::as_alias()) + << " " << serialize(join.constraint, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = on_t; + + template + std::string operator()(const statement_type& on, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << static_cast(on) << " " << serialize(on.arg, newContext) << " "; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = group_by_with_having; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << "GROUP BY " << streaming_expressions_tuple(statement.args, newContext) << " HAVING " + << serialize(statement.expression, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = group_by_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << "GROUP BY " << streaming_expressions_tuple(statement.args, newContext); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = having_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << "HAVING " << serialize(statement.expression, newContext); + return ss.str(); + } + }; + + /** + * HO - has offset + * OI - offset is implicit + */ + template + struct statement_serializer, void> { + using statement_type = limit_t; + + template + std::string operator()(const statement_type& limt, const Ctx& 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) { + ss << serialize(value, newContext); + }); + ss << ", "; + ss << serialize(limt.lim, newContext); + } else { + ss << serialize(limt.lim, newContext) << " OFFSET "; + limt.off.apply([&newContext, &ss](auto& value) { + ss << serialize(value, newContext); + }); + } + } else { + ss << serialize(limt.lim, newContext); + } + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = default_values_t; + + template + std::string operator()(const statement_type&, const Ctx&) const { + return "DEFAULT VALUES"; + } + }; + + template + struct statement_serializer, void> { + using statement_type = using_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + auto newContext = context; + newContext.skip_table_name = true; + return static_cast(statement) + " (" + serialize(statement.column, newContext) + ")"; + } + }; + + template + struct statement_serializer, void> { + using statement_type = std::tuple; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << streaming_expressions_tuple(statement, context); + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = values_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << "VALUES "; + { + Ctx tupleContext = context; + tupleContext.use_parentheses = true; + ss << streaming_expressions_tuple(statement.tuple, tupleContext); + } + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = dynamic_values_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << "VALUES " << streaming_dynamic_expressions(statement.vector, context); + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/storage.h b/libs/sqlite_orm-1.8.2/dev/storage.h new file mode 100644 index 0000000..fda117d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/storage.h @@ -0,0 +1,1437 @@ +#pragma once + +#include +#include // std::unique_ptr/shared_ptr, std::make_unique/std::make_shared +#include // std::system_error +#include // std::string +#include // std::remove_reference, std::is_base_of, std::decay, std::false_type, std::true_type +#include // std::identity +#include // std::stringstream +#include // std::map +#include // std::vector +#include // std::tuple_size, std::tuple, std::make_tuple, std::tie +#include // std::forward, std::pair +#include // std::for_each, std::ranges::for_each +#include "functional/cxx_optional.h" + +#include "functional/cxx_universal.h" +#include "functional/cxx_functional_polyfill.h" +#include "functional/static_magic.h" +#include "functional/mpl.h" +#include "tuple_helper/tuple_traits.h" +#include "tuple_helper/tuple_filter.h" +#include "tuple_helper/tuple_iteration.h" +#include "type_traits.h" +#include "alias.h" +#include "row_extractor_builder.h" +#include "error_code.h" +#include "type_printer.h" +#include "constraints.h" +#include "field_printer.h" +#include "rowid.h" +#include "operators.h" +#include "select_constraints.h" +#include "core_functions.h" +#include "conditions.h" +#include "statement_binder.h" +#include "column_result.h" +#include "mapped_type_proxy.h" +#include "sync_schema_result.h" +#include "table_info.h" +#include "storage_impl.h" +#include "journal_mode.h" +#include "view.h" +#include "ast_iterator.h" +#include "storage_base.h" +#include "prepared_statement.h" +#include "expression_object_type.h" +#include "statement_serializer.h" +#include "triggers.h" +#include "object_from_column_builder.h" +#include "table.h" +#include "column.h" +#include "index.h" +#include "util.h" +#include "serializing_util.h" + +namespace sqlite_orm { + + namespace internal { + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_preparable_v = false; + + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_preparable_v().prepare(std::declval()))>> = true; + + /** + * Storage class itself. Create an instanse to use it as an interfacto to sqlite db by calling `make_storage` + * function. + */ + template + struct storage_t : storage_base { + using self = storage_t; + using db_objects_type = db_objects_tuple; + + /** + * @param filename database filename. + * @param dbObjects db_objects_tuple + */ + storage_t(std::string filename, db_objects_type dbObjects) : + storage_base{std::move(filename), foreign_keys_count(dbObjects)}, db_objects{std::move(dbObjects)} {} + + private: + db_objects_type db_objects; + + /** + * Obtain a storage_t's const db_objects_tuple. + * + * @note Historically, `serializer_context_builder` was declared friend, along with + * a few other library stock objects, in order to limit access to the db_objects_tuple. + * However, one could gain access to a storage_t's db_objects_tuple through + * `serializer_context_builder`, hence leading the whole friend declaration mambo-jumbo + * ad absurdum. + * Providing a free function is way better and cleaner. + * + * Hence, friend was replaced by `obtain_db_objects()` and `pick_const_impl()`. + */ + friend const db_objects_type& obtain_db_objects(const self& storage) noexcept { + return storage.db_objects; + } + + template + void create_table(sqlite3* db, const std::string& tableName, const Table& table) { + using table_type = std::decay_t; + using context_t = serializer_context; + + std::stringstream ss; + context_t context{this->db_objects}; + ss << "CREATE TABLE " << streaming_identifier(tableName) << " ( " + << streaming_expressions_tuple(table.elements, context) << ")"; + if(table_type::is_without_rowid_v) { + ss << " WITHOUT ROWID"; + } + ss.flush(); + perform_void_exec(db, ss.str()); + } + + /** + * Copies sourceTableName to another table with name: destinationTableName + * Performs INSERT INTO %destinationTableName% () SELECT %table.column_names% FROM %sourceTableName% + */ + template + void copy_table(sqlite3* db, + const std::string& sourceTableName, + const std::string& destinationTableName, + const Table& table, + const std::vector& columnsToIgnore) const; + +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + void drop_column(sqlite3* db, const std::string& tableName, const std::string& columnName) { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " DROP COLUMN " + << streaming_identifier(columnName) << std::flush; + perform_void_exec(db, ss.str()); + } +#endif + + template + void drop_create_with_loss(sqlite3* db, const Table& table) { + // eliminated all transaction handling + this->drop_table_internal(db, table.name); + this->create_table(db, table.name, table); + } + + template + void backup_table(sqlite3* db, const Table& table, 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.. + auto backupTableName = table.name + "_backup"; + if(this->table_exists(db, backupTableName)) { + int suffix = 1; + do { + std::stringstream ss; + ss << suffix << std::flush; + auto anotherBackupTableName = backupTableName + ss.str(); + if(!this->table_exists(db, anotherBackupTableName)) { + backupTableName = std::move(anotherBackupTableName); + break; + } + ++suffix; + } while(true); + } + this->create_table(db, backupTableName, table); + + this->copy_table(db, table.name, backupTableName, table, columnsToIgnore); + + this->drop_table_internal(db, table.name); + + this->rename_table(db, backupTableName, table.name); + } + + template + void assert_mapped_type() const { + using mapped_types_tuple = std::tuple; + static_assert(mpl::invoke_t, mapped_types_tuple>::value, + "type is not mapped to a storage"); + } + + template, + std::enable_if_t = true> + void assert_insertable_type() const {} + + template, + std::enable_if_t = true> + void assert_insertable_type() const { + using elements_type = elements_type_t; + using pkcol_index_sequence = col_index_sequence_with; + static_assert( + count_filtered_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_filtered_tuple::template fn, + pkcol_index_sequence>::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."); + } + + template + auto& get_table() const { + return pick_table(this->db_objects); + } + + template + auto& get_table() { + return pick_table(this->db_objects); + } + + public: + template + view_t iterate(Args&&... args) { + this->assert_mapped_type(); + + auto con = this->get_connection(); + return {*this, std::move(con), std::forward(args)...}; + } + + /** + * Delete from routine. + * O is an object's type. Must be specified explicitly. + * @param args optional conditions: `where`, `join` etc + * @example: storage.remove_all(); - DELETE FROM users + * @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) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::remove_all(std::forward(args)...)); + this->execute(statement); + } + + /** + * Delete routine. + * O is an object's type. Must be specified explicitly. + * @param ids ids of object to be removed. + */ + template + void remove(Ids... ids) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::remove(std::forward(ids)...)); + this->execute(statement); + } + + /** + * Update routine. Sets all non primary key fields where primary key is equal. + * O is an object type. May be not specified explicitly cause it can be deduced by + * compiler from first parameter. + * @param o object to be updated. + */ + template + void update(const O& o) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::update(std::ref(o))); + this->execute(statement); + } + + template + void update_all(S set, Wargs... wh) { + static_assert(internal::is_set::value, + "first argument in update_all can be either set or dynamic_set"); + auto statement = this->prepare(sqlite_orm::update_all(std::move(set), std::forward(wh)...)); + this->execute(statement); + } + + protected: + template + std::string group_concat_internal(F O::*m, std::unique_ptr y, Args&&... args) { + this->assert_mapped_type(); + std::vector rows; + if(y) { + rows = this->select(sqlite_orm::group_concat(m, std::move(*y)), std::forward(args)...); + } else { + rows = this->select(sqlite_orm::group_concat(m), std::forward(args)...); + } + if(!rows.empty()) { + return std::move(rows.front()); + } else { + return {}; + } + } + + public: + /** + * SELECT * routine. + * O is an object type to be extracted. Must be specified explicitly. + * @return All objects of type O stored in database at the moment in `std::vector`. + * @note If you need to return the result in a different container type then use a different `get_all` function overload `get_all>` + * @example: storage.get_all() - SELECT * FROM users + * @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) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_all(std::forward(args)...)); + return this->execute(statement); + } + + /** + * SELECT * routine. + * O is an object type to be extracted. Must be specified explicitly. + * R is an explicit return type. This type must have `push_back(O &&)` function. + * @return All objects of type O stored in database at the moment in `R`. + * @example: storage.get_all>(); - SELECT * FROM users + * @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) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_all(std::forward(args)...)); + return this->execute(statement); + } + + /** + * SELECT * routine. + * O is an object type to be extracted. Must be specified explicitly. + * @return All objects of type O as `std::unique_ptr` inside a `std::vector` stored in database at the moment. + * @note If you need to return the result in a different container type then use a different `get_all_pointer` function overload `get_all_pointer>` + * @example: storage.get_all_pointer(); - SELECT * FROM users + * @example: storage.get_all_pointer(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 + */ + template + 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); + } + + /** + * SELECT * routine. + * O is an object type to be extracted. Must be specified explicitly. + * R is a container type. std::vector> is default + * @return All objects of type O as std::unique_ptr stored in database at the moment. + * @example: storage.get_all_pointer>(); - SELECT * FROM users + * @example: storage.get_all_pointer>(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 + */ + template + 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); + } + + /** + * Select * by id routine. + * throws std::system_error{orm_error_code::not_found} if object not found with given + * id. throws std::system_error with orm_error_category in case of db error. O is an object type to be + * extracted. Must be specified explicitly. + * @return Object of type O where id is equal parameter passed or throws + * `std::system_error{orm_error_code::not_found}` if there is no object with such id. + */ + template + O get(Ids... ids) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get(std::forward(ids)...)); + return this->execute(statement); + } + + /** + * The same as `get` function but doesn't throw an exception if noting found but returns std::unique_ptr + * with null value. throws std::system_error in case of db error. + */ + template + std::unique_ptr get_pointer(Ids... ids) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_pointer(std::forward(ids)...)); + return this->execute(statement); + } + + /** + * A previous version of get_pointer() that returns a shared_ptr + * instead of a unique_ptr. New code should prefer get_pointer() + * unless the data needs to be shared. + * + * @note + * Most scenarios don't need shared ownership of data, so we should prefer + * unique_ptr when possible. It's more efficient, doesn't require atomic + * ops for a reference count (which can cause major slowdowns on + * weakly-ordered platforms like ARM), and can be easily promoted to a + * shared_ptr, exactly like we're doing here. + * (Conversely, you _can't_ go from shared back to unique.) + */ + template + std::shared_ptr get_no_throw(Ids... ids) { + return std::shared_ptr(this->get_pointer(std::forward(ids)...)); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * The same as `get` function but doesn't throw an exception if noting found but + * returns an empty std::optional. throws std::system_error in case of db error. + */ + template + std::optional get_optional(Ids... ids) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_optional(std::forward(ids)...)); + return this->execute(statement); + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + /** + * SELECT COUNT(*) https://www.sqlite.org/lang_aggfunc.html#count + * @return Number of O object in table. + */ + template> + int count(Args&&... args) { + this->assert_mapped_type(); + auto rows = this->select(sqlite_orm::count(), std::forward(args)...); + if(!rows.empty()) { + return rows.front(); + } else { + return 0; + } + } + + /** + * 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) { + this->assert_mapped_type(); + auto rows = this->select(sqlite_orm::count(m), std::forward(args)...); + if(!rows.empty()) { + return rows.front(); + } else { + return 0; + } + } + + /** + * 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 database. + */ + template + 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()) { + return rows.front(); + } else { + return 0; + } + } + + template + std::string group_concat(F O::*m) { + return this->group_concat_internal(m, {}); + } + + /** + * GROUP_CONCAT(X) query. https://www.sqlite.org/lang_aggfunc.html#groupconcat + * @param m is a class member pointer (the same you passed into make_column). + * @return group_concat query result. + */ + template, + std::enable_if_t::value >= 1, bool> = true> + std::string group_concat(F O::*m, Args&&... args) { + return this->group_concat_internal(m, {}, std::forward(args)...); + } + + /** + * GROUP_CONCAT(X, Y) query. https://www.sqlite.org/lang_aggfunc.html#groupconcat + * @param m is a class member pointer (the same you passed into make_column). + * @return group_concat query result. + */ + template + std::string group_concat(F O::*m, std::string y, Args&&... args) { + return this->group_concat_internal(m, + std::make_unique(std::move(y)), + std::forward(args)...); + } + + template + std::string group_concat(F O::*m, const char* y, Args&&... args) { + std::unique_ptr str; + if(y) { + str = std::make_unique(y); + } else { + str = std::make_unique(); + } + return this->group_concat_internal(m, std::move(str), std::forward(args)...); + } + + /** + * MAX(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return std::unique_ptr with max value or null if sqlite engine returned null. + */ + template> + 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()) { + return std::move(rows.front()); + } else { + return {}; + } + } + + /** + * MIN(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return std::unique_ptr with min value or null if sqlite engine returned null. + */ + template> + 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()) { + return std::move(rows.front()); + } else { + return {}; + } + } + + /** + * SUM(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return std::unique_ptr with sum value or null if sqlite engine returned null. + */ + template> + 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)...); + if(!rows.empty()) { + if(rows.front()) { + return std::make_unique(std::move(*rows.front())); + } else { + return {}; + } + } else { + return {}; + } + } + + /** + * TOTAL(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return total value (the same as SUM but not nullable. More details here + * https://www.sqlite.org/lang_aggfunc.html) + */ + template + 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()) { + return std::move(rows.front()); + } else { + return {}; + } + } + + /** + * Select a single column into std::vector or multiple columns into std::vector>. + * For a single column use `auto rows = storage.select(&User::id, where(...)); + * For multicolumns use `auto rows = storage.select(columns(&User::id, &User::name), where(...)); + */ + template> + std::vector select(T m, Args... args) { + static_assert(!is_compound_operator_v || sizeof...(Args) == 0, + "Cannot use args with a compound operator"); + auto statement = this->prepare(sqlite_orm::select(std::move(m), std::forward(args)...)); + return this->execute(statement); + } + + template = true> + std::string dump(const T& preparedStatement, bool parametrized = true) const { + return this->dump(preparedStatement.expression, parametrized); + } + + template, + std::enable_if_t && !is_mapped_v, bool> = true> + std::string dump(E&& expression, bool parametrized = false) const { + static_assert(is_preparable_v, "Expression must be a high-level statement"); + + decltype(auto) e2 = static_if>( + [](auto expression) -> auto{ + expression.highest_level = true; + return expression; + }, + [](const auto& expression) -> decltype(auto) { + return (expression); + })(std::forward(expression)); + using context_t = serializer_context; + context_t context{this->db_objects}; + context.replace_bindable_with_question = parametrized; + // just like prepare_impl() + context.skip_table_name = false; + return serialize(e2, context); + } + + /** + * Returns a string representation of object of a class mapped to the storage. + * Type of string has json-like style. + */ + template = true> + std::string dump(const O& object) const { + auto& table = this->get_table(); + std::stringstream ss; + ss << "{ "; + table.for_each_column([&ss, &object, first = true](auto& column) mutable { + using column_type = std::decay_t; + using field_type = typename column_type::field_type; + constexpr std::array sep = {", ", ""}; + + ss << sep[std::exchange(first, false)] << column.name << " : '" + << field_printer{}(polyfill::invoke(column.member_pointer, object)) << "'"; + }); + ss << " }"; + return ss.str(); + } + + /** + * This is REPLACE (INSERT OR REPLACE) function. + * Also if you need to insert value with knows id you should + * also you this function instead of insert cause inserts ignores + * id and creates own one. + */ + template + void replace(const O& o) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::replace(std::ref(o))); + this->execute(statement); + } + + template + void replace_range(It from, It to, Projection project = {}) { + using O = std::decay_t(), *std::declval()))>; + this->assert_mapped_type(); + if(from == to) { + return; + } + + auto statement = + this->prepare(sqlite_orm::replace_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); + } + + template + void replace_range(It from, It to, Projection project = {}) { + this->assert_mapped_type(); + if(from == to) { + return; + } + + auto statement = + this->prepare(sqlite_orm::replace_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); + } + + template + int insert(const O& o, columns_t cols) { + static_assert(cols.count > 0, "Use insert or replace with 1 argument instead"); + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::insert(std::ref(o), std::move(cols))); + return int(this->execute(statement)); + } + + /** + * Insert routine. Inserts object with all non primary key fields in passed object. Id of passed + * object doesn't matter. + * @return id of just created object. + */ + template + int insert(const O& o) { + this->assert_mapped_type(); + this->assert_insertable_type(); + 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, Projection project = {}) { + using O = std::decay_t(), *std::declval()))>; + this->assert_mapped_type(); + this->assert_insertable_type(); + if(from == to) { + return; + } + auto statement = + this->prepare(sqlite_orm::insert_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); + } + + template + void insert_range(It from, It to, Projection project = {}) { + this->assert_mapped_type(); + this->assert_insertable_type(); + if(from == to) { + return; + } + auto statement = + this->prepare(sqlite_orm::insert_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); + } + + /** + * Change table name inside storage's schema info. This function does not + * affect database + */ + template + void rename_table(std::string name) { + this->assert_mapped_type(); + auto& table = this->get_table(); + table.name = std::move(name); + } + + using storage_base::rename_table; + + /** + * Get table's name stored in storage's schema info. This function does not call + * any SQLite queries + */ + template + const std::string& tablename() const { + this->assert_mapped_type(); + auto& table = this->get_table(); + return table.name; + } + + template + [[deprecated("Use the more accurately named function `find_column_name()`")]] const std::string* + column_name(F O::*memberPointer) const { + return internal::find_column_name(this->db_objects, memberPointer); + } + + template + const std::string* find_column_name(F O::*memberPointer) const { + return internal::find_column_name(this->db_objects, memberPointer); + } + + protected: + template + sync_schema_result schema_status(const index_t&, sqlite3*, bool, bool*) { + return sync_schema_result::already_in_sync; + } + + template + sync_schema_result schema_status(const table_t& table, + sqlite3* db, + bool preserve, + bool* attempt_to_preserve) { + if(attempt_to_preserve) { + *attempt_to_preserve = true; + } + + auto dbTableInfo = this->pragma.table_xinfo(table.name); + auto res = sync_schema_result::already_in_sync; + + // first let's see if table with such name exists.. + auto gottaCreateTable = !this->table_exists(db, table.name); + if(!gottaCreateTable) { + + // get table info provided in `make_table` call.. + auto storageTableInfo = table.get_table_info(); + + // this vector will contain pointers to columns that gotta be added.. + std::vector columnsToAdd; + + if(calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { + gottaCreateTable = true; + } + + if(!gottaCreateTable) { // if all storage columns are equal to actual db columns but there are + // excess columns at the db.. + if(!dbTableInfo.empty()) { + // extra table columns than storage columns + if(!preserve) { +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + res = sync_schema_result::old_columns_removed; +#else + gottaCreateTable = true; +#endif + } else { + res = sync_schema_result::old_columns_removed; + } + } + } + if(gottaCreateTable) { + res = sync_schema_result::dropped_and_recreated; + } else { + if(!columnsToAdd.empty()) { + // extra storage columns than table columns + for(const table_xinfo* colInfo: columnsToAdd) { + const basic_generated_always::storage_type* generatedStorageType = + table.find_column_generated_storage_type(colInfo->name); + if(generatedStorageType) { + if(*generatedStorageType == basic_generated_always::storage_type::stored) { + gottaCreateTable = true; + break; + } + // fallback cause VIRTUAL can be added + } else { + if(colInfo->notnull && colInfo->dflt_value.empty()) { + gottaCreateTable = true; + // no matter if preserve is true or false, there is no way to preserve data, so we wont try! + if(attempt_to_preserve) { + *attempt_to_preserve = false; + }; + break; + } + } + } + if(!gottaCreateTable) { + if(res == sync_schema_result::old_columns_removed) { + res = sync_schema_result::new_columns_added_and_old_columns_removed; + } else { + res = sync_schema_result::new_columns_added; + } + } else { + res = sync_schema_result::dropped_and_recreated; + } + } else { + if(res != sync_schema_result::old_columns_removed) { + res = sync_schema_result::already_in_sync; + } + } + } + } else { + res = sync_schema_result::new_table_created; + } + return res; + } + + template + sync_schema_result sync_table(const index_t& index, sqlite3* db, bool) { + auto res = sync_schema_result::already_in_sync; + using context_t = serializer_context; + context_t context{this->db_objects}; + auto query = serialize(index, context); + perform_void_exec(db, query); + return res; + } + + template + sync_schema_result sync_table(const trigger_t& trigger, sqlite3* db, bool) { + auto res = sync_schema_result::already_in_sync; // TODO Change accordingly + using context_t = serializer_context; + context_t context{this->db_objects}; + perform_void_exec(db, serialize(trigger, context)); + return res; + } + + template = true> + sync_schema_result sync_table(const Table& table, sqlite3* db, bool preserve); + + template + void add_column(sqlite3* db, const std::string& tableName, const C& column) const { + using context_t = serializer_context; + + context_t context{this->db_objects}; + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " ADD COLUMN " << serialize(column, context) + << std::flush; + perform_void_exec(db, ss.str()); + } + + template + prepared_statement_t prepare_impl(S statement) { + using context_t = serializer_context; + context_t context{this->db_objects}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + + auto con = this->get_connection(); + sqlite3_stmt* stmt = prepare_stmt(con.get(), serialize(statement, context)); + return prepared_statement_t{std::forward(statement), stmt, con}; + } + + public: + /** + * This is a cute function used to replace migration up/down functionality. + * It performs check storage schema with actual db schema and: + * * if there are excess tables exist in db they are ignored (not dropped) + * * every table from storage is compared with it's db analog and + * * if table doesn't exist it is being created + * * if table exists its colums are being compared with table_info from db and + * * if there are columns in db that do not exist in storage (excess) table will be dropped and + * recreated + * * if there are columns in storage that do not exist in db they will be added using `ALTER TABLE + * ... ADD COLUMN ...' command + * * if there is any column existing in both db and storage but differs by any of + * properties/constraints (pk, notnull, dflt_value) table will be dropped and recreated. Be aware that + * `sync_schema` doesn't guarantee that data will not be dropped. It guarantees only that it will make db + * schema the same as you specified in `make_storage` function call. A good point is that if you have no db + * file at all it will be created and all tables also will be created with exact tables and columns you + * specified in `make_storage`, `make_table` and `make_column` calls. The best practice is to call this + * function right after storage creation. + * @param preserve affects function's behaviour in case it is needed to remove a column. If it is `false` + * so table will be dropped if there is column to remove if SQLite version is < 3.35.0 and remove column if SQLite version >= 3.35.0, + * if `true` - table is being copied into another table, dropped and copied table is renamed with source table name. + * Warning: sync_schema doesn't check foreign keys cause it is unable to do so in sqlite3. If you know how to get foreign key info please + * submit an issue https://github.com/fnc12/sqlite_orm/issues + * @return std::map with std::string key equal table name and `sync_schema_result` as value. + * `sync_schema_result` is a enum value that stores table state after syncing a schema. `sync_schema_result` + * can be printed out on std::ostream with `operator<<`. + */ + std::map sync_schema(bool preserve = false) { + auto con = this->get_connection(); + std::map result; + iterate_tuple(this->db_objects, [this, db = con.get(), preserve, &result](auto& schemaObject) { + sync_schema_result status = this->sync_table(schemaObject, db, preserve); + result.emplace(schemaObject.name, status); + }); + return result; + } + + /** + * This function returns the same map that `sync_schema` returns but it + * doesn't perform `sync_schema` actually - just simulates it in case you want to know + * what will happen if you sync your schema. + */ + std::map sync_schema_simulate(bool preserve = false) { + auto con = this->get_connection(); + std::map result; + iterate_tuple(this->db_objects, [this, db = con.get(), preserve, &result](auto& schemaObject) { + sync_schema_result status = this->schema_status(schemaObject, db, preserve, nullptr); + result.emplace(schemaObject.name, status); + }); + return result; + } + + using storage_base::table_exists; // now that it is in storage_base make it into overload set + + template + prepared_statement_t> prepare(select_t sel) { + sel.highest_level = true; + return prepare_impl>(std::move(sel)); + } + + template + prepared_statement_t> prepare(get_all_t get_) { + return prepare_impl>(std::move(get_)); + } + + template + prepared_statement_t> prepare(get_all_pointer_t get_) { + 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_) { + return prepare_impl>(std::move(get_)); + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + prepared_statement_t> prepare(update_all_t upd) { + return prepare_impl>(std::move(upd)); + } + + template + prepared_statement_t> prepare(remove_all_t rem) { + return prepare_impl>(std::move(rem)); + } + + template + prepared_statement_t> prepare(get_t get_) { + return prepare_impl>(std::move(get_)); + } + + template + prepared_statement_t> prepare(get_pointer_t get_) { + return prepare_impl>(std::move(get_)); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + prepared_statement_t> prepare(get_optional_t get_) { + return prepare_impl>(std::move(get_)); + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + prepared_statement_t> prepare(update_t upd) { + return prepare_impl>(std::move(upd)); + } + + template + prepared_statement_t> prepare(remove_t statement) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + return this->prepare_impl>(std::move(statement)); + } + + template + prepared_statement_t> prepare(insert_t statement) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + this->assert_insertable_type(); + return this->prepare_impl>(std::move(statement)); + } + + template + prepared_statement_t> prepare(replace_t rep) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + return this->prepare_impl>(std::move(rep)); + } + + 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 this->prepare_impl>(std::move(statement)); + } + + template + prepared_statement_t> prepare(replace_range_t statement) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + return this->prepare_impl>(std::move(statement)); + } + + template + prepared_statement_t> prepare(insert_explicit ins) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + return this->prepare_impl>(std::move(ins)); + } + + template + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.args, conditional_binder{statement.stmt}); + perform_step(stmt); + } + + template + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.args, conditional_binder{stmt}); + perform_step(stmt); + } + + template + int64 execute(const prepared_statement_t>& statement) { + using statement_type = std::decay_t; + using expression_type = typename statement_type::expression_type; + using object_type = typename expression_object_type::type; + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + tuple_value_binder{stmt}( + statement.expression.columns.columns, + [&table = this->get_table(), &object = statement.expression.obj](auto& memberPointer) { + return table.object_field_value(object, memberPointer); + }); + perform_step(stmt); + return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); + } + + template, is_replace_range>, bool> = true> + void execute(const prepared_statement_t& statement) { + using statement_type = std::decay_t; + using expression_type = typename statement_type::expression_type; + using object_type = typename expression_object_type::type; + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + auto processObject = [&table = this->get_table(), + bindValue = field_value_binder{stmt}](auto& object) mutable { + table.template for_each_column_excluding( + call_as_template_base([&bindValue, &object](auto& column) { + bindValue(polyfill::invoke(column.member_pointer, object)); + })); + }; + + static_if>( + [&processObject](auto& expression) { +#if __cpp_lib_ranges >= 201911L + std::ranges::for_each(expression.range.first, + expression.range.second, + std::ref(processObject), + std::ref(expression.transformer)); +#else + auto& transformer = expression.transformer; + std::for_each(expression.range.first, + expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); +#endif + }, + [&processObject](auto& expression) { + const object_type& o = get_object(expression); + processObject(o); + })(statement.expression); + + perform_step(stmt); + } + + template, is_insert_range>, bool> = true> + int64 execute(const prepared_statement_t& statement) { + using statement_type = std::decay_t; + using expression_type = typename statement_type::expression_type; + using object_type = typename expression_object_type::type; + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + auto processObject = [&table = this->get_table(), + bindValue = field_value_binder{stmt}](auto& object) mutable { + using is_without_rowid = typename std::decay_t::is_without_rowid; + table.template for_each_column_excluding< + mpl::conjunction>, + mpl::disjunction_fn>>( + call_as_template_base([&table, &bindValue, &object](auto& column) { + if(!table.exists_in_composite_primary_key(column)) { + bindValue(polyfill::invoke(column.member_pointer, object)); + } + })); + }; + + static_if>( + [&processObject](auto& expression) { +#if __cpp_lib_ranges >= 201911L + std::ranges::for_each(expression.range.first, + expression.range.second, + std::ref(processObject), + std::ref(expression.transformer)); +#else + auto& transformer = expression.transformer; + std::for_each(expression.range.first, + expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); +#endif + }, + [&processObject](auto& expression) { + const object_type& o = get_object(expression); + processObject(o); + })(statement.expression); + + perform_step(stmt); + return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); + } + + template + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + perform_step(stmt); + } + + template + void execute(const prepared_statement_t>& statement) { + using statement_type = std::decay_t; + using expression_type = typename statement_type::expression_type; + using object_type = typename expression_object_type::type; + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + auto& table = this->get_table(); + + field_value_binder bindValue{stmt}; + auto& object = get_object(statement.expression); + table.template for_each_column_excluding>( + call_as_template_base([&table, &bindValue, &object](auto& column) { + if(!table.exists_in_composite_primary_key(column)) { + bindValue(polyfill::invoke(column.member_pointer, object)); + } + })); + table.for_each_column([&table, &bindValue, &object](auto& column) { + if(column.template is() || table.exists_in_composite_primary_key(column)) { + bindValue(polyfill::invoke(column.member_pointer, object)); + } + }); + perform_step(stmt); + } + + template + std::unique_ptr execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + + std::unique_ptr res; + perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + res = std::make_unique(); + object_from_column_builder builder{*res, stmt}; + table.for_each_column(builder); + }); + return res; + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + std::optional execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + + std::optional res; + perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + object_from_column_builder builder{res.emplace(), stmt}; + table.for_each_column(builder); + }); + return res; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + T execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + std::optional res; + perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + object_from_column_builder builder{res.emplace(), stmt}; + table.for_each_column(builder); + }); + if(!res.has_value()) { + throw std::system_error{orm_error_code::not_found}; + } + return std::move(res).value(); +#else + auto& table = this->get_table(); + auto stepRes = sqlite3_step(stmt); + switch(stepRes) { + case SQLITE_ROW: { + T res; + object_from_column_builder builder{res, stmt}; + table.for_each_column(builder); + return res; + } break; + case SQLITE_DONE: { + throw std::system_error{orm_error_code::not_found}; + } break; + default: { + throw_translated_sqlite_error(stmt); + } + } +#endif + } + + template + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.conditions, conditional_binder{stmt}); + perform_step(stmt); + } + + template + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + conditional_binder bindNode{stmt}; + iterate_ast(statement.expression.set, bindNode); + iterate_ast(statement.expression.conditions, bindNode); + perform_step(stmt); + } + + template> + std::vector execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + + std::vector res; + perform_steps(stmt, + [rowExtractor = make_row_extractor(lookup_table(this->db_objects)), + &res](sqlite3_stmt* stmt) { + res.push_back(rowExtractor.extract(stmt, 0)); + }); + return res; + } + + template + R execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + + R res; + perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + T obj; + object_from_column_builder builder{obj, stmt}; + table.for_each_column(builder); + res.push_back(std::move(obj)); + }); + return res; + } + + template + R execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + + R res; + perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + auto obj = std::make_unique(); + object_from_column_builder builder{*obj, stmt}; + table.for_each_column(builder); + res.push_back(std::move(obj)); + }); + return res; + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + R execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + + R res; + perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + auto obj = std::make_optional(); + object_from_column_builder builder{*obj, stmt}; + table.for_each_column(builder); + res.push_back(std::move(obj)); + }); + return res; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + }; // struct storage_t + } + + /* + * Factory function for a storage, from a database file and a bunch of database object definitions. + */ + template + internal::storage_t make_storage(std::string filename, DBO... dbObjects) { + return {std::move(filename), internal::db_objects_tuple{std::forward(dbObjects)...}}; + } + + /** + * sqlite3_threadsafe() interface. + */ + inline int threadsafe() { + return sqlite3_threadsafe(); + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/storage_base.h b/libs/sqlite_orm-1.8.2/dev/storage_base.h new file mode 100644 index 0000000..6d43e10 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/storage_base.h @@ -0,0 +1,790 @@ +#pragma once + +#include +#include // std::function, std::bind +#include // std::string +#include // std::stringstream +#include // std::move +#include // std::system_error +#include // std::vector +#include // std::make_unique, std::unique_ptr +#include // std::map +#include // std::decay, std::is_same +#include // std::find_if + +#include "functional/cxx_universal.h" +#include "functional/static_magic.h" +#include "tuple_helper/tuple_iteration.h" +#include "pragma.h" +#include "limit_accessor.h" +#include "transaction_guard.h" +#include "row_extractor.h" +#include "connection_holder.h" +#include "backup.h" +#include "function.h" +#include "values_to_tuple.h" +#include "arg_values.h" +#include "util.h" +#include "serializing_util.h" + +namespace sqlite_orm { + + namespace internal { + + struct storage_base { + using collating_function = std::function; + + std::function on_open; + pragma_t pragma; + limit_accessor limit; + + transaction_guard_t transaction_guard() { + this->begin_transaction(); + return {this->get_connection(), + std::bind(&storage_base::commit, this), + std::bind(&storage_base::rollback, this)}; + } + + void drop_index(const std::string& indexName) { + std::stringstream ss; + ss << "DROP INDEX " << quote_identifier(indexName) << std::flush; + perform_void_exec(this->get_connection().get(), ss.str()); + } + + void drop_trigger(const std::string& triggerName) { + std::stringstream ss; + ss << "DROP TRIGGER " << quote_identifier(triggerName) << std::flush; + perform_void_exec(this->get_connection().get(), ss.str()); + } + + void vacuum() { + perform_void_exec(this->get_connection().get(), "VACUUM"); + } + + /** + * Drops table with given name. + */ + void drop_table(const std::string& tableName) { + this->drop_table_internal(this->get_connection().get(), tableName); + } + + /** + * Rename table named `from` to `to`. + */ + void rename_table(const std::string& from, const std::string& to) { + this->rename_table(this->get_connection().get(), from, to); + } + + protected: + void rename_table(sqlite3* db, const std::string& oldName, const std::string& newName) const { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(oldName) << " RENAME TO " << streaming_identifier(newName) + << std::flush; + perform_void_exec(db, ss.str()); + } + + /** + * Checks whether table exists in db. Doesn't check storage itself - works only with actual database. + * 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) { + auto con = this->get_connection(); + return this->table_exists(con.get(), tableName); + } + + bool table_exists(sqlite3* db, const std::string& tableName) const { + bool result = false; + std::stringstream ss; + ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = " << streaming_identifier("table") + << " AND name = " << quote_string_literal(tableName) << std::flush; + perform_exec( + db, + ss.str(), + [](void* data, int argc, char** argv, char** /*azColName*/) -> int { + auto& res = *(bool*)data; + if(argc) { + res = !!std::atoi(argv[0]); + } + return 0; + }, + &result); + return result; + } + + void add_generated_cols(std::vector& columnsToAdd, + const std::vector& storageTableInfo) { + // iterate through storage columns + for(const table_xinfo& storageColumnInfo: storageTableInfo) { + if(storageColumnInfo.hidden) { + columnsToAdd.push_back(&storageColumnInfo); + } + } + } + + public: + /** + * sqlite3_changes function. + */ + int changes() { + auto con = this->get_connection(); + return sqlite3_changes(con.get()); + } + + /** + * sqlite3_total_changes function. + */ + int total_changes() { + auto con = this->get_connection(); + return sqlite3_total_changes(con.get()); + } + + int64 last_insert_rowid() { + auto con = this->get_connection(); + return sqlite3_last_insert_rowid(con.get()); + } + + int busy_timeout(int ms) { + auto con = this->get_connection(); + return sqlite3_busy_timeout(con.get(), ms); + } + + /** + * Returns libsqlite3 version, not sqlite_orm + */ + std::string libversion() { + return sqlite3_libversion(); + } + + bool transaction(const std::function& f) { + auto guard = this->transaction_guard(); + return guard.commit_on_destroy = f(); + } + + std::string current_timestamp() { + auto con = this->get_connection(); + return this->current_timestamp(con.get()); + } + +#if SQLITE_VERSION_NUMBER >= 3007010 + /** + * \fn db_release_memory + * \brief Releases freeable memory of database. It is function can/should be called periodically by + * application, if application has less memory usage constraint. \note sqlite3_db_release_memory added + * in 3.7.10 https://sqlite.org/changes.html + */ + int db_release_memory() { + auto con = this->get_connection(); + return sqlite3_db_release_memory(con.get()); + } +#endif + + /** + * Returns existing permanent table names in database. Doesn't check storage itself - works only with + * actual database. + * @return Returns list of tables in database. + */ + std::vector table_names() { + auto con = this->get_connection(); + std::vector tableNames; + using data_t = std::vector; + perform_exec( + con.get(), + "SELECT name FROM sqlite_master WHERE type='table'", + [](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_.emplace_back(argv[i]); + } + } + return 0; + }, + &tableNames); + return tableNames; + } + + void open_forever() { + this->isOpenedForever = true; + this->connection->retain(); + if(1 == this->connection->retain_count()) { + this->on_open_internal(this->connection->get()); + } + } + + /** + * 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"; + * } + * }; + * ``` + * + * Note: Currently, a function's name must not contain white-space characters, because it doesn't get quoted. + */ + template + void create_scalar_function() { + static_assert(is_scalar_function_v, "F can't be an aggregate function"); + + std::stringstream ss; + ss << F::name() << std::flush; + auto name = ss.str(); + using args_tuple = typename callable_arguments::args_tuple; + using return_type = typename callable_arguments::return_type; + constexpr auto argsCount = std::is_same>::value + ? -1 + : int(std::tuple_size::value); + this->scalarFunctions.emplace_back(new user_defined_scalar_function_t{ + std::move(name), + argsCount, + []() -> int* { + return (int*)(new F()); + }, + /* call = */ + [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { + auto& function = *static_cast(functionVoidPointer); + args_tuple argsTuple; + values_to_tuple{}(values, argsTuple, argsCount); + auto result = call(function, std::move(argsTuple)); + statement_binder().result(context, result); + }, + delete_function_callback, + }); + + if(this->connection->retain_count() > 0) { + sqlite3* 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"; + * } + * }; + * ``` + * + * Note: Currently, a function's name must not contain white-space characters, because it doesn't get quoted. + */ + template + void create_aggregate_function() { + static_assert(is_aggregate_function_v, "F can't be a scalar function"); + + std::stringstream ss; + ss << F::name() << std::flush; + auto name = ss.str(); + using args_tuple = typename callable_arguments::args_tuple; + using return_type = typename callable_arguments::return_type; + constexpr auto argsCount = std::is_same>::value + ? -1 + : int(std::tuple_size::value); + this->aggregateFunctions.emplace_back(new user_defined_aggregate_function_t{ + std::move(name), + argsCount, + /* create = */ + []() -> int* { + return (int*)(new F()); + }, + /* step = */ + [](sqlite3_context*, void* functionVoidPointer, int argsCount, sqlite3_value** values) { + auto& function = *static_cast(functionVoidPointer); + args_tuple argsTuple; + values_to_tuple{}(values, argsTuple, argsCount); + call(function, &F::step, std::move(argsTuple)); + }, + /* finalCall = */ + [](sqlite3_context* context, void* functionVoidPointer) { + auto& function = *static_cast(functionVoidPointer); + auto result = function.fin(); + statement_binder().result(context, result); + }, + delete_function_callback, + }); + + if(this->connection->retain_count() > 0) { + sqlite3* 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_v, "F cannot be an aggregate function"); + std::stringstream ss; + ss << F::name() << std::flush; + this->delete_function_impl(ss.str(), 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_v, "F cannot be a scalar function"); + std::stringstream ss; + ss << F::name() << std::flush; + this->delete_function_impl(ss.str(), 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() << std::flush; + this->create_collation(ss.str(), std::move(func)); + } + + void create_collation(const std::string& name, collating_function f) { + collating_function* function = nullptr; + const auto functionExists = bool(f); + if(functionExists) { + function = &(collatingFunctions[name] = std::move(f)); + } else { + collatingFunctions.erase(name); + } + + // create collations if db is open + if(this->connection->retain_count() > 0) { + sqlite3* db = this->connection->get(); + auto resultCode = sqlite3_create_collation(db, + name.c_str(), + SQLITE_UTF8, + function, + functionExists ? collate_callback : nullptr); + if(resultCode != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + } + + template + void delete_collation() { + std::stringstream ss; + ss << C::name() << std::flush; + this->create_collation(ss.str(), {}); + } + + void begin_transaction() { + this->begin_transaction_internal("BEGIN TRANSACTION"); + } + + void begin_deferred_transaction() { + this->begin_transaction_internal("BEGIN DEFERRED TRANSACTION"); + } + + void begin_immediate_transaction() { + this->begin_transaction_internal("BEGIN IMMEDIATE TRANSACTION"); + } + + void begin_exclusive_transaction() { + this->begin_transaction_internal("BEGIN EXCLUSIVE TRANSACTION"); + } + + void commit() { + sqlite3* db = this->connection->get(); + perform_void_exec(db, "COMMIT"); + this->connection->release(); + if(this->connection->retain_count() < 0) { + throw std::system_error{orm_error_code::no_active_transaction}; + } + } + + void rollback() { + sqlite3* db = this->connection->get(); + perform_void_exec(db, "ROLLBACK"); + this->connection->release(); + if(this->connection->retain_count() < 0) { + throw std::system_error{orm_error_code::no_active_transaction}; + } + } + + void backup_to(const std::string& filename) { + auto backup = this->make_backup_to(filename); + backup.step(-1); + } + + void backup_to(storage_base& other) { + auto backup = this->make_backup_to(other); + backup.step(-1); + } + + void backup_from(const std::string& filename) { + auto backup = this->make_backup_from(filename); + backup.step(-1); + } + + 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) { + auto holder = std::make_unique(filename); + connection_ref conRef{*holder}; + return {conRef, "main", this->get_connection(), "main", std::move(holder)}; + } + + 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) { + auto holder = std::make_unique(filename); + connection_ref conRef{*holder}; + return {this->get_connection(), "main", conRef, "main", std::move(holder)}; + } + + backup_t make_backup_from(storage_base& other) { + return {this->get_connection(), "main", other.get_connection(), "main", {}}; + } + + const std::string& filename() const { + return this->connection->filename; + } + + /** + * Checks whether connection to database is opened right now. + * Returns always `true` for in memory databases. + */ + bool is_opened() const { + return this->connection->retain_count() > 0; + } + + /* + * returning false when there is a transaction in place + * otherwise true; function is not const because it has to call get_connection() + */ + bool get_autocommit() { + auto con = this->get_connection(); + return sqlite3_get_autocommit(con.get()); + } + + int busy_handler(std::function handler) { + _busy_handler = std::move(handler); + if(this->is_opened()) { + if(_busy_handler) { + return sqlite3_busy_handler(this->connection->get(), busy_handler_callback, this); + } else { + return sqlite3_busy_handler(this->connection->get(), nullptr, nullptr); + } + } else { + return SQLITE_OK; + } + } + + protected: + storage_base(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:"), + connection(std::make_unique(std::move(filename))), + cachedForeignKeysCount(foreignKeysCount) { + if(this->inMemory) { + this->connection->retain(); + this->on_open_internal(this->connection->get()); + } + } + + 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)), + cachedForeignKeysCount(other.cachedForeignKeysCount) { + if(this->inMemory) { + this->connection->retain(); + this->on_open_internal(this->connection->get()); + } + } + + ~storage_base() { + if(this->isOpenedForever) { + this->connection->release(); + } + if(this->inMemory) { + this->connection->release(); + } + } + + void begin_transaction_internal(const std::string& query) { + this->connection->retain(); + if(1 == this->connection->retain_count()) { + this->on_open_internal(this->connection->get()); + } + sqlite3* db = this->connection->get(); + perform_void_exec(db, query); + } + + connection_ref get_connection() { + connection_ref res{*this->connection}; + if(1 == this->connection->retain_count()) { + this->on_open_internal(this->connection->get()); + } + return res; + } + +#if SQLITE_VERSION_NUMBER >= 3006019 + + void foreign_keys(sqlite3* db, bool value) { + std::stringstream ss; + ss << "PRAGMA foreign_keys = " << value << std::flush; + perform_void_exec(db, ss.str()); + } + + bool foreign_keys(sqlite3* db) { + bool result = false; + perform_exec(db, "PRAGMA foreign_keys", extract_single_value, &result); + return result; + } + +#endif + void on_open_internal(sqlite3* db) { + +#if SQLITE_VERSION_NUMBER >= 3006019 + if(this->cachedForeignKeysCount) { + this->foreign_keys(db, true); + } +#endif + if(this->pragma._synchronous != -1) { + this->pragma.synchronous(this->pragma._synchronous); + } + + if(this->pragma._journal_mode != -1) { + this->pragma.set_pragma("journal_mode", static_cast(this->pragma._journal_mode), db); + } + + 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_translated_sqlite_error(db); + } + } + + for(auto& p: this->limit.limits) { + sqlite3_limit(db, p.first, p.second); + } + + if(_busy_handler) { + 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 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) { + sqlite3* 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_translated_sqlite_error(db); + } + } + } else { + throw std::system_error{orm_error_code::function_not_found}; + } + } + + void try_to_create_function(sqlite3* db, user_defined_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_translated_sqlite_error(db); + } + } + + void try_to_create_function(sqlite3* db, user_defined_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_translated_sqlite_error(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{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; + perform_exec(db, "SELECT CURRENT_TIMESTAMP", extract_single_value, &result); + return result; + } + + void drop_table_internal(sqlite3* db, const std::string& tableName) { + std::stringstream ss; + ss << "DROP TABLE " << streaming_identifier(tableName) << std::flush; + perform_void_exec(db, ss.str()); + } + + 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); + if(storage._busy_handler) { + return storage._busy_handler(triesCount); + } else { + return 0; + } + } + + bool calculate_remove_add_columns(std::vector& columnsToAdd, + std::vector& storageTableInfo, + std::vector& dbTableInfo) const { + bool notEqual = false; + + // iterate through storage columns + for(size_t storageColumnInfoIndex = 0; storageColumnInfoIndex < storageTableInfo.size(); + ++storageColumnInfoIndex) { + + // get storage's column info + 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) { + return ti.name == columnName; + }); + if(dbColumnInfoIt != dbTableInfo.end()) { + auto& dbColumnInfo = *dbColumnInfoIt; + auto columnsAreEqual = + dbColumnInfo.name == storageColumnInfo.name && + dbColumnInfo.notnull == storageColumnInfo.notnull && + (!dbColumnInfo.dflt_value.empty()) == (!storageColumnInfo.dflt_value.empty()) && + dbColumnInfo.pk == storageColumnInfo.pk && + (dbColumnInfo.hidden == 0) == (storageColumnInfo.hidden == 0); + if(!columnsAreEqual) { + notEqual = true; + break; + } + dbTableInfo.erase(dbColumnInfoIt); + storageTableInfo.erase(storageTableInfo.begin() + + static_cast(storageColumnInfoIndex)); + --storageColumnInfoIndex; + } else { + columnsToAdd.push_back(&storageColumnInfo); + } + } + return notEqual; + } + + 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/libs/sqlite_orm-1.8.2/dev/storage_impl.h b/libs/sqlite_orm-1.8.2/dev/storage_impl.h new file mode 100644 index 0000000..0abbffa --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/storage_impl.h @@ -0,0 +1,74 @@ +#pragma once + +#include // std::string + +#include "functional/cxx_universal.h" // ::nullptr_t +#include "functional/static_magic.h" +#include "tuple_helper/tuple_filter.h" +#include "tuple_helper/tuple_iteration.h" +#include "type_traits.h" +#include "select_constraints.h" +#include "storage_lookup.h" + +// interface functions +namespace sqlite_orm { + namespace internal { + + template + using tables_index_sequence = filter_tuple_sequence_t; + + template = true> + int foreign_keys_count(const DBOs& dbObjects) { + int res = 0; + iterate_tuple(dbObjects, tables_index_sequence{}, [&res](const auto& table) { + res += table.foreign_keys_count(); + }); + return res; + } + + template> + auto lookup_table(const DBOs& dbObjects) { + return static_if>( + [](const auto& dbObjects) { + return &pick_table(dbObjects); + }, + empty_callable())(dbObjects); + } + + template> + decltype(auto) lookup_table_name(const DBOs& dbObjects) { + return static_if>( + [](const auto& dbObjects) -> const std::string& { + return pick_table(dbObjects).name; + }, + empty_callable())(dbObjects); + } + + /** + * Find column name by its type and member pointer. + */ + template = true> + const std::string* find_column_name(const DBOs& dbObjects, F O::*field) { + return pick_table(dbObjects).find_column_name(field); + } + + /** + * Materialize column pointer: + * 1. by explicit object type and member pointer. + */ + template = true> + constexpr decltype(auto) materialize_column_pointer(const DBOs&, const column_pointer& cp) { + return cp.field; + } + + /** + * Find column name by: + * 1. by explicit object type and member pointer. + */ + template = true> + const std::string* find_column_name(const DBOs& dbObjects, const column_pointer& cp) { + auto field = materialize_column_pointer(dbObjects, cp); + return pick_table(dbObjects).find_column_name(field); + } + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/storage_lookup.h b/libs/sqlite_orm-1.8.2/dev/storage_lookup.h new file mode 100644 index 0000000..75daa65 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/storage_lookup.h @@ -0,0 +1,144 @@ +#pragma once + +#include // std::true_type, std::false_type, std::remove_const, std::enable_if, std::is_base_of, std::is_void +#include +#include // std::index_sequence, std::make_index_sequence + +#include "functional/cxx_universal.h" +#include "functional/cxx_type_traits_polyfill.h" +#include "type_traits.h" + +namespace sqlite_orm { + namespace internal { + + template + struct storage_t; + + template + using db_objects_tuple = std::tuple; + + template + struct is_storage : std::false_type {}; + + template + struct is_storage> : std::true_type {}; + template + struct is_storage> : std::true_type {}; + + template + struct is_db_objects : std::false_type {}; + + template + struct is_db_objects> : std::true_type {}; + template + struct is_db_objects> : std::true_type {}; + + /** + * std::true_type if given object is mapped, std::false_type otherwise. + * + * Note: unlike table_t<>, index_t<>::object_type and trigger_t<>::object_type is always void. + */ + template + struct object_type_matches : polyfill::conjunction>>, + std::is_same>> {}; + + /** + * std::true_type if given lookup type (object) is mapped, std::false_type otherwise. + */ + template + struct lookup_type_matches : polyfill::disjunction> {}; + } + + // pick/lookup metafunctions + namespace internal { + + /** + * Indirect enabler for DBO, accepting an index to disambiguate non-unique DBOs + */ + template + struct enable_found_table : std::enable_if::value, DBO> {}; + + /** + * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. + * + * Lookup - mapped data type + * Seq - index sequence matching the number of DBOs + * DBOs - db_objects_tuple type + */ + template + struct storage_pick_table; + + template + struct storage_pick_table, db_objects_tuple> + : enable_found_table... {}; + + /** + * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. + * + * Lookup - 'table' type, mapped data type + * DBOs - db_objects_tuple type, possibly const-qualified + */ + template + using storage_pick_table_t = typename storage_pick_table::value>, + std::remove_const_t>::type; + + /** + * Find a table definition (`table_t`) from a tuple of database objects; + * `std::nonesuch` if not found. + * + * DBOs - db_objects_tuple type + * Lookup - mapped data type + */ + template + struct storage_find_table : polyfill::detected_or {}; + + /** + * Find a table definition (`table_t`) from a tuple of database objects; + * `std::nonesuch` if not found. + * + * DBOs - db_objects_tuple type, possibly const-qualified + * Lookup - mapped data type + */ + template + using storage_find_table_t = typename storage_find_table>::type; + +#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template + struct is_mapped : std::false_type {}; + template + struct is_mapped>> : std::true_type {}; +#else + template> + struct is_mapped : std::true_type {}; + template + struct is_mapped : std::false_type {}; +#endif + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_mapped_v = is_mapped::value; + } +} + +// runtime lookup functions +namespace sqlite_orm { + namespace internal { + /** + * Pick the table definition for the specified lookup type from the given tuple of schema objects. + * + * Note: This function requires Lookup to be mapped, otherwise it is removed from the overload resolution set. + */ + template = true> + auto& pick_table(DBOs& dbObjects) { + using table_type = storage_pick_table_t; + return std::get(dbObjects); + } + + template = true> + auto lookup_table(const DBOs& dbObjects); + + template = true> + decltype(auto) lookup_table_name(const DBOs& dbObjects); + + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/storage_traits.h b/libs/sqlite_orm-1.8.2/dev/storage_traits.h new file mode 100644 index 0000000..ef776c0 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/storage_traits.h @@ -0,0 +1,37 @@ +#pragma once + +#include // std::tuple + +#include "functional/cxx_type_traits_polyfill.h" +#include "tuple_helper/tuple_filter.h" +#include "tuple_helper/tuple_transformer.h" +#include "type_traits.h" +#include "storage_lookup.h" + +namespace sqlite_orm { + + namespace internal { + + namespace storage_traits { + + /** + * DBO - db object (table) + */ + template + struct storage_mapped_columns_impl + : tuple_transformer, is_column>, field_type_t> {}; + + template<> + struct storage_mapped_columns_impl { + using type = std::tuple<>; + }; + + /** + * DBOs - db_objects_tuple type + * Lookup - mapped or unmapped data type + */ + template + struct storage_mapped_columns : storage_mapped_columns_impl> {}; + } + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/sync_schema_result.h b/libs/sqlite_orm-1.8.2/dev/sync_schema_result.h new file mode 100644 index 0000000..e4df5cc --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/sync_schema_result.h @@ -0,0 +1,61 @@ +#pragma once + +#include + +namespace sqlite_orm { + + enum class sync_schema_result { + + /** + * created new table, table with the same tablename did not exist + */ + new_table_created, + + /** + * table schema is the same as storage, nothing to be done + */ + already_in_sync, + + /** + * removed excess columns in table (than storage) without dropping a table + */ + old_columns_removed, + + /** + * lacking columns in table (than storage) added without dropping a table + */ + new_columns_added, + + /** + * both old_columns_removed and new_columns_added + */ + new_columns_added_and_old_columns_removed, + + /** + * old table is dropped and new is recreated. Reasons : + * 1. delete excess columns in the table than storage if preseve = false + * 2. Lacking columns in the table cannot be added due to NULL and DEFAULT constraint + * 3. Reasons 1 and 2 both together + * 4. data_type mismatch between table and storage. + */ + dropped_and_recreated, + }; + + 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"; + case sync_schema_result::already_in_sync: + return os << "table and storage is already in sync."; + case sync_schema_result::old_columns_removed: + return os << "old excess columns removed"; + case sync_schema_result::new_columns_added: + return os << "new columns added"; + case sync_schema_result::new_columns_added_and_old_columns_removed: + return os << "old excess columns removed and new columns added"; + case sync_schema_result::dropped_and_recreated: + return os << "old table dropped and recreated"; + } + return os; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/table.h b/libs/sqlite_orm-1.8.2/dev/table.h new file mode 100644 index 0000000..65073b6 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/table.h @@ -0,0 +1,310 @@ +#pragma once + +#include // std::string +#include // std::remove_reference, std::is_same, std::decay +#include // std::vector +#include // std::tuple_size, std::tuple_element +#include // std::forward, std::move + +#include "functional/cxx_universal.h" +#include "functional/cxx_type_traits_polyfill.h" +#include "functional/cxx_functional_polyfill.h" +#include "functional/static_magic.h" +#include "functional/mpl.h" +#include "functional/index_sequence_util.h" +#include "tuple_helper/tuple_filter.h" +#include "tuple_helper/tuple_traits.h" +#include "tuple_helper/tuple_iteration.h" +#include "member_traits/member_traits.h" +#include "typed_comparator.h" +#include "type_traits.h" +#include "constraints.h" +#include "table_info.h" +#include "column.h" + +namespace sqlite_orm { + + namespace internal { + + struct basic_table { + + /** + * Table name. + */ + std::string name; + }; + + /** + * Table definition. + */ + template + struct table_t : basic_table { + using object_type = O; + using elements_type = std::tuple; + + static constexpr bool is_without_rowid_v = WithoutRowId; + using is_without_rowid = polyfill::bool_constant; + + elements_type elements; + +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + table_t(std::string name_, elements_type elements_) : + basic_table{std::move(name_)}, elements{std::move(elements_)} {} +#endif + + table_t without_rowid() const { + return {this->name, this->elements}; + } + + /** + * Returns foreign keys count in table definition + */ + constexpr int foreign_keys_count() const { +#if SQLITE_VERSION_NUMBER >= 3006019 + using fk_index_sequence = filter_tuple_sequence_t; + return int(fk_index_sequence::size()); +#else + return 0; +#endif + } + + /** + * Function used to get field value from object by mapped member pointer/setter/getter. + * + * For a setter the corresponding getter has to be searched, + * so the method returns a pointer to the field as returned by the found getter. + * Otherwise the method invokes the member pointer and returns its result. + */ + template = true> + decltype(auto) object_field_value(const object_type& object, M memberPointer) const { + return polyfill::invoke(memberPointer, object); + } + + template = true> + const member_field_type_t* object_field_value(const object_type& object, M memberPointer) const { + using field_type = member_field_type_t; + const field_type* res = nullptr; + iterate_tuple(this->elements, + col_index_sequence_with_field_type{}, + call_as_template_base([&res, &memberPointer, &object](const auto& column) { + if(compare_any(column.setter, memberPointer)) { + res = &polyfill::invoke(column.member_pointer, object); + } + })); + return res; + } + + const basic_generated_always::storage_type* + find_column_generated_storage_type(const std::string& name) const { + const basic_generated_always::storage_type* result = nullptr; +#if SQLITE_VERSION_NUMBER >= 3031000 + iterate_tuple(this->elements, + col_index_sequence_with{}, + [&result, &name](auto& column) { + if(column.name != name) { + return; + } + using generated_op_index_sequence = + filter_tuple_sequence_t, + is_generated_always>; + constexpr size_t opIndex = first_index_sequence_value(generated_op_index_sequence{}); + result = &get(column.constraints).storage; + }); +#else + (void)name; +#endif + return result; + } + + template + bool exists_in_composite_primary_key(const column_field& column) const { + bool res = false; + this->for_each_primary_key([&column, &res](auto& primaryKey) { + using colrefs_tuple = decltype(primaryKey.columns); + using same_type_index_sequence = + filter_tuple_sequence_t>::template fn, + member_field_type_t>; + iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { + if(compare_any(memberPointer, column.member_pointer) || + compare_any(memberPointer, column.setter)) { + res = true; + } + }); + }); + return res; + } + + /** + * Call passed lambda with all defined primary keys. + */ + template + void for_each_primary_key(L&& lambda) const { + using pk_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->elements, pk_index_sequence{}, lambda); + } + + std::vector composite_key_columns_names() const { + std::vector res; + this->for_each_primary_key([this, &res](auto& primaryKey) { + res = this->composite_key_columns_names(primaryKey); + }); + return res; + } + + std::vector primary_key_column_names() const { + using pkcol_index_sequence = col_index_sequence_with; + + if(pkcol_index_sequence::size() > 0) { + return create_from_tuple>(this->elements, + pkcol_index_sequence{}, + &column_identifier::name); + } else { + return this->composite_key_columns_names(); + } + } + + template + void for_each_primary_key_column(L&& lambda) const { + iterate_tuple(this->elements, + col_index_sequence_with{}, + call_as_template_base([&lambda](const auto& column) { + lambda(column.member_pointer); + })); + this->for_each_primary_key([&lambda](auto& primaryKey) { + iterate_tuple(primaryKey.columns, lambda); + }); + } + + template + std::vector composite_key_columns_names(const primary_key_t& primaryKey) const { + return create_from_tuple>(primaryKey.columns, + [this, empty = std::string{}](auto& memberPointer) { + if(const std::string* columnName = + this->find_column_name(memberPointer)) { + return *columnName; + } else { + return empty; + } + }); + } + + /** + * Searches column name by class member pointer passed as the first argument. + * @return column name or empty string if nothing found. + */ + template = true> + const std::string* find_column_name(M m) const { + const std::string* res = nullptr; + using field_type = member_field_type_t; + iterate_tuple(this->elements, + col_index_sequence_with_field_type{}, + [&res, m](auto& c) { + if(compare_any(c.member_pointer, m) || compare_any(c.setter, m)) { + res = &c.name; + } + }); + return res; + } + + /** + * Counts and returns amount of columns without GENERATED ALWAYS constraints. Skips table constraints. + */ + constexpr int non_generated_columns_count() const { +#if SQLITE_VERSION_NUMBER >= 3031000 + using non_generated_col_index_sequence = + col_index_sequence_excluding; + return int(non_generated_col_index_sequence::size()); +#else + return this->count_columns_amount(); +#endif + } + + /** + * Counts and returns amount of columns. Skips constraints. + */ + constexpr int count_columns_amount() const { + using col_index_sequence = filter_tuple_sequence_t; + return int(col_index_sequence::size()); + } + + /** + * Call passed lambda with all defined foreign keys. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` + */ + template + void for_each_foreign_key(L&& lambda) const { + using fk_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->elements, fk_index_sequence{}, lambda); + } + + template + void for_each_foreign_key_to(L&& lambda) const { + using fk_index_sequence = filter_tuple_sequence_t; + using filtered_index_sequence = filter_tuple_sequence_t::template fn, + target_type_t, + fk_index_sequence>; + iterate_tuple(this->elements, filtered_index_sequence{}, lambda); + } + + /** + * Call passed lambda with all defined columns. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` + */ + template + void for_each_column(L&& lambda) const { + using col_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->elements, col_index_sequence{}, lambda); + } + + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. + */ + template class OpTraitFn, class L> + void for_each_column_excluding(L&& lambda) const { + iterate_tuple(this->elements, col_index_sequence_excluding{}, lambda); + } + + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. + */ + template = true> + void for_each_column_excluding(L&& lambda) const { + this->for_each_column_excluding(lambda); + } + + std::vector get_table_info() const; + }; + + template + struct is_table : std::false_type {}; + + template + struct is_table> : std::true_type {}; + } + + /** + * Factory function for a table definition. + * + * The mapped object type is determined implicitly from the first column definition. + */ + template>::object_type> + internal::table_t make_table(std::string name, Cs... args) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::make_tuple(std::forward(args)...)}); + } + + /** + * Factory function for a table definition. + * + * The mapped object type is explicitly specified. + */ + template + internal::table_t make_table(std::string name, Cs... args) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::make_tuple(std::forward(args)...)}); + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/table_info.h b/libs/sqlite_orm-1.8.2/dev/table_info.h new file mode 100644 index 0000000..087a3c6 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/table_info.h @@ -0,0 +1,52 @@ +#pragma once + +#include // std::string + +#include "functional/cxx_universal.h" + +namespace sqlite_orm { + + struct table_info { + int cid = 0; + std::string name; + std::string type; + bool notnull = false; + std::string dflt_value; + int pk = 0; + +#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) + 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(std::move(name_)), type(std::move(type_)), notnull(notnull_), dflt_value(std::move(dflt_value_)), + pk(pk_) {} +#endif + }; + + struct table_xinfo { + int cid = 0; + std::string name; + std::string type; + bool notnull = false; + std::string dflt_value; + int pk = 0; + int hidden = 0; // different than 0 => generated_always_as() - TODO verify + +#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) + table_xinfo(decltype(cid) cid_, + decltype(name) name_, + decltype(type) type_, + decltype(notnull) notnull_, + decltype(dflt_value) dflt_value_, + decltype(pk) pk_, + decltype(hidden) hidden_) : + cid(cid_), + name(std::move(name_)), type(std::move(type_)), notnull(notnull_), dflt_value(std::move(dflt_value_)), + pk(pk_), hidden{hidden_} {} +#endif + }; +} diff --git a/libs/sqlite_orm-1.8.2/dev/table_name_collector.h b/libs/sqlite_orm-1.8.2/dev/table_name_collector.h new file mode 100644 index 0000000..549b8c8 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/table_name_collector.h @@ -0,0 +1,97 @@ +#pragma once + +#include // std::set +#include // std::string +#include // std::pair, std::move + +#include "functional/cxx_type_traits_polyfill.h" +#include "type_traits.h" +#include "mapped_type_proxy.h" +#include "select_constraints.h" +#include "alias.h" +#include "core_functions.h" +#include "storage_lookup.h" + +namespace sqlite_orm { + + namespace internal { + + struct table_name_collector_base { + using table_name_set = std::set>; + + table_name_set table_names; + }; + + template + struct table_name_collector : table_name_collector_base { + using db_objects_type = DBOs; + + const db_objects_type& db_objects; + + table_name_collector() = default; + + table_name_collector(const db_objects_type& dbObjects) : db_objects{dbObjects} {} + + template + void operator()(const T&) const {} + + template + void operator()(F O::*) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + + template + void operator()(const column_pointer&) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + + template + void operator()(const alias_column_t&) { + // note: instead of accessing the column, we are interested in the type the column is aliased into + auto tableName = lookup_table_name>(this->db_objects); + this->table_names.emplace(std::move(tableName), alias_extractor::as_alias()); + } + + template + void operator()(const count_asterisk_t&) { + auto tableName = lookup_table_name(this->db_objects); + if(!tableName.empty()) { + this->table_names.emplace(std::move(tableName), ""); + } + } + + template + void operator()(const asterisk_t&) { + auto tableName = lookup_table_name>(this->db_objects); + table_names.emplace(std::move(tableName), alias_extractor::as_alias()); + } + + template + void operator()(const object_t&) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + + template + void operator()(const table_rowid_t&) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + + template + void operator()(const table_oid_t&) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + + template + void operator()(const table__rowid_t&) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + }; + + template = true> + table_name_collector make_table_name_collector(const DBOs& dbObjects) { + return {dbObjects}; + } + + } + +} diff --git a/libs/sqlite_orm-1.8.2/dev/table_type_of.h b/libs/sqlite_orm-1.8.2/dev/table_type_of.h new file mode 100644 index 0000000..47c9d9d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/table_type_of.h @@ -0,0 +1,43 @@ +#pragma once + +namespace sqlite_orm { + + namespace internal { + + template + struct column_pointer; + + template + struct indexed_column_t; + + /** + * 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_of::type` is `User` + * - `table_type_of::type` is `User` + * - `table_type_of::type` is `User` + */ + template + struct table_type_of; + + template + struct table_type_of { + using type = O; + }; + + template + struct table_type_of> { + using type = T; + }; + + template + struct table_type_of> { + using type = typename table_type_of::type; + }; + + template + using table_type_of_t = typename table_type_of::type; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/tags.h b/libs/sqlite_orm-1.8.2/dev/tags.h new file mode 100644 index 0000000..bc77292 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/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/libs/sqlite_orm-1.8.2/dev/transaction_guard.h b/libs/sqlite_orm-1.8.2/dev/transaction_guard.h new file mode 100644 index 0000000..3ad6d77 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/transaction_guard.h @@ -0,0 +1,80 @@ +#pragma once + +#include // std::function +#include // std::move + +#include "connection_holder.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * Class used as a guard for a transaction. Calls `ROLLBACK` in destructor. + * Has explicit `commit()` and `rollback()` functions. After explicit function is fired + * guard won't do anything in d-tor. Also you can set `commit_on_destroy` to true to + * make it call `COMMIT` on destroy. + * + * Note: The guard's destructor is explicitly marked as potentially throwing, + * so exceptions that occur during commit or rollback are propagated to the caller. + */ + struct transaction_guard_t { + /** + * This is a public lever to tell a guard what it must do in its destructor + * if `gotta_fire` is true + */ + bool commit_on_destroy = false; + + transaction_guard_t(connection_ref connection_, + std::function commit_func_, + std::function rollback_func_) : + connection(std::move(connection_)), + commit_func(std::move(commit_func_)), rollback_func(std::move(rollback_func_)) {} + + transaction_guard_t(transaction_guard_t&& other) : + commit_on_destroy(other.commit_on_destroy), connection(std::move(other.connection)), + commit_func(std::move(other.commit_func)), rollback_func(std::move(other.rollback_func)), + gotta_fire(other.gotta_fire) { + other.gotta_fire = false; + } + + ~transaction_guard_t() noexcept(false) { + if(this->gotta_fire) { + if(this->commit_on_destroy) { + this->commit_func(); + } else { + this->rollback_func(); + } + } + } + + transaction_guard_t& operator=(transaction_guard_t&&) = delete; + + /** + * Call `COMMIT` explicitly. After this call + * guard will not call `COMMIT` or `ROLLBACK` + * in its destructor. + */ + void commit() { + this->gotta_fire = false; + this->commit_func(); + } + + /** + * Call `ROLLBACK` explicitly. After this call + * guard will not call `COMMIT` or `ROLLBACK` + * in its destructor. + */ + void rollback() { + this->gotta_fire = false; + this->rollback_func(); + } + + protected: + connection_ref connection; + std::function commit_func; + std::function rollback_func; + bool gotta_fire = true; + }; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/triggers.h b/libs/sqlite_orm-1.8.2/dev/triggers.h new file mode 100644 index 0000000..c85e65a --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/triggers.h @@ -0,0 +1,289 @@ +#pragma once + +#include +#include +#include +#include + +#include "functional/cxx_universal.h" +#include "optional_container.h" + +// NOTE Idea : Maybe also implement a custom trigger system to call a c++ callback when a trigger triggers ? +// (Could be implemented with a normal trigger that insert or update an internal table and then retreive +// the event in the C++ code, to call the C++ user callback, with update hooks: https://www.sqlite.org/c3ref/update_hook.html) +// It could be an interesting feature to bring to sqlite_orm that other libraries don't have ? + +namespace sqlite_orm { + namespace internal { + enum class trigger_timing { trigger_before, trigger_after, trigger_instead_of }; + enum class trigger_type { trigger_delete, trigger_insert, trigger_update }; + + /** + * This class is an intermediate SQLite trigger, to be used with + * `make_trigger` to create a full trigger. + * T is the base of the trigger (contains its type, timing and associated table) + * S is the list of trigger statements + */ + template + struct partial_trigger_t { + using statements_type = std::tuple; + + /** + * Base of the trigger (contains its type, timing and associated table) + */ + T base; + /** + * Statements of the triggers (to be executed when the trigger fires) + */ + statements_type statements; + + partial_trigger_t(T trigger_base, S... statements) : + base{std::move(trigger_base)}, statements{std::make_tuple(std::forward(statements)...)} {} + + partial_trigger_t& end() { + return *this; + } + }; + + struct base_trigger { + /** + * Name of the trigger + */ + std::string name; + }; + + /** + * This class represent a SQLite trigger + * T is the base of the trigger (contains its type, timing and associated table) + * S is the list of trigger statments + */ + template + struct trigger_t : base_trigger { + using object_type = void; + using elements_type = typename partial_trigger_t::statements_type; + + /** + * Base of the trigger (contains its type, timing and associated table) + */ + T base; + + /** + * Statements of the triggers (to be executed when the trigger fires) + */ + elements_type elements; + +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + trigger_t(std::string name, T trigger_base, elements_type statements) : + base_trigger{std::move(name)}, base(std::move(trigger_base)), elements(std::move(statements)) {} +#endif + }; + + /** + * Base of a trigger. Contains the trigger type/timming and the table type + * T is the table type + * W is `when` expression type + * Type is the trigger base type (type+timing) + */ + template + struct trigger_base_t { + using table_type = T; + using when_type = W; + using trigger_type_base = Type; + + /** + * Contains the trigger type and timing + */ + trigger_type_base type_base; + /** + * Value used to determine if we execute the trigger on each row or on each statement + * (SQLite doesn't support the FOR EACH STATEMENT syntax yet: https://sqlite.org/lang_createtrigger.html#description + * so this value is more of a placeholder for a later update) + */ + bool do_for_each_row = false; + /** + * When expression (if any) + * If a WHEN expression is specified, the trigger will only execute + * if the expression evaluates to true when the trigger is fired + */ + optional_container container_when; + + trigger_base_t(trigger_type_base type_base_) : type_base(std::move(type_base_)) {} + + trigger_base_t& for_each_row() { + this->do_for_each_row = true; + return *this; + } + + template + trigger_base_t when(WW expression) { + trigger_base_t res(this->type_base); + res.container_when.field = std::move(expression); + return res; + } + + template + partial_trigger_t, S...> begin(S... statements) { + return {*this, std::forward(statements)...}; + } + }; + + /** + * Contains the trigger type and timing + */ + struct trigger_type_base_t { + /** + * Value indicating if the trigger is run BEFORE, AFTER or INSTEAD OF + * the statement that fired it. + */ + trigger_timing timing; + /** + * The type of the statement that would cause the trigger to fire. + * Can be DELETE, INSERT, or UPDATE. + */ + trigger_type type; + + trigger_type_base_t(trigger_timing timing, trigger_type type) : timing(timing), type(type) {} + + template + trigger_base_t on() { + return {*this}; + } + }; + + /** + * Special case for UPDATE OF (columns) + * Contains the trigger type and timing + */ + template + struct trigger_update_type_t : trigger_type_base_t { + using columns_type = std::tuple; + + /** + * Contains the columns the trigger is watching. Will only + * trigger if one of theses columns is updated. + */ + columns_type columns; + + trigger_update_type_t(trigger_timing timing, trigger_type type, Cs... columns) : + trigger_type_base_t(timing, type), columns(std::make_tuple(std::forward(columns)...)) {} + + template + trigger_base_t> on() { + return {*this}; + } + }; + + struct trigger_timing_t { + trigger_timing timing; + + trigger_type_base_t delete_() { + return {timing, trigger_type::trigger_delete}; + } + + trigger_type_base_t insert() { + return {timing, trigger_type::trigger_insert}; + } + + trigger_type_base_t update() { + return {timing, trigger_type::trigger_update}; + } + + template + trigger_update_type_t update_of(Cs... columns) { + return {timing, trigger_type::trigger_update, std::forward(columns)...}; + } + }; + + struct raise_t { + enum class type_t { + ignore, + rollback, + abort, + fail, + }; + + type_t type = type_t::ignore; + std::string message; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + raise_t(type_t type, std::string message) : type{type}, message{std::move(message)} {} +#endif + }; + + template + struct new_t { + using expression_type = T; + + expression_type expression; + }; + + template + struct old_t { + using expression_type = T; + + expression_type expression; + }; + } // NAMESPACE internal + + /** + * NEW.expression function used within TRIGGER expressions + */ + template + internal::new_t new_(T expression) { + return {std::move(expression)}; + } + + /** + * OLD.expression function used within TRIGGER expressions + */ + template + internal::old_t old(T expression) { + return {std::move(expression)}; + } + + /** + * RAISE(IGNORE) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_ignore() { + return {internal::raise_t::type_t::ignore, {}}; + } + + /** + * RAISE(ROLLBACK, %message%) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_rollback(std::string message) { + return {internal::raise_t::type_t::rollback, std::move(message)}; + } + + /** + * RAISE(ABORT, %message%) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_abort(std::string message) { + return {internal::raise_t::type_t::abort, std::move(message)}; + } + + /** + * RAISE(FAIL, %message%) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_fail(std::string message) { + return {internal::raise_t::type_t::fail, std::move(message)}; + } + + template + internal::trigger_t make_trigger(std::string name, const internal::partial_trigger_t& part) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::move(part.base), std::move(part.statements)}); + } + + inline internal::trigger_timing_t before() { + return {internal::trigger_timing::trigger_before}; + } + + inline internal::trigger_timing_t after() { + return {internal::trigger_timing::trigger_after}; + } + + inline internal::trigger_timing_t instead_of() { + return {internal::trigger_timing::trigger_instead_of}; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/tuple_helper/same_or_void.h b/libs/sqlite_orm-1.8.2/dev/tuple_helper/same_or_void.h new file mode 100644 index 0000000..9de9b5d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/tuple_helper/same_or_void.h @@ -0,0 +1,28 @@ +#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 = A; + }; + + template + struct same_or_void : same_or_void {}; + + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/tuple_helper/tuple_filter.h b/libs/sqlite_orm-1.8.2/dev/tuple_helper/tuple_filter.h new file mode 100644 index 0000000..2fea55d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/tuple_helper/tuple_filter.h @@ -0,0 +1,90 @@ +#pragma once + +#include // std::integral_constant, std::index_sequence, std::conditional, std::declval +#include // std::tuple + +#include "../functional/cxx_universal.h" +#include "../functional/index_sequence_util.h" + +namespace sqlite_orm { + namespace internal { + + template + using tuple_cat_t = decltype(std::tuple_cat(std::declval()...)); + + template + struct conc_tuple { + using type = tuple_cat_t; + }; + + template + struct tuple_from_index_sequence; + + template + struct tuple_from_index_sequence> { + using type = std::tuple...>; + }; + + template + using tuple_from_index_sequence_t = typename tuple_from_index_sequence::type; + + template class Pred, template class Proj, class Seq> + struct filter_tuple_sequence; + +#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template class Pred, template class Proj, size_t... Idx> + struct filter_tuple_sequence> + : flatten_idxseq>>::value, + std::index_sequence, + std::index_sequence<>>...> {}; +#else + template class Pred, class SFINAE = void> + struct tuple_seq_single { + using type = std::index_sequence<>; + }; + + template class Pred> + struct tuple_seq_single::value>> { + using type = std::index_sequence; + }; + + template class Pred, template class Proj, size_t... Idx> + struct filter_tuple_sequence> + : flatten_idxseq>, Pred>::type...> {}; +#endif + + template + class Pred, + template class Proj = polyfill::type_identity_t, + class Seq = std::make_index_sequence::value>> + using filter_tuple_sequence_t = typename filter_tuple_sequence::type; + + template + class Pred, + template class FilterProj = polyfill::type_identity_t, + class Seq = std::make_index_sequence::value>> + using filter_tuple_t = tuple_from_index_sequence_t>; + + template + class Pred, + template class FilterProj = polyfill::type_identity_t> + struct count_tuple : std::integral_constant::size()> {}; + + /* + * Count a tuple, picking only those elements specified in the index sequence. + * + * Implementation note: must be distinct from `count_tuple` because legacy compilers have problems + * with a default Sequence in function template parameters [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. + */ + template + class Pred, + class Seq, + template class FilterProj = polyfill::type_identity_t> + struct count_filtered_tuple + : std::integral_constant::size()> {}; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/tuple_helper/tuple_fy.h b/libs/sqlite_orm-1.8.2/dev/tuple_helper/tuple_fy.h new file mode 100644 index 0000000..3e2ec3b --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/tuple_helper/tuple_fy.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace sqlite_orm { + + namespace internal { + + template + struct tuplify { + using type = std::tuple; + }; + template + struct tuplify> { + using type = std::tuple; + }; + + template + using tuplify_t = typename tuplify::type; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/tuple_helper/tuple_iteration.h b/libs/sqlite_orm-1.8.2/dev/tuple_helper/tuple_iteration.h new file mode 100644 index 0000000..4d0a971 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/tuple_helper/tuple_iteration.h @@ -0,0 +1,129 @@ +#pragma once + +#include // std::tuple, std::get, std::tuple_element, std::tuple_size +#include // std::index_sequence, std::make_index_sequence +#include // std::forward, std::move + +#include "../functional/cxx_universal.h" +#include "../functional/cxx_type_traits_polyfill.h" +#include "../functional/cxx_functional_polyfill.h" +#include "../functional/index_sequence_util.h" + +namespace sqlite_orm { + 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(std::move(t))...); + } + + template + auto call(Function& f, FunctionPointer functionPointer, Tuple t) { + constexpr size_t size = std::tuple_size::value; + return call_impl(f, functionPointer, std::move(t), std::make_index_sequence{}); + } + + template + auto call(Function& f, Tuple t) { + return call(f, &Function::operator(), std::move(t)); + } + +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + template + void iterate_tuple(const Tpl& tpl, std::index_sequence, L&& lambda) { + if constexpr(reversed) { + // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= + int sink; + ((lambda(std::get(tpl)), sink) = ... = 0); + } else { + (lambda(std::get(tpl)), ...); + } + } +#else + template + void iterate_tuple(const Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} + + template + void iterate_tuple(const Tpl& tpl, std::index_sequence, L&& lambda) { +#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr(reversed) { +#else + if(reversed) { +#endif + iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); + lambda(std::get(tpl)); + } else { + lambda(std::get(tpl)); + iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); + } + } +#endif + template + void iterate_tuple(const Tpl& tpl, L&& lambda) { + iterate_tuple(tpl, + std::make_index_sequence::value>{}, + std::forward(lambda)); + } + +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + void iterate_tuple(std::index_sequence, L&& lambda) { + (lambda((std::tuple_element_t*)nullptr), ...); + } +#else + template + void iterate_tuple(std::index_sequence<>, L&& /*lambda*/) {} + + template + void iterate_tuple(std::index_sequence, L&& lambda) { + lambda((std::tuple_element_t*)nullptr); + iterate_tuple(std::index_sequence{}, std::forward(lambda)); + } +#endif + template + void iterate_tuple(L&& lambda) { + iterate_tuple(std::make_index_sequence::value>{}, std::forward(lambda)); + } + + template + R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { + return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; + } + + template + R create_from_tuple(Tpl&& tpl, Projection project = {}) { + return create_from_tuple( + std::forward(tpl), + std::make_index_sequence>::value>{}, + std::forward(project)); + } + + template class Base, class L> + struct lambda_as_template_base : L { +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + lambda_as_template_base(L&& lambda) : L{std::move(lambda)} {} +#endif + template + decltype(auto) operator()(const Base& object) { + return L::operator()(object); + } + }; + + /* + * This method wraps the specified callable in another function object, + * which in turn implicitly casts its single argument to the specified template base class, + * then passes the converted argument to the lambda. + * + * Note: This method is useful for reducing combinatorial instantiation of template lambdas, + * as long as this library supports compilers that do not implement + * explicit template parameters in generic lambdas [SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED]. + * Unfortunately it doesn't work with user-defined conversion operators in order to extract + * parts of a class. In other words, the destination type must be a direct template base class. + */ + template class Base, class L> + lambda_as_template_base call_as_template_base(L lambda) { + return {std::move(lambda)}; + } + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/tuple_helper/tuple_traits.h b/libs/sqlite_orm-1.8.2/dev/tuple_helper/tuple_traits.h new file mode 100644 index 0000000..becf162 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/tuple_helper/tuple_traits.h @@ -0,0 +1,53 @@ +#pragma once + +#include // std::is_same +#include + +#include "../functional/cxx_type_traits_polyfill.h" +#include "../functional/mpl.h" + +namespace sqlite_orm { + namespace internal { + /* + * Higher-order trait metafunction that checks whether a tuple contains a type with given trait. + */ + template class TraitFn, class Tuple> + struct tuple_has {}; + template class TraitFn, class... Types> + struct tuple_has> : polyfill::disjunction...> {}; + + /* + * Trait metafunction class that checks whether a tuple contains a type with given trait. + */ + template class TraitFn> + using check_if_tuple_has = mpl::bind_front_higherorder_fn; + + /* + * Trait metafunction class that checks whether a tuple doesn't contain a type with given trait. + */ + template class TraitFn> + using check_if_tuple_has_not = mpl::not_>; + + /* + * Metafunction class that checks whether a tuple contains given type. + */ + template + using check_if_tuple_has_type = mpl::bind_front_higherorder_fn::template fn>; + + /* + * Metafunction class that checks whether a tuple contains a given template. + * + * Note: we are using 2 small tricks: + * 1. A template template parameter can be treated like a metafunction, so we can just "quote" a 'primary' + * template into the MPL system (e.g. `std::vector`). + * 2. This metafunction class does the opposite of the trait function `is_specialization`: + * `is_specialization` tries to instantiate the primary template template parameter using the + * template parameters of a template type, then compares both instantiated types. + * Here instead, `pass_extracted_fn_to` extracts the template template parameter from a template type, + * then compares the resulting template template parameters. + */ + template class Primary> + using check_if_tuple_has_template = + mpl::bind_front_higherorder_fn::template fn>; + } +} \ No newline at end of file diff --git a/libs/sqlite_orm-1.8.2/dev/tuple_helper/tuple_transformer.h b/libs/sqlite_orm-1.8.2/dev/tuple_helper/tuple_transformer.h new file mode 100644 index 0000000..9c7a73c --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/tuple_helper/tuple_transformer.h @@ -0,0 +1,26 @@ +#pragma once + +#include // std::tuple + +#include "../functional/mpl.h" + +namespace sqlite_orm { + namespace internal { + + template class Op> + struct tuple_transformer; + + template class Op> + struct tuple_transformer, Op> { + using type = std::tuple...>; + }; + + /* + * Transform specified tuple. + * + * `Op` is a metafunction operation. + */ + template class Op> + using transform_tuple_t = typename tuple_transformer::type; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/type_is_nullable.h b/libs/sqlite_orm-1.8.2/dev/type_is_nullable.h new file mode 100644 index 0000000..1f77b95 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/type_is_nullable.h @@ -0,0 +1,41 @@ +#pragma once + +#include // std::false_type, std::true_type, std::enable_if +#include // std::shared_ptr, std::unique_ptr +#include "functional/cxx_optional.h" + +#include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + + /** + * 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 : std::false_type { + bool operator()(const T&) const { + return true; + } + }; + + /** + * This is a specialization for std::shared_ptr, std::unique_ptr, std::optional, which are nullable in sqlite_orm. + */ + template + struct type_is_nullable, +#endif + polyfill::is_specialization_of, + polyfill::is_specialization_of>>> : std::true_type { + bool operator()(const T& t) const { + return static_cast(t); + } + }; + +} diff --git a/libs/sqlite_orm-1.8.2/dev/type_printer.h b/libs/sqlite_orm-1.8.2/dev/type_printer.h new file mode 100644 index 0000000..6623dcd --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/type_printer.h @@ -0,0 +1,81 @@ +#pragma once + +#include // std::string +#include // std::shared_ptr, std::unique_ptr +#include // std::vector +#include "functional/cxx_optional.h" + +#include "functional/cxx_type_traits_polyfill.h" +#include "type_traits.h" +#include "is_std_ptr.h" + +namespace sqlite_orm { + + /** + * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) + */ + template + struct type_printer {}; + + struct integer_printer { + const std::string& print() const { + static const std::string res = "INTEGER"; + return res; + } + }; + + struct text_printer { + const std::string& print() const { + static const std::string res = "TEXT"; + return res; + } + }; + + struct real_printer { + const std::string& print() const { + static const std::string res = "REAL"; + return res; + } + }; + + struct blob_printer { + const std::string& print() const { + static const std::string res = "BLOB"; + return res; + } + }; + + // Note: char, unsigned/signed char are used for storing integer values, not char values. + template + struct type_printer>, + std::is_integral>>> : integer_printer { + }; + + template + struct type_printer::value>> : real_printer {}; + + template + struct type_printer, + std::is_base_of, + std::is_base_of>>> : text_printer {}; + + template + struct type_printer::value>> : type_printer {}; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct type_printer>> + : type_printer {}; +#endif + + template<> + struct type_printer, void> : blob_printer {}; +} diff --git a/libs/sqlite_orm-1.8.2/dev/type_traits.h b/libs/sqlite_orm-1.8.2/dev/type_traits.h new file mode 100644 index 0000000..a3a1628 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/type_traits.h @@ -0,0 +1,61 @@ +#pragma once + +#include // std::enable_if, std::is_same + +#include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + // C++ generic traits used throughout the library + namespace internal { + template + using is_any_of = polyfill::disjunction...>; + + // enable_if for types + template class Op, class... Args> + using match_if = std::enable_if_t::value>; + + // enable_if for types + template class Op, class... Args> + using match_if_not = std::enable_if_t>>; + + // enable_if for types + template class Primary> + using match_specialization_of = std::enable_if_t>; + + // enable_if for functions + template class Op, class... Args> + using satisfies = std::enable_if_t::value, bool>; + + // enable_if for functions + template class Op, class... Args> + using satisfies_not = std::enable_if_t>::value, bool>; + + // enable_if for functions + template class Primary> + using satisfies_is_specialization_of = std::enable_if_t, bool>; + } + + // type name template aliases for syntactic sugar + namespace internal { + template + using type_t = typename T::type; + + template + using field_type_t = typename T::field_type; + + template + using constraints_type_t = typename T::constraints_type; + + template + using object_type_t = typename T::object_type; + + template + using elements_type_t = typename T::elements_type; + + template + using target_type_t = typename T::target_type; + + template + using on_type_t = typename T::on_type; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/typed_comparator.h b/libs/sqlite_orm-1.8.2/dev/typed_comparator.h new file mode 100644 index 0000000..d911c1b --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/typed_comparator.h @@ -0,0 +1,16 @@ +#pragma once + +namespace sqlite_orm { + + namespace internal { + + template + bool compare_any(const L& /*lhs*/, const R& /*rhs*/) { + return false; + } + template + bool compare_any(const O& lhs, const O& rhs) { + return lhs == rhs; + } + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/util.h b/libs/sqlite_orm-1.8.2/dev/util.h new file mode 100644 index 0000000..f162eed --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/util.h @@ -0,0 +1,130 @@ +#pragma once + +#include +#include // std::string +#include // std::move + +#include "error_code.h" + +namespace sqlite_orm { + + /** + * Escape the provided character in the given string by doubling it. + * @param str A copy of the original string + * @param char2Escape The character to escape + */ + inline std::string sql_escape(std::string str, char char2Escape) { + for(size_t pos = 0; (pos = str.find(char2Escape, pos)) != str.npos; pos += 2) { + str.replace(pos, 1, 2, char2Escape); + } + + return str; + } + + /** + * Quote the given string value using single quotes, + * escape containing single quotes by doubling them. + */ + inline std::string quote_string_literal(std::string v) { + constexpr char quoteChar = '\''; + return quoteChar + sql_escape(std::move(v), quoteChar) + quoteChar; + } + + /** + * Quote the given string value using single quotes, + * escape containing single quotes by doubling them. + */ + inline std::string quote_blob_literal(std::string v) { + constexpr char quoteChar = '\''; + return std::string{char('x'), quoteChar} + std::move(v) + quoteChar; + } + + /** + * Quote the given identifier using double quotes, + * escape containing double quotes by doubling them. + */ + inline std::string quote_identifier(std::string identifier) { + constexpr char quoteChar = '"'; + return quoteChar + sql_escape(std::move(identifier), quoteChar) + quoteChar; + } + + namespace internal { + // Wrapper to reduce boiler-plate code + inline sqlite3_stmt* reset_stmt(sqlite3_stmt* stmt) { + sqlite3_reset(stmt); + return stmt; + } + + // note: query is deliberately taken by value, such that it is thrown away early + inline sqlite3_stmt* prepare_stmt(sqlite3* db, std::string query) { + sqlite3_stmt* stmt; + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + return stmt; + } + + inline 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_translated_sqlite_error(db); + } + } + + inline void perform_exec(sqlite3* db, + const char* query, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) { + int rc = sqlite3_exec(db, query, callback, user_data, nullptr); + if(rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + + inline void perform_exec(sqlite3* db, + const std::string& query, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) { + return perform_exec(db, query.c_str(), callback, user_data); + } + + template + void perform_step(sqlite3_stmt* stmt) { + int rc = sqlite3_step(stmt); + if(rc != expected) { + throw_translated_sqlite_error(stmt); + } + } + + template + void perform_step(sqlite3_stmt* stmt, L&& lambda) { + switch(int rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + break; + default: { + throw_translated_sqlite_error(stmt); + } + } + } + + template + void perform_steps(sqlite3_stmt* stmt, L&& lambda) { + int rc; + do { + switch(rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + break; + default: { + throw_translated_sqlite_error(stmt); + } + } + } while(rc != SQLITE_DONE); + } + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/values.h b/libs/sqlite_orm-1.8.2/dev/values.h new file mode 100644 index 0000000..42229dc --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/values.h @@ -0,0 +1,44 @@ +#pragma once + +#include // std::vector +#include // std::tuple +#include // std::forward + +#include "functional/cxx_universal.h" +#include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct values_t { + using args_tuple = std::tuple; + + args_tuple tuple; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_values_v = polyfill::is_specialization_of_v; + + template + using is_values = polyfill::bool_constant>; + + 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 {{std::move(vector)}}; + } + +} diff --git a/libs/sqlite_orm-1.8.2/dev/values_to_tuple.h b/libs/sqlite_orm-1.8.2/dev/values_to_tuple.h new file mode 100644 index 0000000..a85201d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/values_to_tuple.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include // std::index_sequence, std::make_index_sequence +#include // std::tuple, std::tuple_size, std::get + +#include "functional/cxx_universal.h" +#include "row_extractor.h" +#include "arg_values.h" + +namespace sqlite_orm { + + namespace internal { + + struct values_to_tuple { + template + void operator()(sqlite3_value** values, Tpl& tuple, int /*argsCount*/) const { + (*this)(values, tuple, std::make_index_sequence::value>{}); + } + + void operator()(sqlite3_value** values, std::tuple& tuple, int argsCount) const { + std::get<0>(tuple) = arg_values(argsCount, values); + } + + private: +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + void operator()(sqlite3_value** values, Tpl& tuple, std::index_sequence) const { + (this->extract(values[Idx], std::get(tuple)), ...); + } +#else + template + void operator()(sqlite3_value** values, Tpl& tuple, std::index_sequence) const { + this->extract(values[I], std::get(tuple)); + (*this)(values, tuple, std::index_sequence{}); + } + template + void operator()(sqlite3_value** /*values*/, Tpl&, std::index_sequence) const {} +#endif + template + void extract(sqlite3_value* value, T& t) const { + t = row_extractor{}.extract(value); + } + }; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/view.h b/libs/sqlite_orm-1.8.2/dev/view.h new file mode 100644 index 0000000..36c5442 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/view.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include // std::string +#include // std::forward, std::move +#include // std::tuple, std::make_tuple + +#include "row_extractor.h" +#include "error_code.h" +#include "iterator.h" +#include "ast_iterator.h" +#include "prepared_statement.h" +#include "connection_holder.h" +#include "util.h" + +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; + connection_ref connection; + get_all_t, 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() const { + return this->storage.template count(); + } + + bool empty() const { + return !this->size(); + } + + iterator_t begin() { + using context_t = serializer_context; + context_t context{obtain_db_objects(this->storage)}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + + statement_finalizer stmt{prepare_stmt(this->connection.get(), serialize(this->args, context))}; + iterate_ast(this->args.conditions, conditional_binder{stmt.get()}); + return {std::move(stmt), *this}; + } + + iterator_t end() { + return {}; + } + }; + } +} diff --git a/libs/sqlite_orm-1.8.2/dev/xdestroy_handling.h b/libs/sqlite_orm-1.8.2/dev/xdestroy_handling.h new file mode 100644 index 0000000..75b89c6 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/dev/xdestroy_handling.h @@ -0,0 +1,250 @@ +#pragma once + +#include // std::integral_constant +#if defined(SQLITE_ORM_CONCEPTS_SUPPORTED) && SQLITE_ORM_HAS_INCLUDE() +#include +#endif + +#include "functional/cxx_universal.h" +#include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + + using xdestroy_fn_t = void (*)(void*); + using null_xdestroy_t = std::integral_constant; + SQLITE_ORM_INLINE_VAR constexpr null_xdestroy_t null_xdestroy_f{}; +} + +namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_CONCEPTS_SUPPORTED + /** + * Constrains a deleter to be state-less. + */ + template + concept stateless_deleter = std::is_empty_v && std::is_default_constructible_v; + + /** + * Constrains a deleter to be an integral function constant. + */ + template + concept integral_fp_c = requires { + typename D::value_type; + D::value; + requires std::is_function_v>; + }; + + /** + * Constrains a deleter to be or to yield a function pointer. + */ + template + concept yields_fp = requires(D d) { + // yielding function pointer by using the plus trick + { +d }; + requires std::is_function_v>; + }; +#endif + +#if __cpp_lib_concepts >= 201907L + /** + * Yield a deleter's function pointer. + */ + template + struct yield_fp_of { + using type = decltype(+std::declval()); + }; +#else + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_stateless_deleter_v = + std::is_empty::value && std::is_default_constructible::value; + + template + struct is_integral_fp_c : std::false_type {}; + template + struct is_integral_fp_c< + D, + polyfill::void_t>::value>>> + : std::true_type {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_integral_fp_c_v = is_integral_fp_c::value; + + template + struct can_yield_fp : std::false_type {}; + template + struct can_yield_fp< + D, + polyfill::void_t< + decltype(+std::declval()), + std::enable_if_t())>>::value>>> + : std::true_type {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool can_yield_fp_v = can_yield_fp::value; + + template> + struct yield_fp_of { + using type = void; + }; + template + struct yield_fp_of { + using type = decltype(+std::declval()); + }; +#endif + template + using yielded_fn_t = typename yield_fp_of::type; + +#if __cpp_lib_concepts >= 201907L + template + concept is_unusable_for_xdestroy = (!stateless_deleter && + (yields_fp && !std::convertible_to, xdestroy_fn_t>)); + + /** + * This concept tests whether a deleter yields a function pointer, which is convertible to an xdestroy function pointer. + * Note: We are using 'is convertible' rather than 'is same' because of any exception specification. + */ + template + concept yields_xdestroy = yields_fp && std::convertible_to, xdestroy_fn_t>; + + template + concept needs_xdestroy_proxy = (stateless_deleter && + (!yields_fp || !std::convertible_to, xdestroy_fn_t>)); + + /** + * xDestroy function that constructs and invokes the stateless deleter. + * + * Requires that the deleter can be called with the q-qualified pointer argument; + * it doesn't check so explicitly, but a compiler error will occur. + */ + template + requires(!integral_fp_c) + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder> + auto o = (P*)p; + // ignoring return code + (void)D{}(o); + } + + /** + * xDestroy function that invokes the integral function pointer constant. + * + * Performs a const-cast of the argument pointer in order to allow for C API functions + * that take a non-const parameter, but user code passes a pointer to a const object. + */ + template + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder>, + auto o = (std::remove_cv_t

*)(P*)p; + // ignoring return code + (void)D{}(o); + } +#else + template + SQLITE_ORM_INLINE_VAR constexpr bool is_unusable_for_xdestroy_v = + !is_stateless_deleter_v && + (can_yield_fp_v && !std::is_convertible, xdestroy_fn_t>::value); + + template + SQLITE_ORM_INLINE_VAR constexpr bool can_yield_xdestroy_v = + can_yield_fp_v && std::is_convertible, xdestroy_fn_t>::value; + + template + SQLITE_ORM_INLINE_VAR constexpr bool needs_xdestroy_proxy_v = + is_stateless_deleter_v && + (!can_yield_fp_v || !std::is_convertible, xdestroy_fn_t>::value); + + template, bool> = true> + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder> + auto o = (P*)p; + // ignoring return code + (void)D{}(o); + } + + template, bool> = true> + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder>, + auto o = (std::remove_cv_t

*)(P*)p; + // ignoring return code + (void)D{}(o); + } +#endif + } +} + +namespace sqlite_orm { + +#if __cpp_lib_concepts >= 201907L + /** + * Prohibits using a yielded function pointer, which is not of type xdestroy_fn_t. + * + * Explicitly declared for better error messages. + */ + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept + requires(internal::is_unusable_for_xdestroy) + { + static_assert(polyfill::always_false_v, + "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); + return nullptr; + } + + /** + * Obtains a proxy 'xDestroy' function pointer [of type void(*)(void*)] + * for a deleter in a type-safe way. + * + * The deleter can be one of: + * - integral function constant + * - state-less (empty) deleter + * - non-capturing lambda + * + * Type-safety is garanteed by checking whether the deleter or yielded function pointer + * is invocable with the non-q-qualified pointer value. + */ + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept + requires(internal::needs_xdestroy_proxy) + { + return internal::xdestroy_proxy; + } + + /** + * Directly obtains a 'xDestroy' function pointer [of type void(*)(void*)] + * from a deleter in a type-safe way. + * + * The deleter can be one of: + * - function pointer of type xdestroy_fn_t + * - structure holding a function pointer + * - integral function constant + * - non-capturing lambda + * ... and yield a function pointer of type xdestroy_fn_t. + * + * Type-safety is garanteed by checking whether the deleter or yielded function pointer + * is invocable with the non-q-qualified pointer value. + */ + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P*) noexcept + requires(internal::yields_xdestroy) + { + return d; + } +#else + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) { + static_assert(polyfill::always_false_v, + "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); + return nullptr; + } + + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept { + return internal::xdestroy_proxy; + } + + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P*) noexcept { + return d; + } +#endif +} diff --git a/libs/sqlite_orm-1.8.2/examples/CMakeLists.txt b/libs/sqlite_orm-1.8.2/examples/CMakeLists.txt new file mode 100644 index 0000000..c9268a7 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required (VERSION 3.16) + +# note: find_package(SQLite3 REQUIRED) already done in top-level CMakeLists + +file(GLOB files "*.cpp") +foreach(file ${files}) + get_filename_component(file_basename ${file} NAME_WE) + add_executable(${file_basename} ${file}) + # note: sqlite3 already linked in top-level CMakeLists + target_link_libraries(${file_basename} PRIVATE sqlite_orm) +endforeach() diff --git a/libs/sqlite_orm-1.8.2/examples/blob.cpp b/libs/sqlite_orm-1.8.2/examples/blob.cpp new file mode 100644 index 0000000..279ef84 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/blob.cpp @@ -0,0 +1,46 @@ + +#include +#include +#include +#include +#include + +using std::cout; +using std::endl; + +struct User { + int id; + std::string name; + std::vector hash; // binary format +}; + +int main(int, char**) { + using namespace sqlite_orm; + auto storage = make_storage("blob.sqlite", + make_table("users", + make_column("id", &User::id, primary_key()), + make_column("name", &User::name), + make_column("hash", &User::hash))); + storage.sync_schema(); + storage.remove_all(); + + User alex{ + 0, + "Alex", + {0x10, 0x20, 0x30, 0x40}, + }; + alex.id = storage.insert(alex); + + cout << "users count = " << storage.count() << endl; + + cout << "alex = " << storage.dump(storage.get(alex.id)) << endl; + + auto hash = storage.get(alex.id).hash; + assert(hash.size() == 4); + assert(hash[0] == 0x10); + assert(hash[1] == 0x20); + assert(hash[2] == 0x30); + assert(hash[3] == 0x40); + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/blob_binding.cpp b/libs/sqlite_orm-1.8.2/examples/blob_binding.cpp new file mode 100644 index 0000000..3979f1c --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/blob_binding.cpp @@ -0,0 +1,149 @@ +#include +#include + +using namespace sqlite_orm; +using std::cout; +using std::endl; + +/** + * This is a struct which we want to store as BLOB. + * Format is simple: every int field will be stored as + * 4 byte value one by one. 16 bytes total. + */ +struct Rect { + int x = 0; + int y = 0; + int width = 0; + int height = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Rect() = default; + Rect(int x, int y, int width, int height) : x{x}, y{y}, width{width}, height{height} {} +#endif +}; + +bool operator==(const Rect& lhs, const Rect& rhs) { + return lhs.x == rhs.x && lhs.y == rhs.y && lhs.width == rhs.width && lhs.height == rhs.height; +} + +struct Zone { + int id = 0; + Rect rect; // this member will be mapped as BLOB column + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Zone() = default; + Zone(int id, Rect rect) : id{id}, rect{rect} {} +#endif +}; + +bool operator==(const Zone& lhs, const Zone& rhs) { + return lhs.id == rhs.id && lhs.rect == rhs.rect; +} + +namespace sqlite_orm { + + /** + * First of all is a type_printer template class. + * It is responsible for sqlite type string representation. + * We want Rect to be `BLOB` so let's just derive from + * blob_printer. Also there are other printers: real_printer, + * integer_printer and text_printer. + */ + template<> + struct type_printer : public blob_printer {}; + + /** + * This is a binder class. It is used to bind c++ values to sqlite queries. + * Here we have to create rect binary representation and bind it as std::vector. + * Any statement_binder specialization must have `int bind(sqlite3_stmt*, int, const T&)` function + * which returns bind result. Also you can call any of `sqlite3_bind_*` functions directly. + * More here https://www.sqlite.org/c3ref/bind_blob.html + */ + template<> + struct statement_binder { + + int bind(sqlite3_stmt* stmt, int index, const Rect& value) { + std::vector blobValue; + blobValue.reserve(16); + auto encodeInteger = [&blobValue](int value) { + auto preciseValue = int32_t(value); + const auto intPointer = &preciseValue; + auto charPointer = (const char*)(intPointer); + blobValue.push_back(charPointer[0]); + blobValue.push_back(charPointer[1]); + blobValue.push_back(charPointer[2]); + blobValue.push_back(charPointer[3]); + }; + encodeInteger(value.x); + encodeInteger(value.y); + encodeInteger(value.width); + encodeInteger(value.height); + return statement_binder>().bind(stmt, index, blobValue); + } + }; + + /** + * field_printer is used in `dump` and `where` functions. Here we have to create + * a string from mapped object. + */ + template<> + struct field_printer { + std::string operator()(const Rect& value) const { + std::stringstream ss; + ss << "{ x = " << value.x << ", y = " << value.y << ", width = " << value.width + << ", height = " << value.height << " }"; + return ss.str(); + } + }; + + /** + * This is a reverse operation: here we have to specify a way to transform std::vector received from + * database to our Rect object. Every `row_extractor` specialization must have `extract(sqlite3_stmt *stmt, + * int columnIndex)` function which returns a mapped type value. + */ + template<> + struct row_extractor { + + Rect extract(sqlite3_stmt* stmt, int columnIndex) { + auto blobPointer = sqlite3_column_blob(stmt, columnIndex); + auto charPointer = (const char*)blobPointer; + Rect value; + auto decodeInteger = [charPointer](int& integer, int index) { + auto pointerWithOffset = charPointer + index * 4; + auto intPointer = (const int32_t*)pointerWithOffset; + integer = int(*intPointer); + }; + decodeInteger(value.x, 0); + decodeInteger(value.y, 1); + decodeInteger(value.width, 2); + decodeInteger(value.height, 3); + return value; + } + }; +} + +int main() { + auto storage = make_storage( + {}, + make_table("zones", make_column("id", &Zone::id, primary_key()), make_column("rect", &Zone::rect))); + storage.sync_schema(); + storage.replace(Zone{1, Rect{10, 10, 200, 300}}); + + auto allZones = storage.get_all(); + cout << "zones count = " << allZones.size() << ":" << endl; // zones count = 1: + for(auto& zone: allZones) { + cout << "zone = " << storage.dump(zone) + << endl; // zone = { id : '1', rect : '{ x = 10, y = 10, width = 200, height = 300 }' } + } + + storage.update_all(set(c(&Zone::rect) = Rect{20, 20, 500, 600}), where(c(&Zone::id) == 1)); + cout << endl; + allZones = storage.get_all(); + cout << "zones count = " << allZones.size() << ":" << endl; // zones count = 1: + for(auto& zone: allZones) { + cout << "zone = " << storage.dump(zone) + << endl; // zone = { id : '1', rect : '{ x = 20, y = 20, width = 500, height = 600 }' } + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/case.cpp b/libs/sqlite_orm-1.8.2/examples/case.cpp new file mode 100644 index 0000000..8d288e8 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/case.cpp @@ -0,0 +1,111 @@ +/** + * Got it from here https://www.tutlane.com/tutorial/sqlite/sqlite-case-statement + */ +#include +#include + +using namespace sqlite_orm; + +using std::cout; +using std::endl; + +int main() { + struct Student { + int id = 0; + std::string name; + std::string email; + float marks = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Student() {} + Student(int id, std::string name, std::string email, float marks) : + id{id}, name{std::move(name)}, email{std::move(email)}, marks{marks} {} +#endif + }; + + auto storage = make_storage({}, + make_table("STUDENT", + make_column("ID", &Student::id, primary_key()), + make_column("NAME", &Student::name), + make_column("EMAIL", &Student::email), + make_column("MARKS", &Student::marks))); + storage.sync_schema(); + + storage.transaction([&storage] { + storage.replace(Student{1, "Shweta", "shweta@gmail.com", 80}); + storage.replace(Student{2, "Yamini", "rani@gmail.com", 60}); + storage.replace(Student{3, "Sonal", "sonal@gmail.com", 50}); + storage.replace(Student{4, "Jagruti", "jagu@gmail.com", 30}); + return true; + }); + + // list all students + for(auto& student: storage.iterate()) { + cout << storage.dump(student) << endl; + } + cout << endl; + + { // without alias + + // SELECT ID, NAME, MARKS, + // CASE + // WHEN MARKS >=80 THEN 'A+' + // WHEN MARKS >=70 THEN 'A' + // WHEN MARKS >=60 THEN 'B' + // WHEN MARKS >=50 THEN 'C' + // ELSE 'Sorry!! Failed' + // END + // FROM STUDENT; + auto rows = storage.select(columns(&Student::id, + &Student::name, + &Student::marks, + case_() + .when(greater_or_equal(&Student::marks, 80), then("A+")) + .when(greater_or_equal(&Student::marks, 70), then("A")) + .when(greater_or_equal(&Student::marks, 60), then("B")) + .when(greater_or_equal(&Student::marks, 50), then("C")) + .else_("Sorry!! Failed") + .end())); + for(auto& row: rows) { + cout << std::get<0>(row) << ' ' << std::get<1>(row) << ' ' << std::get<2>(row) << ' ' << std::get<3>(row) + << endl; + } + cout << endl; + } + { // with alias + + struct GradeAlias : alias_tag { + static const std::string& get() { + static const std::string res = "Grade"; + return res; + } + }; + + // SELECT ID, NAME, MARKS, + // CASE + // WHEN MARKS >=80 THEN 'A+' + // WHEN MARKS >=70 THEN 'A' + // WHEN MARKS >=60 THEN 'B' + // WHEN MARKS >=50 THEN 'C' + // ELSE 'Sorry!! Failed' + // END as 'Grade' + // FROM STUDENT; + auto rows = storage.select(columns(&Student::id, + &Student::name, + &Student::marks, + as(case_() + .when(greater_or_equal(&Student::marks, 80), then("A+")) + .when(greater_or_equal(&Student::marks, 70), then("A")) + .when(greater_or_equal(&Student::marks, 60), then("B")) + .when(greater_or_equal(&Student::marks, 50), then("C")) + .else_("Sorry!! Failed") + .end()))); + for(auto& row: rows) { + cout << std::get<0>(row) << ' ' << std::get<1>(row) << ' ' << std::get<2>(row) << ' ' << std::get<3>(row) + << endl; + } + cout << endl; + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/check.cpp b/libs/sqlite_orm-1.8.2/examples/check.cpp new file mode 100644 index 0000000..c19456d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/check.cpp @@ -0,0 +1,75 @@ +#include +#include +#include + +using namespace sqlite_orm; +using std::cout; +using std::endl; + +int main() { + struct Contact { + int id = 0; + std::string firstName; + std::string lastName; + std::string email; + std::string phone; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Contact() {} + Contact(int id, std::string firstName, std::string lastName, std::string email, std::string phone) : + id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, email{std::move(email)}, + phone{std::move(phone)} {} +#endif + }; + + struct Product { + int id = 0; + std::string name; + float listPrice = 0; + float discount = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Product() {} + Product(int id, std::string name, float listPrice, float discount) : + id{id}, name{std::move(name)}, listPrice{listPrice}, discount{discount} {} +#endif + }; + + auto storage = make_storage(":memory:", + 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("email", &Contact::email), + make_column("phone", &Contact::phone), + check(length(&Contact::phone) >= 10)), + make_table("products", + make_column("product_id", &Product::id, primary_key()), + make_column("product_name", &Product::name), + make_column("list_price", &Product::listPrice), + make_column("discount", &Product::discount, default_value(0)), + check(c(&Product::listPrice) >= &Product::discount and + c(&Product::discount) >= 0 and c(&Product::listPrice) >= 0))); + storage.sync_schema(); + + try { + storage.insert(Contact{0, "John", "Doe", {}, "408123456"}); + } 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) { + cout << e.what() << endl; + } + + try { + storage.insert(Product{0, "New XFactor", 1000, -10}); + } catch(const std::system_error& e) { + cout << e.what() << endl; + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/chrono_binding.cpp b/libs/sqlite_orm-1.8.2/examples/chrono_binding.cpp new file mode 100644 index 0000000..5400f48 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/chrono_binding.cpp @@ -0,0 +1,171 @@ +#include +#if __cpp_lib_chrono >= 201907L && __cpp_lib_format >= 201907L +#define ENABLE_THIS_EXAMPLE +#endif + +#ifdef ENABLE_THIS_EXAMPLE +#include +#include +#include +#include +#include + +/////////////////////////////// +/// sys_days binding as TEXT +/// /////////////////////////// + +#include +#include + +/** + * This is the date we want to map to our sqlite db. + * Let's make it `TEXT` + */ + +// also we need transform functions to make string from enum.. +static std::string sysDaysToString(std::chrono::sys_days pt) { + auto r = std::format("{:%F}", pt); + return r; +} + +/** + * and sys_days from string. This function has nullable result cause + * string can be an invalid sys_days. + * Of course we won't allow this to happen but as developers we must have + * a scenario for this case. + * These functions are just helpers. They will be called from several places + * that's why I placed it separatedly. You can use any transformation type/form + * (for example BETTER_ENUM https://github.com/aantron/better-enums) + */ +static std::optional sysDaysFromString(const std::string& s) { + using namespace std::literals; + using namespace std::chrono; + + std::stringstream ss{s}; + std::chrono::sys_days tt; + ss >> std::chrono::parse("%F"s, tt); + if(!ss.fail()) { + return {tt}; + } + return std::nullopt; +} + +/** + * This is where magic happens. To tell sqlite_orm how to act + * with SysDays we have to create a few service classes + * specializations (traits) in sqlite_orm namespace. + */ +namespace sqlite_orm { + + /** + * First of all is a type_printer template class. + * It is responsible for sqlite type string representation. + * We want SysDays to be `TEXT` so let's just derive from + * text_printer. Also there are other printers: real_printer and + * integer_printer. We must use them if we want to map our type to `REAL` (double/float) + * or `INTEGER` (int/long/short etc) respectively. + */ + template<> + struct type_printer : public text_printer {}; + + /** + * This is a binder class. It is used to bind c++ values to sqlite queries. + * Here we have to create sysday string representation and bind it as string. + * Any statement_binder specialization must have `int bind(sqlite3_stmt*, int, const T&)` function + * which returns bind result. Also you can call any of `sqlite3_bind_*` functions directly. + * More here https://www.sqlite.org/c3ref/bind_blob.html + */ + template<> + struct statement_binder { + + int bind(sqlite3_stmt* stmt, int index, const std::chrono::sys_days& value) const { + return statement_binder().bind(stmt, index, sysDaysToString(value)); + } + }; + + /** + * field_printer is used in `dump` and `where` functions. Here we have to create + * a string from mapped object. + */ + template<> + struct field_printer { + std::string operator()(const std::chrono::sys_days& t) const { + return sysDaysToString(t); + } + }; + + /** + * This is a reverse operation: here we have to specify a way to transform string received from + * database to our sysdays object. Here we call `sysDaysFromString` and throw `std::runtime_error` if it returns null. + * Every `row_extractor` specialization must have `extract(const char*)`, `extract(sqlite3_stmt *stmt, int columnIndex)` + * and `extract(sqlite3_value* value)` + * functions which return a mapped type value. + */ + template<> + struct row_extractor { + std::chrono::sys_days extract(const char* row_value) const { + if(row_value) { + auto sd = sysDaysFromString(row_value); + if(sd) { + return sd.value(); + } else { + throw std::runtime_error("incorrect date string (" + std::string(row_value) + ")"); + } + } else { + // ! row_value + throw std::runtime_error("incorrect date string (nullptr)"); + } + } + + std::chrono::sys_days extract(sqlite3_stmt* stmt, int columnIndex) const { + auto str = sqlite3_column_text(stmt, columnIndex); + return this->extract((const char*)str); + } + std::chrono::sys_days extract(sqlite3_value* row_value) const { + auto characters = (const char*)(sqlite3_value_text(row_value)); + return extract(characters); + } + }; +} + +//////////////////////////////// +/// end sys_days binding as TEXT +//////////////////////////////// + +using namespace sqlite_orm; +using std::cout; +using std::endl; + +struct Person { + int id; + std::string name; + std::chrono::sys_days birthdate; +}; +#endif + +int main(int, char**) { +#ifdef ENABLE_THIS_EXAMPLE + const std::string db_name = "sys_days.sqlite"; + ::remove(db_name.c_str()); + + auto storage = make_storage(db_name, + make_table("Persons", + make_column("id", &Person::id, primary_key().autoincrement()), + make_column("name", &Person::name), + make_column("birthdate", &Person::birthdate))); + + using namespace std::chrono; + + year_month_day birthdate{year{1960}, month{7}, day{26}}; + + storage.sync_schema(); + + Person person{1, "Juan Dent", birthdate}; // we are using the implicit operator from year_month_day to sys_days + storage.replace(person); + + auto pers = storage.get(1); + year_month_day ymd = pers.birthdate; // using the implicit operator from sys_days to year_month_day + assert(ymd == birthdate); +#endif + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/collate.cpp b/libs/sqlite_orm-1.8.2/examples/collate.cpp new file mode 100644 index 0000000..4267900 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/collate.cpp @@ -0,0 +1,65 @@ +#include + +#include +#include + +#include + +using std::cout; +using std::endl; + +struct User { + int id; + std::string name; + time_t createdAt; +}; + +struct Foo { + std::string text; + int baz; +}; + +int main(int, char**) { + + using namespace sqlite_orm; + auto storage = make_storage( + "collate.sqlite", + make_table("users", + make_column("id", &User::id, primary_key()), + make_column("name", &User::name), + make_column("created_at", &User::createdAt)), + make_table("foo", make_column("text", &Foo::text, collate_nocase()), make_column("baz", &Foo::baz))); + storage.sync_schema(); + storage.remove_all(); + storage.remove_all(); + + storage.insert(User{0, "Lil Kim", std::time(nullptr)}); + storage.insert(User{0, "lil kim", std::time(nullptr)}); + storage.insert(User{0, "Nicki Minaj", std::time(nullptr)}); + + // SELECT COUNT(*) + // FROM users + // WHERE name = 'lil kim' + auto preciseLilKimsCount = storage.count(where(is_equal(&User::name, "lil kim"))); + cout << "preciseLilKimsCount = " << preciseLilKimsCount << endl; + + // SELECT COUNT(*) FROM users WHERE name = 'lil kim' COLLATE NOCASE + auto nocaseCount = storage.count(where(is_equal(&User::name, "lil kim").collate_nocase())); + cout << "nocaseCount = " << nocaseCount << endl; + + // SELECT COUNT(*) FROM users + cout << "total users count = " << storage.count() << endl; + + storage.insert(Foo{"Touch", 10}); + storage.insert(Foo{"touch", 20}); + + cout << "foo count = " << storage.count(where(c(&Foo::text) == "touch")) << endl; + + // SELECT id + // FROM users + // ORDER BY name COLLATE RTRIM ASC + auto rows = storage.select(&User::id, order_by(&User::name).collate_rtrim().asc()); + cout << "rows count = " << rows.size() << endl; + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/column_aliases.cpp b/libs/sqlite_orm-1.8.2/examples/column_aliases.cpp new file mode 100644 index 0000000..8333541 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/column_aliases.cpp @@ -0,0 +1,81 @@ +#include +#include +#include + +using std::cout; +using std::endl; +using std::system_error; +using namespace sqlite_orm; + +void marvel_hero_ordered_by_o_pos() { + struct MarvelHero { + int id = 0; + std::string name; + std::string abilities; + short points = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + MarvelHero() {} + MarvelHero(int id, std::string name, std::string abilities, short points) : + id{id}, name{std::move(name)}, abilities{std::move(abilities)}, points{points} {} +#endif + }; + + auto storage = make_storage("", + make_table("marvel", + make_column("id", &MarvelHero::id, primary_key()), + make_column("name", &MarvelHero::name), + make_column("abilities", &MarvelHero::abilities), + make_column("points", &MarvelHero::points))); + storage.sync_schema(); + + // insert values + storage.transaction([&storage] { + storage.insert(MarvelHero{-1, "Tony Stark", "Iron man, playboy, billionaire, philanthropist", 5}); + storage.insert(MarvelHero{-1, "Thor", "Storm god", -10}); + storage.insert(MarvelHero{-1, "Vision", "Min Stone", 4}); + storage.insert(MarvelHero{-1, "Captain America", "Vibranium shield", -3}); + storage.insert(MarvelHero{-1, "Hulk", "Strength", -15}); + storage.insert(MarvelHero{-1, "Star Lord", "Humor", 19}); + storage.insert(MarvelHero{-1, "Peter Parker", "Spiderman", 16}); + 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}); + + return true; + }); + + { + // SELECT name, instr(abilities, 'o') i + // FROM marvel + // WHERE i > 0 + // ORDER BY i + auto rows = storage.select(columns(&MarvelHero::name, as(instr(&MarvelHero::abilities, "o"))), + where(greater_than(get(), 0)), + order_by(get())); + for(auto& row: rows) { + cout << get<0>(row) << '\t' << get<1>(row) << '\n'; + } + } + cout << endl; + { + // SELECT name, instr(abilities, 'o') + // FROM marvel + // ORDER BY 2 + auto rows = storage.select(columns(&MarvelHero::name, instr(&MarvelHero::abilities, "o")), order_by(2)); + for(auto& row: rows) { + cout << get<0>(row) << '\t' << get<1>(row) << '\n'; + } + } + cout << endl; +} + +int main() { + try { + marvel_hero_ordered_by_o_pos(); + } catch(const system_error& e) { + cout << "[" << e.code() << "] " << e.what(); + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/composite_key.cpp b/libs/sqlite_orm-1.8.2/examples/composite_key.cpp new file mode 100644 index 0000000..e128f0e --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/composite_key.cpp @@ -0,0 +1,63 @@ +/** + * This example shows you how to create a storage with a tablw with a composite primary key + * and another table woth foreign key to first table's primary compisite key. + */ +#include +#include + +#include +#include + +#include + +using std::cout; +using std::endl; + +struct User { + int id; + std::string firstName; + std::string lastName; +}; + +struct UserVisit { + int userId; + std::string userFirstName; + time_t time; +}; + +int main() { + using namespace sqlite_orm; + + auto storage = make_storage( + {}, + 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)), + make_table("visits", + make_column("user_id", &UserVisit::userId), + make_column("user_first_name", &UserVisit::userFirstName), + make_column("time", &UserVisit::time), + foreign_key(&UserVisit::userId, &UserVisit::userFirstName).references(&User::id, &User::firstName))); + storage.sync_schema(); + + storage.replace(User{ + 1, + "Bebe", + "Rexha", + }); + auto bebeRexha = storage.get(1, "Bebe"); + cout << "bebeRexha = " << storage.dump(bebeRexha) << endl; + auto bebeRexhaMaybe = storage.get_pointer(1, "Bebe"); + 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) { + cout << "exception = " << e.what() << endl; + } + storage.replace(User{2, "The Weeknd", "Singer"}); + auto weeknd = storage.get(2, "The Weeknd"); + cout << "weeknd = " << storage.dump(weeknd) << endl; + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/core_functions.cpp b/libs/sqlite_orm-1.8.2/examples/core_functions.cpp new file mode 100644 index 0000000..4f783db --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/core_functions.cpp @@ -0,0 +1,1224 @@ +#include +#include +#include + +using std::cout; +using std::endl; + +struct MarvelHero { + int id = 0; + std::string name; + std::string abilities; + short points = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + MarvelHero() {} + MarvelHero(int id, std::string name, std::string abilities, short points) : + id{id}, name{std::move(name)}, abilities{std::move(abilities)}, points{points} {} +#endif +}; + +struct Contact { + int id = 0; + std::string firstName; + std::string lastName; + std::string phone; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Contact() {} + Contact(int id, std::string firstName, std::string lastName, std::string phone) : + id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, phone{std::move(phone)} {} +#endif +}; + +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; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Customer() {} + Customer(int id, + 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) : + id{id}, + firstName{std::move(firstName)}, lastName{std::move(lastName)}, company{std::move(company)}, + address{std::move(address)}, city{std::move(city)}, state{std::move(state)}, country{std::move(country)}, + postalCode{std::move(postalCode)}, phone{std::move(phone)}, fax{std::move(fax)}, email{std::move(email)}, + supportRepId{supportRepId} {} +#endif +}; + +int main(int, char** argv) { + cout << "path = " << argv[0] << endl; + + using namespace sqlite_orm; + auto storage = make_storage("core_functions.sqlite", + make_table("marvel", + make_column("id", &MarvelHero::id, primary_key()), + make_column("name", &MarvelHero::name), + make_column("abilities", &MarvelHero::abilities), + make_column("points", &MarvelHero::points)), + 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)), + 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(); + + // insert values.. + storage.transaction([&storage] { + storage.insert(MarvelHero{-1, "Tony Stark", "Iron man, playboy, billionaire, philanthropist", 5}); + storage.insert(MarvelHero{-1, "Thor", "Storm god", -10}); + storage.insert(MarvelHero{-1, "Vision", "Min Stone", 4}); + storage.insert(MarvelHero{-1, "Captain America", "Vibranium shield", -3}); + storage.insert(MarvelHero{-1, "Hulk", "Strength", -15}); + storage.insert(MarvelHero{-1, "Star Lord", "Humor", 19}); + storage.insert(MarvelHero{-1, "Peter Parker", "Spiderman", 16}); + 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; + }); + + 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); + + // SELECT LENGTH(name) + // FROM marvel + auto nameLengths = storage.select(length(&MarvelHero::name)); // nameLengths is std::vector + cout << "nameLengths.size = " << nameLengths.size() << endl; + for(auto& len: nameLengths) { + cout << len << " "; + } + cout << endl; + + // SELECT name, LENGTH(name) + // FROM marvel + auto namesWithLengths = storage.select( + columns(&MarvelHero::name, + length(&MarvelHero::name))); // namesWithLengths is std::vector> + cout << "namesWithLengths.size = " << namesWithLengths.size() << endl; + for(auto& row: namesWithLengths) { + cout << "LENGTH(" << std::get<0>(row) << ") = " << std::get<1>(row) << endl; + } + + // SELECT name + // FROM marvel + // WHERE LENGTH(name) > 5 + auto namesWithLengthGreaterThan5 = + storage.select(&MarvelHero::name, where(length(&MarvelHero::name) > 5)); // std::vector + cout << "namesWithLengthGreaterThan5.size = " << namesWithLengthGreaterThan5.size() << endl; + for(auto& name: namesWithLengthGreaterThan5) { + cout << "name = " << name << endl; + } + + // SELECT LENGTH('ototo') + auto custom = storage.select(length("ototo")); + cout << "custom = " << custom.front() << endl; + + // SELECT LENGTH(1990), LENGTH(CURRENT_TIMESTAMP) + auto customTwo = storage.select(columns(length(1990), length(storage.current_timestamp()))); + cout << "customTwo = {" << std::get<0>(customTwo.front()) << ", " << std::get<1>(customTwo.front()) << "}" << endl; + + // SELECT ABS(points) + // FROM marvel + auto absPoints = storage.select(abs(&MarvelHero::points)); // std::vector> + cout << "absPoints: "; + for(auto& value: absPoints) { + if(value) { + cout << *value; + } else { + cout << "null"; + } + cout << " "; + } + cout << endl; + + // SELECT name + // FROM marvel + // 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) { + cout << name << endl; + } + cout << endl; + + // SELECT length(abs(points)) + // FROM marvel + auto twoFunctions = storage.select(length(abs(&MarvelHero::points))); + cout << "twoFunctions.size = " << twoFunctions.size() << endl; + cout << endl; + + // SELECT LOWER(name) + // FROM marvel + auto lowerNames = storage.select(lower(&MarvelHero::name)); + cout << "lowerNames.size = " << lowerNames.size() << endl; + for(auto& name: lowerNames) { + cout << name << endl; + } + cout << endl; + + // SELECT UPPER(abilities) + // FROM marvel + auto upperAbilities = storage.select(upper(&MarvelHero::abilities)); + cout << "upperAbilities.size = " << upperAbilities.size() << endl; + for(auto& abilities: upperAbilities) { + cout << abilities << endl; + } + cout << endl; + + storage.transaction([&] { + storage.remove_all(); + + // SELECT 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; + }); + +#if SQLITE_VERSION_NUMBER >= 3007016 + + // SELECT CHAR(67, 72, 65, 82) + auto charString = storage.select(char_(67, 72, 65, 82)).front(); + cout << "SELECT CHAR(67,72,65,82) = *" << charString << "*" << endl; + + // SELECT LOWER(name) || '@marvel.com' + // FROM marvel + auto emails = storage.select(lower(&MarvelHero::name) || c("@marvel.com")); + cout << "emails.size = " << emails.size() << endl; + for(auto& email: emails) { + cout << email << endl; + } + cout << endl; + +#endif + + // TRIM examples are taken from here https://www.techonthenet.com/sqlite/functions/trim.php + + // SELECT TRIM(' TechOnTheNet.com ') + cout << "trim ' TechOnTheNet.com ' = '" << storage.select(trim(" TechOnTheNet.com ")).front() << "'" + << endl; + + // SELECT TRIM('000123000', '0') + cout << "TRIM('000123000', '0') = " << storage.select(trim("000123000", "0")).front() << endl; + + // SELECT TRIM('zTOTNxyxzyyy', 'xyz') + cout << "SELECT TRIM('zTOTNxyxzyyy', 'xyz') = " << storage.select(trim("zTOTNxyxzyyy", "xyz")).front() << endl; + + // SELECT TRIM('42totn6372', '0123456789') + cout << "TRIM('42totn6372', '0123456789') = " << storage.select(trim("42totn6372", "0123456789")).front() << endl; + + // SELECT RANDOM() + for(auto i = 0; i < 10; ++i) { + cout << "RANDOM() = " << storage.select(sqlite_orm::random()).front() << endl; + } + + // SELECT * FROM marvel ORDER BY RANDOM() + for(auto& hero: storage.iterate(order_by(sqlite_orm::random()))) { + cout << "hero = " << storage.dump(hero) << endl; + } + + // https://www.techonthenet.com/sqlite/functions/ltrim.php + + // SELECT ltrim(' TechOnTheNet.com'); + cout << "ltrim(' TechOnTheNet.com') = *" << storage.select(ltrim(" TechOnTheNet.com")).front() << "*" << endl; + + // SELECT ltrim(' TechOnTheNet.com '); + cout << "ltrim(' TechOnTheNet.com ') = *" << storage.select(ltrim(" TechOnTheNet.com ")).front() << "*" + << endl; + + // SELECT ltrim(' TechOnTheNet.com is great!'); + cout << "ltrim(' TechOnTheNet.com is great!') = *" + << storage.select(ltrim(" TechOnTheNet.com is great!")).front() << "*" << endl; + + { // core functions can be use within prepared statements as well! + + auto lTrimStatement = storage.prepare(select(ltrim("000123", "0"))); + + // SELECT ltrim('000123', '0'); + cout << "ltrim('000123', '0') = " << storage.execute(lTrimStatement).front() << endl; + + // SELECT ltrim('123123totn', '123'); + get<0>(lTrimStatement) = "123123totn"; + get<1>(lTrimStatement) = "123"; + cout << "ltrim('123123totn', '123') = " << storage.execute(lTrimStatement).front() << endl; + + // SELECT ltrim('123123totn123', '123'); + get<0>(lTrimStatement) = "123123totn123"; + get<1>(lTrimStatement) = "123"; + cout << "ltrim('123123totn123', '123') = " << storage.execute(lTrimStatement).front() << endl; + + // SELECT ltrim('xyxzyyyTOTN', 'xyz'); + get<0>(lTrimStatement) = "xyxzyyyTOTN"; + get<1>(lTrimStatement) = "xyz"; + cout << "ltrim('xyxzyyyTOTN', 'xyz') = " << storage.execute(lTrimStatement).front() << endl; + + // SELECT ltrim('6372totn', '0123456789'); + get<0>(lTrimStatement) = "6372totn"; + get<1>(lTrimStatement) = "0123456789"; + cout << "ltrim('6372totn', '0123456789') = " << storage.execute(lTrimStatement).front() << endl; + } + + // https://www.techonthenet.com/sqlite/functions/rtrim.php + + // SELECT rtrim('TechOnTheNet.com '); + cout << "rtrim('TechOnTheNet.com ') = *" << storage.select(rtrim("TechOnTheNet.com ")).front() << "*" << endl; + + // SELECT rtrim(' TechOnTheNet.com '); + cout << "rtrim(' TechOnTheNet.com ') = *" << storage.select(rtrim(" TechOnTheNet.com ")).front() << "*" + << endl; + + // SELECT rtrim('TechOnTheNet.com is great! '); + cout << "rtrim('TechOnTheNet.com is great! ') = *" + << storage.select(rtrim("TechOnTheNet.com is great! ")).front() << "*" << endl; + + // SELECT rtrim('123000', '0'); + cout << "rtrim('123000', '0') = *" << storage.select(rtrim("123000", "0")).front() << "*" << endl; + + // SELECT rtrim('totn123123', '123'); + cout << "rtrim('totn123123', '123') = *" << storage.select(rtrim("totn123123", "123")).front() << "*" << endl; + + // SELECT rtrim('123totn123123', '123'); + cout << "rtrim('123totn123123', '123') = *" << storage.select(rtrim("123totn123123", "123")).front() << "*" << endl; + + // SELECT rtrim('TOTNxyxzyyy', 'xyz'); + cout << "rtrim('TOTNxyxzyyy', 'xyz') = *" << storage.select(rtrim("TOTNxyxzyyy", "xyz")).front() << "*" << endl; + + // SELECT rtrim('totn6372', '0123456789'); + cout << "rtrim('totn6372', '0123456789') = *" << storage.select(rtrim("totn6372", "0123456789")).front() << "*" + << endl; + + // SELECT coalesce(10,20); + cout << "coalesce(10,20) = " << storage.select(coalesce(10, 20)).front() << endl; + + // SELECT substr('SQLite substr', 8); + cout << "substr('SQLite substr', 8) = " << storage.select(substr("SQLite substr", 8)).front() << endl; + + // SELECT substr('SQLite substr', 1, 6); + cout << "substr('SQLite substr', 1, 6) = " << storage.select(substr("SQLite substr", 1, 6)).front() << endl; + + // SELECT hex(67); + cout << "hex(67) = " << storage.select(hex(67)).front() << endl; + + // SELECT quote('hi') + cout << "SELECT quote('hi') = " << storage.select(quote("hi")).front() << endl; + + // SELECT hex(randomblob(10)) + cout << "SELECT hex(randomblob(10)) = " << storage.select(hex(randomblob(10))).front() << endl; + + // SELECT instr('something about it', 't') + cout << "SELECT instr('something about it', 't') = " << storage.select(instr("something about it", "t")).front() + << endl; + + { + cout << endl; + struct o_pos : alias_tag { + static const std::string& get() { + static const std::string res = "o_pos"; + return res; + } + }; + + // SELECT name, instr(abilities, 'o') o_pos + // FROM marvel + // WHERE o_pos > 0 + // ORDER BY o_pos + auto rows = storage.select(columns(&MarvelHero::name, as(instr(&MarvelHero::abilities, "o"))), + where(greater_than(get(), 0)), + order_by(get())); + for(auto& row: rows) { + cout << get<0>(row) << '\t' << get<1>(row) << endl; + } + cout << endl; + } + + // SELECT replace('AA B CC AAA','A','Z') + cout << "SELECT replace('AA B CC AAA','A','Z') = " << storage.select(replace("AA B CC AAA", "A", "Z")).front() + << endl; + + // SELECT replace('This is a cat','This','That') + cout << "SELECT replace('This is a cat','This','That') = " + << storage.select(replace("This is a cat", "This", "That")).front() << endl; + + // UPDATE contacts + // 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()) { + cout << storage.dump(contact) << endl; + } + + // SELECT round(1929.236, 2) + cout << "SELECT round(1929.236, 2) = " << storage.select(round(1929.236, 2)).front() << endl; + + // SELECT round(1929.236, 1) + cout << "SELECT round(1929.236, 1) = " << storage.select(round(1929.236, 1)).front() << endl; + + // SELECT round(1929.236) + cout << "SELECT round(1929.236) = " << storage.select(round(1929.236)).front() << endl; + + // SELECT round(0.5) + cout << "SELECT round(0.5) = " << storage.select(round(0.5)).front() << endl; +#ifdef SQLITE_SOUNDEX + // 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; + } + + storage.update_all( + set(c(&Contact::phone) = select(&Customer::phone, from(), where(c(&Customer::id) == 1)))); + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/cross_join.cpp b/libs/sqlite_orm-1.8.2/examples/cross_join.cpp new file mode 100644 index 0000000..28b2853 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/cross_join.cpp @@ -0,0 +1,86 @@ +/** + * The example is implemented from here http://www.sqlitetutorial.net/sqlite-cross-join/ + */ + +#include +#include +#include + +using std::cout; +using std::endl; + +namespace DataModel { + + struct Rank { + std::string rank; + }; + + struct Suit { + std::string suit; + }; +} + +static auto initStorage(const std::string& path) { + using namespace DataModel; + using namespace sqlite_orm; + return make_storage(path, + make_table("ranks", make_column("rank", &Rank::rank)), + make_table("suits", make_column("suit", &Suit::suit))); +} + +int main(int, char**) { + using namespace DataModel; + + using Storage = decltype(initStorage("")); + + Storage storage = initStorage("cross_join.sqlite"); + + // sync schema in case db/tables do not exist + storage.sync_schema(); + + // remove old data if something left from after the last time + storage.remove_all(); + storage.remove_all(); + + storage.insert(Rank{"2"}); + storage.insert(Rank{"3"}); + storage.insert(Rank{"4"}); + storage.insert(Rank{"5"}); + storage.insert(Rank{"6"}); + storage.insert(Rank{"7"}); + storage.insert(Rank{"8"}); + storage.insert(Rank{"9"}); + storage.insert(Rank{"10"}); + storage.insert(Rank{"J"}); + storage.insert(Rank{"Q"}); + storage.insert(Rank{"K"}); + storage.insert(Rank{"A"}); + + storage.insert(Suit{"Clubs"}); + storage.insert(Suit{"Diamonds"}); + storage.insert(Suit{"Hearts"}); + storage.insert(Suit{"Spades"}); + + using namespace sqlite_orm; + + /** + * When you pass columns(...) with members sqlite_orm gathers all types and creates + * a std::set with respective table names. Once you passed `cross_join` as an argument + * after columns T's table name is removed from table names set. + */ + // SELECT rank, suit + // FROM ranks + // CROSS JOIN suits + // ORDER BY suit; + auto cards = storage.select(columns(&Rank::rank, &Suit::suit), + cross_join(), + order_by(&Suit::suit)); // cards is vector> + + cout << "cards count = " << cards.size() << endl; + for(auto card: cards) { + cout << std::get<0>(card) << '\t' << std::get<1>(card) << endl; + } + cout << endl; + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/custom_aliases.cpp b/libs/sqlite_orm-1.8.2/examples/custom_aliases.cpp new file mode 100644 index 0000000..0d6ffa7 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/custom_aliases.cpp @@ -0,0 +1,139 @@ +/** + * Example is taken from here https://www.tutorialspoint.com/sqlite/sqlite_alias_syntax.htm + */ + +#include + +#include +#include +#include + +using std::cout; +using std::endl; + +struct Employee { + int id = 0; + std::string name; + int age = 0; + std::string address; + float salary = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Employee() {} + Employee(int id, std::string name, int age, std::string address, float salary) : + id{id}, name{std::move(name)}, age{age}, address{std::move(address)}, salary{salary} {} +#endif +}; + +struct Department { + int id = 0; + std::string dept; + int empId = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Department() {} + Department(int id, std::string dept, int empId) : id{id}, dept{std::move(dept)}, empId{empId} {} +#endif +}; + +using namespace sqlite_orm; + +struct EmployeeIdAlias : alias_tag { + 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 res = "COMPANY_NAME"; + return res; + } +}; + +int main(int, char** argv) { + cout << argv[0] << endl; + + auto storage = make_storage("custom_aliases.sqlite", + 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)), + make_table("DEPARTMENT", + make_column("ID", &Department::id), + make_column("DEPT", &Department::dept), + make_column("EMP_ID", &Department::empId))); + storage.sync_schema(); + storage.remove_all(); + storage.remove_all(); + + // insert initial values + storage.replace(Employee{1, "Paul", 32, "California", 20000.0}); + storage.replace(Employee{2, "Allen", 25, "Texas", 15000.0}); + storage.replace(Employee{3, "Teddy", 23, "Norway", 20000.0}); + storage.replace(Employee{4, "Mark", 25, "Rich-Mond", 65000.0}); + storage.replace(Employee{5, "David", 27, "Texas", 85000.0}); + storage.replace(Employee{6, "Kim", 22, "South-Hall", 45000.0}); + storage.replace(Employee{7, "James", 24, "Houston", 10000.0}); + + storage.replace(Department{1, "IT Billing", 1}); + storage.replace(Department{2, "Engineering", 2}); + storage.replace(Department{3, "Finance", 7}); + storage.replace(Department{4, "Engineering", 3}); + storage.replace(Department{5, "Finance", 4}); + storage.replace(Department{6, "Engineering", 5}); + storage.replace(Department{7, "Finance", 6}); + + // SELECT COMPANY.ID, COMPANY.NAME, COMPANY.AGE, DEPARTMENT.DEPT + // FROM COMPANY, DEPARTMENT + // WHERE COMPANY.ID = DEPARTMENT.EMP_ID; + auto simpleRows = storage.select(columns(&Employee::id, &Employee::name, &Employee::age, &Department::dept), + where(is_equal(&Employee::id, &Department::empId))); + assert(simpleRows.size() == 7); + + cout << "ID" << '\t' << "NAME" << '\t' << "AGE" << '\t' << "DEPT" << endl; + cout << "----------" << '\t' << "----------" << '\t' << "----------" << '\t' << "----------" << endl; + 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; + } + + // SELECT C.ID, C.NAME, C.AGE, D.DEPT + // FROM COMPANY AS C, DEPARTMENT AS D + // WHERE C.ID = D.EMP_ID; + using als_c = alias_c; + using als_d = alias_d; + auto rowsWithTableAliases = + storage.select(columns(alias_column(&Employee::id), + alias_column(&Employee::name), + alias_column(&Employee::age), + alias_column(&Department::dept)), + where(is_equal(alias_column(&Employee::id), alias_column(&Department::empId)))); + assert(rowsWithTableAliases == simpleRows); + + // SELECT COMPANY.ID as COMPANY_ID, COMPANY.NAME AS COMPANY_NAME, COMPANY.AGE, DEPARTMENT.DEPT + // FROM COMPANY, DEPARTMENT + // WHERE COMPANY_ID = DEPARTMENT.EMP_ID; + auto rowsWithColumnAliases = storage.select(columns(as(&Employee::id), + as(&Employee::name), + &Employee::age, + &Department::dept), + where(is_equal(get(), &Department::empId))); + assert(rowsWithColumnAliases == rowsWithTableAliases); + + // SELECT C.ID AS COMPANY_ID, C.NAME AS COMPANY_NAME, C.AGE, D.DEPT + // FROM COMPANY AS C, DEPARTMENT AS D + // WHERE C.ID = D.EMP_ID; + auto rowsWithBothTableAndColumnAliases = + storage.select(columns(as(alias_column(&Employee::id)), + as(alias_column(&Employee::name)), + alias_column(&Employee::age), + alias_column(&Department::dept)), + where(is_equal(alias_column(&Employee::id), alias_column(&Department::empId)))); + assert(rowsWithBothTableAndColumnAliases == rowsWithBothTableAndColumnAliases); + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/date_time.cpp b/libs/sqlite_orm-1.8.2/examples/date_time.cpp new file mode 100644 index 0000000..63ca31c --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/date_time.cpp @@ -0,0 +1,97 @@ + +#include + +#include +#include + +using std::cout; +using std::endl; + +struct DatetimeText { + std::string d1; + std::string d2; +}; + +int main() { + using namespace sqlite_orm; + + auto storage = make_storage( + "", + make_table("datetime_text", make_column("d1", &DatetimeText::d1), make_column("d2", &DatetimeText::d2))); + storage.sync_schema(); + + // SELECT date('now'); + auto now = storage.select(date("now")).front(); + cout << "date('now') = " << now << endl; + + // SELECT date('now','start of month','+1 month','-1 day') + auto nowWithOffset = storage.select(date("now", "start of month", "+1 month", "-1 day")).front(); + cout << "SELECT date('now','start of month','+1 month','-1 day') = " << nowWithOffset << endl; + + // SELECT DATETIME('now') + auto nowTime = storage.select(datetime("now")).front(); + cout << "DATETIME('now') = " << nowTime << endl; + + // SELECT DATETIME('now','localtime') + auto localTime = storage.select(datetime("now", "localtime")).front(); + cout << "DATETIME('now','localtime') = " << localTime << endl; + + // INSERT INTO datetime_text (d1, d2) + // VALUES + // ( + // datetime('now'), + // datetime('now', 'localtime') + // ); + DatetimeText datetimeText{ + storage.select(datetime("now")).front(), + storage.select(datetime("now", "localtime")).front(), + }; + storage.insert(datetimeText); + + cout << "SELECT * FROM datetime_text = " << storage.dump(storage.get_all().front()) << endl; + + auto nowWithOffset2 = storage.select(date("now", "+2 day")).front(); + cout << "SELECT date('now','+2 day') = " << nowWithOffset2 << endl; + + // SELECT julianday('now') + auto juliandayNow = storage.select(julianday("now")).front(); + cout << "SELECT julianday('now') = " << juliandayNow << endl; + + // SELECT julianday('1776-07-04') + auto oldJulianday = storage.select(julianday("1776-07-04")).front(); + cout << "SELECT julianday('1776-07-04') = " << oldJulianday << endl; + + // SELECT julianday('now') + julianday('1776-07-04') + auto julianSum = storage.select(julianday("now") + julianday("1776-07-04")).front(); + cout << "SELECT julianday('now') + julianday('1776-07-04') = " << julianSum << endl; + + // SELECT julianday('now') - julianday('1776-07-04') + auto julianDiff = storage.select(julianday("now") - julianday("1776-07-04")).front(); + cout << "SELECT julianday('now') - julianday('1776-07-04') = " << julianDiff << endl; + + // SELECT (julianday('now') - 2440587.5) * 86400.0 + auto julianConverted = storage.select((julianday("now") - 2440587.5) * 86400.0).front(); + cout << "SELECT (julianday('now') - 2440587.5) * 86400.0 = " << julianConverted << endl; + + // SELECT time('12:00', 'localtime') + auto time12Local = storage.select(time("12:00", "localtime")).front(); + cout << "SELECT time('12:00', 'localtime') = " << time12Local << endl; + + // SELECT time('12:00', 'utc') + auto time12utc = storage.select(time("12:00", "utc")).front(); + cout << "SELECT time('12:00', 'utc') = " << time12utc << endl; + + // SELECT strftime('%Y %m %d','now') + auto strftimeRes = storage.select(strftime("%Y %m %d", "now")).front(); + cout << "SELECT strftime('%Y %m %d','now') = " << strftimeRes << endl; + + // SELECT strftime('%H %M %S %s','now') + auto strftimeRes2 = storage.select(strftime("%H %M %S %s", "now")).front(); + cout << "SELECT strftime('%H %M %S %s','now') = " << strftimeRes2 << endl; + + // SELECT strftime('%s','now') - strftime('%s','2014-10-07 02:34:56') + auto strftimeResSub = storage.select(strftime("%s", "now") - strftime("%s", "2014-10-07 02:34:56")).front(); + cout << "SELECT strftime('%s','now') - strftime('%s','2014-10-07 02:34:56') = " << strftimeResSub << endl; + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/distinct.cpp b/libs/sqlite_orm-1.8.2/examples/distinct.cpp new file mode 100644 index 0000000..3848e1a --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/distinct.cpp @@ -0,0 +1,143 @@ +// +// This example is implemented from here https://www.tutorialspoint.com/sqlite/sqlite_distinct_keyword.htm +// + +#include +#include + +using std::cout; +using std::endl; + +struct Employee { + int id; + std::string name; + int age; + std::string address; + double salary; +}; + +auto initStorage(const std::string& path) { + using namespace sqlite_orm; + return make_storage(path, + 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))); +} +using Storage = decltype(initStorage("")); + +int main(int, char**) { + + Storage storage = initStorage("distinct.sqlite"); + + // sync schema in case this is a very first launch + storage.sync_schema(); + + // remove all employees if it is not the very first launch + storage.remove_all(); + + // now let's insert start values to wirk with. We'll make it within a transaction + // for better perfomance + storage.begin_transaction(); + storage.insert(Employee{ + -1, + "Paul", + 32, + "California", + 20000.0, + }); + storage.insert(Employee{ + -1, + "Allen", + 25, + "Texas", + 15000.0, + }); + storage.insert(Employee{ + -1, + "Teddy", + 23, + "Norway", + 20000.0, + }); + storage.insert(Employee{ + -1, + "Mark", + 25, + "Rich-Mond", + 65000.0, + }); + storage.insert(Employee{ + -1, + "David", + 27, + "Texas", + 85000.0, + }); + storage.insert(Employee{ + -1, + "Kim", + 22, + "South-Hall", + 45000.0, + }); + storage.insert(Employee{ + -1, + "James", + 24, + "Houston", + 10000.0, + }); + 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, + }); + storage.commit(); + + // SELECT 'NAME' FROM 'COMPANY' + auto pureNames = storage.select(&Employee::name); + cout << "NAME" << endl; + cout << "----------" << endl; + for(auto& name: pureNames) { + cout << name << endl; + } + cout << endl; + + using namespace sqlite_orm; + // SELECT DISTINCT 'NAME' FROM 'COMPANY' + auto distinctNames = storage.select(distinct(&Employee::name)); + cout << "NAME" << endl; + cout << "----------" << endl; + for(auto& name: distinctNames) { + cout << name << endl; + } + cout << endl; + + // SELECT DISTINCT 'ADDRESS', 'NAME' FROM 'COMPANY' + auto severalColumns = storage.select(distinct(columns(&Employee::address, &Employee::name))); + cout << "ADDRESS" << '\t' << "NAME" << endl; + cout << "----------" << endl; + for(auto& t: severalColumns) { + cout << std::get<0>(t) << '\t' << std::get<1>(t) << endl; + } + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/enum_binding.cpp b/libs/sqlite_orm-1.8.2/examples/enum_binding.cpp new file mode 100644 index 0000000..e8164f7 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/enum_binding.cpp @@ -0,0 +1,177 @@ + +#include + +#include +#include +#include + +using std::cout; +using std::endl; + +/** + * This is a enum we want to map to our sqlite db. + * First we have to decide what db type enum must have. + * Let's make it `TEXT`: Gender::Male will store as 'male' string + * and Gender::Female as 'female' string. + */ +enum class Gender { + Male, + Female, +}; + +// This is a class with field `gender` with type `Gender`. +struct SuperHero { + int id; + std::string name; + Gender gender; +}; + +// also we need transform functions to make string from enum.. +std::string GenderToString(Gender gender) { + switch(gender) { + case Gender::Female: + return "female"; + case Gender::Male: + return "male"; + } + throw std::domain_error("Invalid Gender enum"); +} + +/** + * and enum from string. This function has nullable result cause + * string can be neither `male` nor `female`. Of course we + * won't allow this to happed but as developers we must have + * a scenario for this case. + * These functions are just helpers. They will be called from several places + * 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) { + if(s == "female") { + return std::make_unique(Gender::Female); + } else if(s == "male") { + return std::make_unique(Gender::Male); + } + return nullptr; +} + +/** + * This is where magic happens. To tell sqlite_orm how to act + * with Gender enum we have to create a few service classes + * specializations (traits) in sqlite_orm namespace. + */ +namespace sqlite_orm { + + /** + * First of all is a type_printer template class. + * It is responsible for sqlite type string representation. + * We want Gender to be `TEXT` so let's just derive from + * text_printer. Also there are other printers: real_printer and + * integer_printer. We must use them if we want to map our type to `REAL` (double/float) + * or `INTEGER` (int/long/short etc) respectively. + */ + template<> + struct type_printer : public text_printer {}; + + /** + * This is a binder class. It is used to bind c++ values to sqlite queries. + * Here we have to create gender string representation and bind it as string. + * Any statement_binder specialization must have `int bind(sqlite3_stmt*, int, const T&)` function + * which returns bind result. Also you can call any of `sqlite3_bind_*` functions directly. + * More here https://www.sqlite.org/c3ref/bind_blob.html + */ + template<> + struct statement_binder { + + 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); + } + }; + + /** + * field_printer is used in `dump` and `where` functions. Here we have to create + * a string from mapped object. + */ + template<> + struct field_printer { + std::string operator()(const Gender& t) const { + return GenderToString(t); + } + }; + + /** + * This is a reverse operation: here we have to specify a way to transform string received from + * database to our Gender object. Here we call `GenderFromString` and throw `std::runtime_error` if it returns + * nullptr. Every `row_extractor` specialization must have `extract(const char*)` and `extract(sqlite3_stmt *stmt, + * int columnIndex)` functions which return a mapped type value. + */ + template<> + struct row_extractor { + Gender extract(const char* row_value) { + if(auto gender = GenderFromString(row_value)) { + return *gender; + } else { + throw std::runtime_error("incorrect gender string (" + std::string(row_value) + ")"); + } + } + + Gender extract(sqlite3_stmt* stmt, int columnIndex) { + auto str = sqlite3_column_text(stmt, columnIndex); + return this->extract((const char*)str); + } + }; +} + +int main(int /* argc*/, char** /*argv*/) { + using namespace sqlite_orm; + auto storage = make_storage("", + make_table("superheros", + make_column("id", &SuperHero::id, primary_key()), + make_column("name", &SuperHero::name), + make_column("gender", &SuperHero::gender))); + storage.sync_schema(); + storage.remove_all(); + + // insert Batman (male) + storage.insert(SuperHero{-1, "Batman", Gender::Male}); + + // get Batman by name + auto batman = storage.get_all(where(c(&SuperHero::name) == "Batman")).front(); + + // print Batman + cout << "batman = " << storage.dump(batman) << endl; + + // insert Wonder woman + storage.insert(SuperHero{-1, "Wonder woman", Gender::Female}); + + // get all superheros + auto allSuperHeros = storage.get_all(); + + // print all superheros + cout << "allSuperHeros = " << allSuperHeros.size() << endl; + for(auto& superHero: allSuperHeros) { + cout << storage.dump(superHero) << endl; + } + + // insert a second male (Superman) + storage.insert(SuperHero{-1, "Superman", Gender::Male}); + + // get all male superheros (2 expected) + auto males = storage.get_all(where(c(&SuperHero::gender) == Gender::Male)); + cout << "males = " << males.size() << endl; + assert(males.size() == 2); + for(auto& superHero: males) { + cout << storage.dump(superHero) << endl; + } + + // get all female superheros (1 expected) + auto females = storage.get_all(where(c(&SuperHero::gender) == Gender::Female)); + assert(females.size() == 1); + cout << "females = " << females.size() << endl; + for(auto& superHero: females) { + cout << storage.dump(superHero) << endl; + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/except_intersection.cpp b/libs/sqlite_orm-1.8.2/examples/except_intersection.cpp new file mode 100644 index 0000000..97a7629 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/except_intersection.cpp @@ -0,0 +1,100 @@ +/** + * Example is implemented from here https://www.tutlane.com/tutorial/sqlite/sqlite-except-operator + */ +#include +#include +#include + +using std::cout; +using std::endl; + +struct DeptMaster { + int deptId = 0; + std::string deptName; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + DeptMaster() = default; + DeptMaster(int deptId, std::string deptName) : deptId{deptId}, deptName{std::move(deptName)} {} +#endif +}; + +struct EmpMaster { + int empId = 0; + std::string firstName; + std::string lastName; + long salary; + decltype(DeptMaster::deptId) deptId; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + EmpMaster() = default; + EmpMaster(int empId, + std::string firstName, + std::string lastName, + long salary, + decltype(DeptMaster::deptId) deptId) : + empId{empId}, + firstName{std::move(firstName)}, lastName{std::move(lastName)}, salary{salary}, deptId{deptId} {} +#endif +}; + +int main() { + using namespace sqlite_orm; + + auto storage = make_storage("", + make_table("dept_master", + make_column("dept_id", &DeptMaster::deptId, primary_key().autoincrement()), + make_column("dept_name", &DeptMaster::deptName)), + make_table("emp_master", + make_column("emp_id", &EmpMaster::empId, primary_key().autoincrement()), + make_column("first_name", &EmpMaster::firstName), + make_column("last_name", &EmpMaster::lastName), + make_column("salary", &EmpMaster::salary), + make_column("dept_id", &EmpMaster::deptId))); + storage.sync_schema(); + storage.remove_all(); + storage.remove_all(); + { + auto valuesToInsert = { + DeptMaster{0, "Admin"}, + DeptMaster{0, "Sales"}, + DeptMaster{0, "Quality Control"}, + DeptMaster{0, "Marketing"}, + }; + storage.insert_range(valuesToInsert.begin(), valuesToInsert.end()); + } + { + auto valuesToInsert = { + EmpMaster{1, "Honey", "Patel", 10100, 1}, + EmpMaster{2, "Shweta", "Jariwala", 19300, 2}, + EmpMaster{3, "Vinay", "Jariwala", 35100, 3}, + EmpMaster{4, "Jagruti", "Viras", 9500, 12}, + }; + storage.replace_range(valuesToInsert.begin(), valuesToInsert.end()); + } + { + // SELECT dept_id + // FROM dept_master + // EXCEPT + // SELECT dept_id + // FROM emp_master + auto rows = storage.select(except(select(&DeptMaster::deptId), select(&EmpMaster::deptId))); + cout << "rows count = " << rows.size() << endl; + for(auto id: rows) { + cout << id << endl; + } + } + { + // SELECT dept_id + // FROM dept_master + // INTERSECT + // SELECT dept_id + // FROM emp_master + auto rows = storage.select(intersect(select(&DeptMaster::deptId), select(&EmpMaster::deptId))); + cout << "rows count = " << rows.size() << endl; + for(auto id: rows) { + cout << id << endl; + } + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/exists.cpp b/libs/sqlite_orm-1.8.2/examples/exists.cpp new file mode 100644 index 0000000..ab5a888 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/exists.cpp @@ -0,0 +1,510 @@ +/** + * Example is implemented from here https://www.w3resource.com/sqlite/exists-operator.php + */ +#include +#include +#include + +using std::cout; +using std::endl; + +struct Customer { + std::string code; + std::string name; + std::string city; + std::string workingArea; + std::string country; + int grade; + double openingAmt; + double receiveAmt; + double paymentAmt; + double outstandingAmt; + std::string phoneNo; + std::string agentCode; +}; + +struct Agent { + std::string code; + std::string name; + std::string workingArea; + double comission; + std::string phoneNo; + std::string country; +}; + +struct Order { + std::string num; + int amount; + int advanceAmount; + std::string date; + std::string custCode; + std::string agentCode; +}; + +int main(int, char**) { + using namespace sqlite_orm; + + auto storage = make_storage("exists.sqlite", + make_table("customer", + make_column("CUST_CODE", &Customer::code, primary_key()), + make_column("CUST_NAME", &Customer::name), + make_column("CUST_CITY", &Customer::city), + make_column("WORKING_AREA", &Customer::workingArea), + make_column("CUST_COUNTRY", &Customer::country), + make_column("GRADE", &Customer::grade), + make_column("OPENING_AMT", &Customer::openingAmt), + make_column("RECEIVE_AMT", &Customer::receiveAmt), + make_column("PAYMENT_AMT", &Customer::paymentAmt), + make_column("OUTSTANDING_AMT", &Customer::outstandingAmt), + make_column("PHONE_NO", &Customer::phoneNo), + make_column("AGENT_CODE", &Customer::agentCode)), + make_table("agents", + make_column("AGENT_CODE", &Agent::code, primary_key()), + make_column("AGENT_NAME", &Agent::name), + make_column("WORKING_AREA", &Agent::workingArea), + make_column("COMMISSION", &Agent::comission), + make_column("PHONE_NO", &Agent::phoneNo), + make_column("COUNTRY", &Agent::country)), + make_table("orders", + make_column("ORD_NUM", &Order::num, primary_key()), + make_column("ORD_AMOUNT", &Order::amount), + make_column("ADVANCE_AMOUNT", &Order::advanceAmount), + make_column("ORD_DATE", &Order::date), + make_column("CUST_CODE", &Order::custCode), + make_column("AGENT_CODE", &Order::agentCode))); + storage.sync_schema(); + storage.remove_all(); + storage.remove_all(); + storage.remove_all(); + + storage.replace(Agent{"A007", "Ramasundar", "Bangalore", 0.15, "077-25814763", ""}); + storage.replace(Agent{"A003", "Alex", "London", 0.13, "075-12458969", ""}); + storage.replace(Agent{"A008", "Alford", "New York", 0.12, "044-25874365", ""}); + storage.replace(Agent{"A011", "Ravi Kumar", "Bangalore", 0.15, "077-45625874", ""}); + storage.replace(Agent{"A010", "Santakumar", "Chennai", 0.14, "007-22388644", ""}); + storage.replace(Agent{"A012", "Lucida", "San Jose", 0.12, "044-52981425", ""}); + storage.replace(Agent{"A005", "Anderson", "Brisban", 0.13, "045-21447739", ""}); + storage.replace(Agent{"A001", "Subbarao", "Bangalore", 0.14, "077-12346674", ""}); + storage.replace(Agent{"A002", "Mukesh", "Mumbai", 0.11, "029-12358964", ""}); + storage.replace(Agent{"A006", "McDen", "London", 0.15, "078-22255588", ""}); + storage.replace(Agent{"A004", "Ivan", "Torento", 0.15, "008-22544166", ""}); + storage.replace(Agent{"A009", "Benjamin", "Hampshair", 0.11, "008-22536178", ""}); + + storage.replace(Customer{"C00013", + "Holmes", + "London", + "London", + "UK", + 2, + 6000.00, + 5000.00, + 7000.00, + 4000.00, + "BBBBBBB", + "A003"}); + storage.replace(Customer{"C00001", + "Micheal", + "New York", + "New York", + "USA", + 2, + 3000.00, + 5000.00, + 2000.00, + 6000.00, + "CCCCCCC", + "A008"}); + storage.replace(Customer{"C00020", + "Albert", + "New York", + "New York", + "USA", + 3, + 5000.00, + 7000.00, + 6000.00, + 6000.00, + "BBBBSBB", + "A008"}); + storage.replace(Customer{"C00025", + "Ravindran", + "Bangalore", + "Bangalore", + "India", + 2, + 5000.00, + 7000.00, + 4000.00, + 8000.00, + "AVAVAVA", + "A011"}); + storage.replace( + Customer{"C00024", "Cook", "London", "London", "UK", 2, 4000.00, 9000.00, 7000.00, 6000.00, "FSDDSDF", "A006"}); + storage.replace(Customer{"C00015", + "Stuart", + "London", + "London", + "UK", + 1, + 6000.00, + 8000.00, + 3000.00, + 11000.00, + "GFSGERS", + "A003"}); + storage.replace(Customer{"C00002", + "Bolt", + "New York", + "New York", + "USA", + 3, + 5000.00, + 7000.00, + 9000.00, + 3000.00, + "DDNRDRH", + "A008"}); + storage.replace(Customer{"C00018", + "Fleming", + "Brisban", + "Brisban", + "Australia", + 2, + 7000.00, + 7000.00, + 9000.00, + 5000.00, + "NHBGVFC", + "A005"}); + storage.replace(Customer{"C00021", + "Jacks", + "Brisban", + "Brisban", + "Australia", + 1, + 7000.00, + 7000.00, + 7000.00, + 7000.00, + "WERTGDF", + "A005"}); + storage.replace(Customer{"C00019", + "Yearannaidu", + "Chennai", + "Chennai", + "India", + 1, + 8000.00, + 7000.00, + 7000.00, + 8000.00, + "ZZZZBFV", + "A010"}); + storage.replace(Customer{"C00005", + "Sasikant", + "Mumbai", + "Mumbai", + "India", + 1, + 7000.00, + 11000.00, + 7000.00, + 11000.00, + "147-25896312", + "A002"}); + storage.replace(Customer{"C00007", + "Ramanathan", + "Chennai", + "Chennai", + "India", + 1, + 7000.00, + 11000.00, + 9000.00, + 9000.00, + "GHRDWSD", + "A010"}); + storage.replace(Customer{"C00022", + "Avinash", + "Mumbai", + "Mumbai", + "India", + 2, + 7000.00, + 11000.00, + 9000.00, + 9000.00, + "113-12345678", + "A002"}); + storage.replace(Customer{"C00004", + "Winston", + "Brisban", + "Brisban", + "Australia", + 1, + 5000.00, + 8000.00, + 7000.00, + 6000.00, + "AAAAAAA", + "A005"}); + storage.replace( + Customer{"C00023", "Karl", "London", "London", "UK", 0, 4000.00, 6000.00, 7000.00, 3000.00, "AAAABAA", "A006"}); + storage.replace(Customer{"C00006", + "Shilton", + "Torento", + "Torento", + "Canada", + 1, + 10000.00, + 7000.00, + 6000.00, + 11000.00, + "DDDDDDD", + "A004"}); + storage.replace(Customer{"C00010", + "Charles", + "Hampshair", + "Hampshair", + "UK", + 3, + 6000.00, + 4000.00, + 5000.00, + 5000.00, + "MMMMMMM", + "A009"}); + storage.replace(Customer{"C00017", + "Srinivas", + "Bangalore", + "Bangalore", + "India", + 2, + 8000.00, + 4000.00, + 3000.00, + 9000.00, + "AAAAAAB", + "A007"}); + storage.replace(Customer{"C00012", + "Steven", + "San Jose", + "San Jose", + "USA", + 1, + 5000.00, + 7000.00, + 9000.00, + 3000.00, + "KRFYGJK", + "A012"}); + storage.replace(Customer{"C00008", + "Karolina", + "Torento", + "Torento", + "Canada", + 1, + 7000.00, + 7000.00, + 9000.00, + 5000.00, + "HJKORED", + "A004"}); + storage.replace(Customer{"C00003", + "Martin", + "Torento", + "Torento", + "Canada", + 2, + 8000.00, + 7000.00, + 7000.00, + 8000.00, + "MJYURFD", + "A004"}); + storage.replace(Customer{"C00009", + "Ramesh", + "Mumbai", + "Mumbai", + "India", + 3, + 8000.00, + 7000.00, + 3000.00, + 12000.00, + "Phone No", + "A002"}); + storage.replace(Customer{"C00014", + "Rangarappa", + "Bangalore", + "Bangalore", + "India", + 2, + 8000.00, + 11000.00, + 7000.00, + 12000.00, + "AAAATGF", + "A001"}); + storage.replace(Customer{"C00016", + "Venkatpati", + "Bangalore", + "Bangalore", + "India", + 2, + 8000.00, + 11000.00, + 7000.00, + 12000.00, + "JRTVFDD", + "A007"}); + storage.replace(Customer{"C00011", + "Sundariya", + "Chennai", + "Chennai", + "India", + 3, + 7000.00, + 11000.00, + 7000.00, + 11000.00, + "PPHGRTS", + "A010"}); + + storage.replace(Order{"200114", 3500, 2000, "15-AUG-08", "C00002", "A008"}); + storage.replace(Order{"200122", 2500, 400, "16-SEP-08", "C00003", "A004"}); + storage.replace(Order{"200118", 500, 100, "20-JUL-08", "C00023", "A006"}); + storage.replace(Order{"200119", 4000, 700, "16-SEP-08", "C00007", "A010"}); + storage.replace(Order{"200121", 1500, 600, "23-SEP-08", "C00008", "A004"}); + storage.replace(Order{"200130", 2500, 400, "30-JUL-08", "C00025", "A011"}); + storage.replace(Order{"200134", 4200, 1800, "25-SEP-08", "C00004", "A005"}); + storage.replace(Order{"200108", 4000, 600, "15-FEB-08", "C00008", "A004"}); + storage.replace(Order{"200103", 1500, 700, "15-MAY-08", "C00021", "A005"}); + storage.replace(Order{"200105", 2500, 500, "18-JUL-08", "C00025", "A011"}); + storage.replace(Order{"200109", 3500, 800, "30-JUL-08", "C00011", "A010"}); + storage.replace(Order{"200101", 3000, 1000, "15-JUL-08", "C00001", "A008"}); + storage.replace(Order{"200111", 1000, 300, "10-JUL-08", "C00020", "A008"}); + storage.replace(Order{"200104", 1500, 500, "13-MAR-08", "C00006", "A004"}); + storage.replace(Order{"200106", 2500, 700, "20-APR-08", "C00005", "A002"}); + storage.replace(Order{"200125", 2000, 600, "10-OCT-08", "C00018", "A005"}); + storage.replace(Order{"200117", 800, 200, "20-OCT-08", "C00014", "A001"}); + storage.replace(Order{"200123", 500, 100, "16-SEP-08", "C00022", "A002"}); + storage.replace(Order{"200120", 500, 100, "20-JUL-08", "C00009", "A002"}); + storage.replace(Order{"200116", 500, 100, "13-JUL-08", "C00010", "A009"}); + storage.replace(Order{"200124", 500, 100, "20-JUN-08", "C00017", "A007"}); + storage.replace(Order{"200126", 500, 100, "24-JUN-08", "C00022", "A002"}); + storage.replace(Order{"200129", 2500, 500, "20-JUL-08", "C00024", "A006"}); + storage.replace(Order{"200127", 2500, 400, "20-JUL-08", "C00015", "A003"}); + storage.replace(Order{"200128", 3500, 1500, "20-JUL-08", "C00009", "A002"}); + storage.replace(Order{"200135", 2000, 800, "16-SEP-08", "C00007", "A010"}); + storage.replace(Order{"200131", 900, 150, "26-AUG-08", "C00012", "A012"}); + storage.replace(Order{"200133", 1200, 400, "29-JUN-08", "C00009", "A002"}); + storage.replace(Order{"200100", 1000, 600, "08-JAN-08", "C00015", "A003"}); + storage.replace(Order{"200110", 3000, 500, "15-APR-08", "C00019", "A010"}); + storage.replace(Order{"200107", 4500, 900, "30-AUG-08", "C00007", "A010"}); + storage.replace(Order{"200112", 2000, 400, "30-MAY-08", "C00016", "A007"}); + storage.replace(Order{"200113", 4000, 600, "10-JUN-08", "C00022", "A002"}); + storage.replace(Order{"200102", 2000, 300, "25-MAY-08", "C00012", "A012"}); + + { + // SELECT agent_code,agent_name,working_area,commission + // FROM agents + // WHERE exists + // (SELECT * + // FROM customer + // WHERE grade=3 AND agents.agent_code=customer.agent_code) + // ORDER BY commission; + auto rows = 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)); + cout << "AGENT_CODE AGENT_NAME WORKING_AREA COMMISSION" << endl; + cout << "---------- ---------------------------------------- ------------ ----------" << endl; + 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; + } + } + { + // 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); + 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)))))); + cout << "CUST_CODE CUST_NAME CUST_CITY GRADE" << endl; + cout << "---------- ---------- ----------------------------------- ----------" << endl; + 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; + } + } + { + // SELECT agent_code,ord_num,ord_amount,cust_code + // FROM orders + // WHERE NOT EXISTS + // (SELECT agent_code + // FROM customer + // WHERE payment_amt=1400); + + auto rows = storage.select( + columns(&Order::agentCode, &Order::num, &Order::amount, &Order::custCode), + from(), + 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) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) + << endl; + } + } + { + + // SELECT "orders"."AGENT_CODE", "orders"."ORD_NUM", "orders"."ORD_AMOUNT", "orders"."CUST_CODE", 'c'."PAYMENT_AMT" + // FROM 'orders' INNER JOIN 'customer' 'c' ON('c'."AGENT_CODE" = "orders"."AGENT_CODE") + // WHERE(NOT(EXISTS + // ( + // SELECT 'd'."AGENT_CODE" FROM 'customer' 'd' WHERE((('c'."PAYMENT_AMT" = 7000) AND('d'."AGENT_CODE" = 'c'."AGENT_CODE"))))) + // ) + // ORDER BY 'c'."PAYMENT_AMT" + + using als = alias_c; + using als_2 = alias_d; + + double amount = 2000; + auto where_clause = + select(alias_column(&Customer::agentCode), + from(), + where(is_equal(alias_column(&Customer::paymentAmt), std::ref(amount)) and + (alias_column(&Customer::agentCode) == c(alias_column(&Customer::agentCode))))); + + amount = 7000; + + auto statement = + storage.prepare(select(columns(&Order::agentCode, + &Order::num, + &Order::amount, + &Order::custCode, + alias_column(&Customer::paymentAmt)), + from(), + inner_join(on(alias_column(&Customer::agentCode) == c(&Order::agentCode))), + where(not exists(where_clause)), + order_by(alias_column(&Customer::paymentAmt)))); + + auto sql = statement.expanded_sql(); + auto rows = storage.execute(statement); + cout << endl; + 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; + } + } + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/fetch_content/CMakeLists.txt b/libs/sqlite_orm-1.8.2/examples/fetch_content/CMakeLists.txt new file mode 100644 index 0000000..1741974 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/fetch_content/CMakeLists.txt @@ -0,0 +1,14 @@ +### Preamble ### +cmake_minimum_required(VERSION 3.15) +project(Test VERSION 0.1 LANGUAGES CXX) + +##### Project wide setup #### +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +### Dependencies ### +add_subdirectory(dependencies) + +### Main Targets ### +add_subdirectory(src) diff --git a/libs/sqlite_orm-1.8.2/examples/fetch_content/dependencies/CMakeLists.txt b/libs/sqlite_orm-1.8.2/examples/fetch_content/dependencies/CMakeLists.txt new file mode 100644 index 0000000..da0ecd3 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/fetch_content/dependencies/CMakeLists.txt @@ -0,0 +1,9 @@ +include(FetchContent) + +# You can configure this for your need, presumbably you want speificy a git tag here instead of a branch +FetchContent_Declare(sqliteOrm + GIT_REPOSITORY https://github.com/fnc12/sqlite_orm + GIT_TAG origin/dev +) + +add_subdirectory(sqlite_orm) diff --git a/libs/sqlite_orm-1.8.2/examples/fetch_content/dependencies/sqlite_orm/CMakeLists.txt b/libs/sqlite_orm-1.8.2/examples/fetch_content/dependencies/sqlite_orm/CMakeLists.txt new file mode 100644 index 0000000..705383a --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/fetch_content/dependencies/sqlite_orm/CMakeLists.txt @@ -0,0 +1,3 @@ +# Set here options for sliteOrm +# for example set(BUILD_TESTING ON) if you want to have tests included of sqliteOrm +FetchContent_MakeAvailable(sqliteOrm) \ No newline at end of file diff --git a/libs/sqlite_orm-1.8.2/examples/fetch_content/src/CMakeLists.txt b/libs/sqlite_orm-1.8.2/examples/fetch_content/src/CMakeLists.txt new file mode 100644 index 0000000..95a90f4 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/fetch_content/src/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(Test) +target_link_libraries(Test PRIVATE sqlite_orm::sqlite_orm) + +target_sources(Test + PRIVATE + main.cpp +) \ No newline at end of file diff --git a/libs/sqlite_orm-1.8.2/examples/fetch_content/src/main.cpp b/libs/sqlite_orm-1.8.2/examples/fetch_content/src/main.cpp new file mode 100644 index 0000000..8b8be1f --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/fetch_content/src/main.cpp @@ -0,0 +1,46 @@ +#include + +#include +#include +#include + +using namespace sqlite_orm; + +using std::cout; +using std::endl; + +struct RapArtist { + int id; + std::string name; +}; + +int main(int, char**) { + + auto storage = make_storage(":memory:", + make_table("rap_artists", + make_column("id", &RapArtist::id, primary_key()), + make_column("name", &RapArtist::name))); + cout << "in memory db opened" << endl; + storage.sync_schema(); + + assert(!storage.count()); + + storage.insert(RapArtist{-1, "The Weeknd"}); + + storage.transaction([&] { + storage.insert(RapArtist{-1, "Drake"}); + return true; + }); + + cout << "rap artists count = " << storage.count() << endl; + + // transaction also work in memory.. + storage.transaction([&] { + storage.insert(RapArtist{-1, "Kanye West"}); + return false; + }); + + cout << "rap artists count = " << storage.count() << " (no Kanye)" << endl; + + return 0; +} \ No newline at end of file diff --git a/libs/sqlite_orm-1.8.2/examples/find_package/CMakeLists.txt b/libs/sqlite_orm-1.8.2/examples/find_package/CMakeLists.txt new file mode 100644 index 0000000..6c6e018 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/find_package/CMakeLists.txt @@ -0,0 +1,14 @@ +### Preamble ### +cmake_minimum_required(VERSION 3.15) +project(Test VERSION 0.1 LANGUAGES CXX) + +##### Project wide setup #### +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +### Dependencies ### +find_package(SqliteOrm REQUIRED) + +### Main Targets ### +add_subdirectory(src) diff --git a/libs/sqlite_orm-1.8.2/examples/find_package/src/CMakeLists.txt b/libs/sqlite_orm-1.8.2/examples/find_package/src/CMakeLists.txt new file mode 100644 index 0000000..95a90f4 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/find_package/src/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(Test) +target_link_libraries(Test PRIVATE sqlite_orm::sqlite_orm) + +target_sources(Test + PRIVATE + main.cpp +) \ No newline at end of file diff --git a/libs/sqlite_orm-1.8.2/examples/find_package/src/main.cpp b/libs/sqlite_orm-1.8.2/examples/find_package/src/main.cpp new file mode 100644 index 0000000..8b8be1f --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/find_package/src/main.cpp @@ -0,0 +1,46 @@ +#include + +#include +#include +#include + +using namespace sqlite_orm; + +using std::cout; +using std::endl; + +struct RapArtist { + int id; + std::string name; +}; + +int main(int, char**) { + + auto storage = make_storage(":memory:", + make_table("rap_artists", + make_column("id", &RapArtist::id, primary_key()), + make_column("name", &RapArtist::name))); + cout << "in memory db opened" << endl; + storage.sync_schema(); + + assert(!storage.count()); + + storage.insert(RapArtist{-1, "The Weeknd"}); + + storage.transaction([&] { + storage.insert(RapArtist{-1, "Drake"}); + return true; + }); + + cout << "rap artists count = " << storage.count() << endl; + + // transaction also work in memory.. + storage.transaction([&] { + storage.insert(RapArtist{-1, "Kanye West"}); + return false; + }); + + cout << "rap artists count = " << storage.count() << " (no Kanye)" << endl; + + return 0; +} \ No newline at end of file diff --git a/libs/sqlite_orm-1.8.2/examples/foreign_key.cpp b/libs/sqlite_orm-1.8.2/examples/foreign_key.cpp new file mode 100644 index 0000000..b8bb850 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/foreign_key.cpp @@ -0,0 +1,233 @@ +// +// The example is implemented from here https://sqlite.org/foreignkeys.html +// + +#include + +#include +#include +#include +#include + +using std::cout; +using std::endl; + +struct Artist { + int artistId; + std::string artistName; +}; + +struct Track { + int trackId; + std::string trackName; + std::unique_ptr trackArtist; // must map to &Artist::artistId +}; + +int main(int, char** argv) { + cout << "path = " << argv[0] << endl; + + using namespace sqlite_orm; + { // simple case with foreign key to a single column without actions + auto storage = make_storage("foreign_key.sqlite", + make_table("artist", + make_column("artistid", &Artist::artistId, primary_key()), + make_column("artistname", &Artist::artistName)), + make_table("track", + make_column("trackid", &Track::trackId, primary_key()), + make_column("trackname", &Track::trackName), + make_column("trackartist", &Track::trackArtist), + foreign_key(&Track::trackArtist).references(&Artist::artistId))); + auto syncSchemaRes = storage.sync_schema(); + for(auto& p: syncSchemaRes) { + cout << p.first << " " << p.second << endl; + } + + storage.remove_all(); + storage.remove_all(); + + storage.replace(Artist{1, "Dean Martin"}); + storage.replace(Artist{2, "Frank Sinatra"}); + + storage.replace(Track{11, "That's Amore", std::make_unique(1)}); + storage.replace(Track{12, "Christmas Blues", std::make_unique(1)}); + storage.replace(Track{13, "My Way", std::make_unique(2)}); + + try { + // This fails because value inserted into the trackartist column (3) + // 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) { + cout << e.what() << endl; + } + + // This succeeds because a NULL is inserted into trackartist. A + // corresponding row in the artist table is not required in this case. + storage.replace(Track{14, "Mr. Bojangles", nullptr}); + + // Trying to modify the trackartist field of the record after it has + // been inserted does not work either, since the new value of trackartist (3) + // still does not correspond to any row in the artist table. + try { + storage.update_all(set(assign(&Track::trackArtist, 3)), + where(is_equal(&Track::trackName, "Mr. Bojangles"))); + assert(0); + } catch(const std::system_error& e) { + cout << e.what() << endl; + } + + // Insert the required row into the artist table. It is then possible to + // update the inserted row to set trackartist to 3 (since a corresponding + // row in the artist table now exists). + storage.replace(Artist{3, "Sammy Davis Jr."}); + storage.update_all(set(assign(&Track::trackArtist, 3)), where(is_equal(&Track::trackName, "Mr. Bojangles"))); + + // Now that "Sammy Davis Jr." (artistid = 3) has been added to the database, + // it is possible to INSERT new tracks using this artist without violating + // the foreign key constraint: + storage.replace(Track{15, "Boogie Woogie", std::make_unique(3)}); + + try { + // Attempting to delete the artist record for "Frank Sinatra" fails, since + // 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) { + cout << e.what() << endl; + } + + // Delete all the records from the track table that refer to the artist + // "Frank Sinatra". Only then is it possible to delete the artist. + storage.remove_all(where(is_equal(&Track::trackName, "My Way"))); + storage.remove_all(where(is_equal(&Artist::artistName, "Frank Sinatra"))); + + try { + // Try to update the artistid of a row in the artist table while there + // 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) { + cout << e.what() << endl; + } + + // Once all the records that refer to a row in the artist table have + // been deleted, it is possible to modify the artistid of the row. + storage.remove_all(where(in(&Track::trackName, {"That's Amore", "Christmas Blues"}))); + storage.update_all(set(c(&Artist::artistId) = 4), where(c(&Artist::artistName) == "Dean Martin")); + } + { // case with ON UPDATE CASCADE + auto storage = make_storage( + "foreign_key2.sqlite", + make_table("artist", + make_column("artistid", &Artist::artistId, primary_key()), + make_column("artistname", &Artist::artistName)), + make_table("track", + make_column("trackid", &Track::trackId, primary_key()), + make_column("trackname", &Track::trackName), + make_column("trackartist", &Track::trackArtist), + foreign_key(&Track::trackArtist).references(&Artist::artistId).on_update.cascade())); + auto syncSchemaRes = storage.sync_schema(); + for(auto& p: syncSchemaRes) { + cout << p.first << " " << p.second << endl; + } + + storage.remove_all(); + storage.remove_all(); + + storage.replace(Artist{1, "Dean Martin"}); + storage.replace(Artist{2, "Frank Sinatra"}); + + storage.replace(Track{11, "That's Amore", std::make_unique(1)}); + storage.replace(Track{12, "Christmas Blues", std::make_unique(1)}); + storage.replace(Track{13, "My Way", std::make_unique(2)}); + + // Update the artistid column of the artist record for "Dean Martin". + // Normally, this would raise a constraint, as it would orphan the two + // dependent records in the track table. However, the ON UPDATE CASCADE clause + // attached to the foreign key definition causes the update to "cascade" + // to the child table, preventing the foreign key constraint violation. + // UPDATE artist SET artistid = 100 WHERE artistname = 'Dean Martin'; + storage.update_all(set(c(&Artist::artistId) = 100), where(c(&Artist::artistName) == "Dean Martin")); + + cout << "artists:" << endl; + for(auto& artist: storage.iterate()) { + cout << artist.artistId << '\t' << artist.artistName << endl; + } + cout << endl; + + cout << "tracks:" << endl; + for(auto& track: storage.iterate()) { + cout << track.trackId << '\t' << track.trackName << '\t'; + if(track.trackArtist) { + cout << *track.trackArtist; + } else { + cout << "null"; + } + cout << endl; + } + cout << endl; + } + { // case with ON DELETE SET DEFAULT + auto storage = make_storage( + "foreign_key3.sqlite", + make_table("artist", + make_column("artistid", &Artist::artistId, primary_key()), + make_column("artistname", &Artist::artistName)), + make_table("track", + make_column("trackid", &Track::trackId, primary_key()), + make_column("trackname", &Track::trackName), + 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) { + cout << p.first << " " << p.second << endl; + } + + storage.remove_all(); + storage.remove_all(); + + storage.replace(Artist{3, "Sammy Davis Jr."}); + + storage.replace(Track{14, "Mr. Bojangles", std::make_unique(3)}); + + // Deleting the row from the parent table causes the child key + // value of the dependent row to be set to integer value 0. However, this + // value does not correspond to any row in the parent table. Therefore + // the foreign key constraint is violated and an is exception thrown. + // DELETE FROM artist WHERE artistname = 'Sammy Davis Jr.'; + try { + storage.remove_all(where(c(&Artist::artistName) == "Sammy Davis Jr.")); + assert(0); + } catch(const std::system_error& e) { + cout << e.what() << endl; + } + + // This time, the value 0 does correspond to a parent table row. And + // so the DELETE statement does not violate the foreign key constraint + // and no exception is thrown. + // INSERT INTO artist VALUES(0, 'Unknown Artist'); + // DELETE FROM artist WHERE artistname = 'Sammy Davis Jr.' + storage.replace(Artist{0, "Unknown Artist"}); + storage.remove_all(where(c(&Artist::artistName) == "Sammy Davis Jr.")); + + cout << "artists:" << endl; + for(auto& artist: storage.iterate()) { + cout << artist.artistId << '\t' << artist.artistName << endl; + } + cout << endl; + + cout << "tracks:" << endl; + for(auto& track: storage.iterate()) { + cout << track.trackId << '\t' << track.trackName << '\t'; + if(track.trackArtist) { + cout << *track.trackArtist; + } else { + cout << "null"; + } + cout << endl; + } + cout << endl; + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/generated_column.cpp b/libs/sqlite_orm-1.8.2/examples/generated_column.cpp new file mode 100644 index 0000000..699cf39 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/generated_column.cpp @@ -0,0 +1,64 @@ +/** + * Thanks to that nice website https://database.guide/how-to-create-a-computed-column-in-sqlite/ for this example. + */ + +#include +#include + +#if SQLITE_VERSION_NUMBER >= 3031000 + +using namespace sqlite_orm; +using std::cout; +using std::endl; + +int main() { + struct Product { + int id = 0; + std::string name; + int quantity = 0; + float price = 0; + float totalValue = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Product() {} + Product(int id, std::string name, int quantity, float price, float totalValue = 0.f) : + id{id}, name{std::move(name)}, quantity{quantity}, price{price}, totalValue{totalValue} {} +#endif + }; + auto storage = make_storage({}, + make_table("products", + make_column("id", &Product::id, primary_key()), + make_column("name", &Product::name), + make_column("quantity", &Product::quantity), + make_column("price", &Product::price), + make_column("total_value", + &Product::totalValue, + generated_always_as(&Product::price * c(&Product::quantity))))); + storage.sync_schema(); + + storage.replace(Product{1, "Hammer", 10, 9.99f}); + storage.replace(Product{2, "Saw", 5, 11.34f}); + storage.replace(Product{3, "Wrench", 7, 37.00f}); + storage.replace(Product{4, "Chisel", 9, 23.00f}); + storage.replace(Product{5, "Bandage", 70, 120.00f}); + + cout << "Products:" << endl; + for(auto& product: storage.iterate()) { + cout << storage.dump(product) << endl; + } + cout << endl; + + // UPDATE products + // SET quantity = 5 WHERE id = 1; + storage.update_all(set(c(&Product::quantity) = 5), where(c(&Product::id) == 1)); + + cout << "Products after update:" << endl; + for(auto& product: storage.iterate()) { + cout << storage.dump(product) << endl; + } + cout << endl; + + return 0; +} + +#endif // SQLITE_VERSION_NUMBER >= 3031000 diff --git a/libs/sqlite_orm-1.8.2/examples/group_by.cpp b/libs/sqlite_orm-1.8.2/examples/group_by.cpp new file mode 100644 index 0000000..8be9122 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/group_by.cpp @@ -0,0 +1,81 @@ +// +// Example is implemented from here https://www.tutorialspoint.com/sqlite/sqlite_group_by.htm +// + +#include +#include +#include + +using std::cout; +using std::endl; + +struct Employee { + int id; + std::string name; + int age; + std::string address; + double salary; +}; + +int main(int, char**) { + using namespace sqlite_orm; + + auto storage = make_storage("group_by.sqlite", + 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(); + storage.remove_all(); + + storage.insert(Employee{-1, "Paul", 32, "California", 20000.0}); + storage.insert(Employee{-1, "Allen", 25, "Texas", 15000.0}); + storage.insert(Employee{-1, "Teddy", 23, "Norway", 20000.0}); + storage.insert(Employee{-1, "Mark", 25, "Rich-Mond", 65000.0}); + storage.insert(Employee{-1, "David", 27, "Texas", 85000.0}); + storage.insert(Employee{-1, "Kim", 22, "South-Hall", 45000.0}); + storage.insert(Employee{-1, "James", 24, "Houston", 10000.0}); + + // If you want to know the total amount of salary on each customer, then GROUP BY query would be as follows: + // SELECT NAME, SUM(SALARY) + // FROM COMPANY + // GROUP BY NAME; + auto salaryName = storage.select(columns(&Employee::name, sum(&Employee::salary)), group_by(&Employee::name)); + for(auto& t: salaryName) { + cout << std::get<0>(t) << '\t' << *std::get<1>(t) << endl; + } + + // Now, let us create three more records in COMPANY table using the following INSERT statements: + + storage.insert(Employee{-1, "Paul", 24, "Houston", 20000.00}); + storage.insert(Employee{-1, "James", 44, "Norway", 5000.00}); + storage.insert(Employee{-1, "James", 24, "Texas", 5000.00}); + + cout << endl << "Now, our table has the following records with duplicate names:" << endl << endl; + + for(auto& employee: storage.iterate()) { + cout << storage.dump(employee) << endl; + } + + cout << endl + << "Again, let us use the same statement to group-by all the records using NAME column as follows:" << endl + << endl; + salaryName = storage.select(columns(&Employee::name, sum(&Employee::salary)), + group_by(&Employee::name), + order_by(&Employee::name)); + for(auto& t: salaryName) { + cout << std::get<0>(t) << '\t' << *std::get<1>(t) << endl; + } + + cout << endl << "Let us use ORDER BY clause along with GROUP BY clause as follows:" << endl << endl; + salaryName = storage.select(columns(&Employee::name, sum(&Employee::salary)), + group_by(&Employee::name), + order_by(&Employee::name).desc()); + for(auto& t: salaryName) { + cout << std::get<0>(t) << '\t' << *std::get<1>(t) << endl; + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/having.cpp b/libs/sqlite_orm-1.8.2/examples/having.cpp new file mode 100644 index 0000000..8b5481a --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/having.cpp @@ -0,0 +1,70 @@ +/** + * Ths example is implemented from here https://www.tutorialspoint.com/sqlite/sqlite_having_clause.htm + */ + +#include +#include +#include + +using std::cout; +using std::endl; + +struct Employee { + int id; + std::string name; + int age; + std::string address; + float salary; +}; + +int main() { + + using namespace sqlite_orm; + + auto storage = make_storage("having.sqlite", + 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(); + storage.remove_all(); + + storage.replace(Employee{1, "Paul", 32, "California", 20000.0}); + storage.replace(Employee{2, "Allen", 25, "Texas", 15000.0}); + storage.replace(Employee{3, "Teddy", 23, "Norway", 20000.0}); + storage.replace(Employee{4, "Mark", 25, "Rich-Mond", 65000.0}); + storage.replace(Employee{5, "David", 27, "Texas", 85000.0}); + storage.replace(Employee{6, "Kim", 22, "South-Hall", 45000.0}); + storage.replace(Employee{7, "James", 24, "Houston", 10000.0}); + storage.replace(Employee{8, "Paul", 24, "Houston", 20000.0}); + storage.replace(Employee{9, "James", 44, "Norway", 5000.0}); + storage.replace(Employee{10, "James", 45, "Texas", 5000.0}); + + { + // SELECT * + // FROM COMPANY + // GROUP BY name + // HAVING count(name) < 2; + auto rows = storage.get_all(group_by(&Employee::name).having(lesser_than(count(&Employee::name), 2))); + for(auto& employee: rows) { + cout << storage.dump(employee) << endl; + } + cout << endl; + } + { + // SELECT * + // FROM COMPANY + // GROUP BY name + // HAVING count(name) > 2; + auto rows = + storage.get_all(group_by(&Employee::name).having(greater_than(count(&Employee::name), 2))); + for(auto& employee: rows) { + cout << storage.dump(employee) << endl; + } + cout << endl; + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/in_memory.cpp b/libs/sqlite_orm-1.8.2/examples/in_memory.cpp new file mode 100644 index 0000000..5799f96 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/in_memory.cpp @@ -0,0 +1,46 @@ +#include + +#include +#include +#include + +using namespace sqlite_orm; + +using std::cout; +using std::endl; + +struct RapArtist { + int id; + std::string name; +}; + +int main(int, char**) { + + auto storage = make_storage(":memory:", + make_table("rap_artists", + make_column("id", &RapArtist::id, primary_key()), + make_column("name", &RapArtist::name))); + cout << "in memory db opened" << endl; + storage.sync_schema(); + + assert(!storage.count()); + + storage.insert(RapArtist{-1, "The Weeknd"}); + + storage.transaction([&] { + storage.insert(RapArtist{-1, "Drake"}); + return true; + }); + + cout << "rap artists count = " << storage.count() << endl; + + // transaction also work in memory.. + storage.transaction([&] { + storage.insert(RapArtist{-1, "Kanye West"}); + return false; + }); + + cout << "rap artists count = " << storage.count() << " (no Kanye)" << endl; + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/index.cpp b/libs/sqlite_orm-1.8.2/examples/index.cpp new file mode 100644 index 0000000..2a68a69 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/index.cpp @@ -0,0 +1,67 @@ +#include + +#include +#include + +using std::cout; +using std::endl; + +struct Contract { + std::string firstName; + std::string lastName; + std::string email; +}; + +using namespace sqlite_orm; + +// beware - put `make_index` before `make_table` cause `sync_schema` is called in reverse order +// otherwise you'll receive an exception +auto storage = make_storage( + "index.sqlite", + make_index("idx_contacts_name", &Contract::firstName, &Contract::lastName, where(length(&Contract::firstName) > 2)), + make_unique_index("idx_contacts_email", indexed_column(&Contract::email).collate("BINARY").desc()), + make_table("contacts", + make_column("first_name", &Contract::firstName), + make_column("last_name", &Contract::lastName), + make_column("email", &Contract::email))); + +int main(int, char**) { + + storage.sync_schema(); + storage.remove_all(); + + storage.insert(Contract{ + "John", + "Doe", + "john.doe@sqlitetutorial.net", + }); + try { + storage.insert(Contract{ + "Johny", + "Doe", + "john.doe@sqlitetutorial.net", + }); + } catch(const std::system_error& e) { + cout << e.what() << endl; + } + std::vector moreContracts = { + Contract{ + "David", + "Brown", + "david.brown@sqlitetutorial.net", + }, + Contract{ + "Lisa", + "Smith", + "lisa.smith@sqlitetutorial.net", + }, + }; + storage.insert_range(moreContracts.begin(), moreContracts.end()); + + auto lisas = storage.get_all(where(c(&Contract::email) == "lisa.smith@sqlitetutorial.net")); + + storage.drop_index("idx_contacts_name"); + storage.drop_index("idx_contacts_email"); + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/insert.cpp b/libs/sqlite_orm-1.8.2/examples/insert.cpp new file mode 100644 index 0000000..742e4d7 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/insert.cpp @@ -0,0 +1,132 @@ +/** + * 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 separately, inserting a subclass object and inserting a vector. + */ + +#include +#include +#include + +using std::cout; +using std::endl; + +struct Employee { + int id; + std::string name; + int age; + std::string address; + double salary; +}; + +struct DetailedEmployee : public Employee { + std::string birthDate; +}; + +int main(int, char**) { + using namespace sqlite_orm; + + auto storage = make_storage("insert.sqlite", + 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(); + storage.remove_all(); + + Employee paul{ + -1, + "Paul", + 32, + "California", + 20000.00, + }; + + // insert returns inserted id + paul.id = storage.insert(paul); + + storage.insert(Employee{ + -1, + "Allen", + 25, + "Texas", + 15000.00, + }); + + DetailedEmployee teddy; + teddy.name = "Teddy"; + teddy.age = 23; + teddy.address = "Norway"; + teddy.salary = 20000.00; + + // to insert subclass object as a superclass you have to specify type explicitly + teddy.id = storage.insert(teddy); + + std::vector otherEmployees; + otherEmployees.push_back(Employee{ + -1, + "Mark", + 25, + "Rich-Mond", + 65000.00, + }); + otherEmployees.push_back(Employee{ + -1, + "David", + 27, + "Texas", + 85000.00, + }); + otherEmployees.push_back(Employee{ + -1, + "Kim", + 22, + "South-Hall", + 45000.00, + }); + // transaction is optional. It is used here to optimize sqlite usage - every insert opens + // 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) { + storage.insert(employee); + } + return true; // commit + }); + + Employee james{ + -1, + "James", + 24, + "Houston", + 10000.00, + }; + james.id = storage.insert(james); + + cout << "---------------------" << endl; + 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/libs/sqlite_orm-1.8.2/examples/iteration.cpp b/libs/sqlite_orm-1.8.2/examples/iteration.cpp new file mode 100644 index 0000000..f7bc9ec --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/iteration.cpp @@ -0,0 +1,68 @@ + +#include +#include +#include + +using std::cout; +using std::endl; + +struct MarvelHero { + int id; + std::string name; + std::string abilities; +}; + +int main(int, char**) { + using namespace sqlite_orm; + auto storage = make_storage("iteration.sqlite", + make_table("marvel", + make_column("id", &MarvelHero::id, primary_key()), + make_column("name", &MarvelHero::name), + make_column("abilities", &MarvelHero::abilities))); + storage.sync_schema(); + + storage.remove_all(); + + // insert values.. + storage.insert(MarvelHero{-1, "Tony Stark", "Iron man, playboy, billionaire, philanthropist"}); + storage.insert(MarvelHero{-1, "Thor", "Storm god"}); + storage.insert(MarvelHero{-1, "Vision", "Min Stone"}); + storage.insert(MarvelHero{-1, "Captain America", "Vibranium shield"}); + storage.insert(MarvelHero{-1, "Hulk", "Strength"}); + storage.insert(MarvelHero{-1, "Star Lord", "Humor"}); + storage.insert(MarvelHero{-1, "Peter Parker", "Spiderman"}); + storage.insert(MarvelHero{-1, "Clint Barton", "Hawkeye"}); + storage.insert(MarvelHero{-1, "Natasha Romanoff", "Black widow"}); + storage.insert(MarvelHero{-1, "Groot", "I am Groot!"}); + + cout << "Heros count = " << storage.count() << endl; + + // 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()) { + 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"))) { + cout << "hero = " << storage.dump(hero) << endl; + } + + cout << "Heros with LENGTH(name) < 6 :" << endl; + for(auto& hero: storage.iterate(where(length(&MarvelHero::name) < 6))) { + cout << "hero = " << storage.dump(hero) << endl; + } + + std::vector heroesByAlgorithm; + heroesByAlgorithm.reserve(static_cast(storage.count())); + { + auto view = storage.iterate(); + std::copy(view.begin(), view.end(), std::back_inserter(heroesByAlgorithm)); + } + cout << "heroesByAlgorithm.size = " << heroesByAlgorithm.size() << endl; + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/key_value.cpp b/libs/sqlite_orm-1.8.2/examples/key_value.cpp new file mode 100644 index 0000000..7658fe1 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/key_value.cpp @@ -0,0 +1,87 @@ + +/****** + This is an implementation of key-value storage just like + NSUserDefaults on iOS or SharedPreferences on Android. + Here is the deal: we need to have `setValueForKey` and `getValueByKey` interface. All data must ba saved in sqlite + database. + To perform this we create schema with table `key_value` and two columns: key:string and value:string. + Key column has PRIMARY KEY constraint cause keys must be unique. `setValue` function takes two string arguments: key + and value. It creates a KeyValue object (KeyValue is a class mapped to the storage) with arguments and calls REPLACE. + REPLACE (shorter version of INSERT OR REPLACE) removes row with a given primary key if it exists and inserts a given + value in the table. After this there is a row in the `key_value` table with key and value passed into `setValue` + function. `getValue` performs `get_pointer` by id and returns its value or returns empty string if nothing obtained + from db. + ******/ + +#include + +#include + +using std::cerr; +using std::cout; +using std::endl; + +/** + * This is just a mapped type. User doesn't have to interact with it directly. + */ +struct KeyValue { + std::string key; + std::string value; +}; + +auto& getStorage() { + using namespace sqlite_orm; + static auto storage = make_storage("key_value_example.sqlite", + make_table("key_value", + make_column("key", &KeyValue::key, primary_key()), + make_column("value", &KeyValue::value))); + return storage; +} + +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) { + using namespace sqlite_orm; + if(auto kv = getStorage().get_pointer(key)) { + return kv->value; + } else { + return {}; + } +} + +int storedKeysCount() { + return getStorage().count(); +} + +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 + + getStorage().sync_schema(); // to create table if it doesn't exist + + struct { + std::string userId = "userId"; + std::string userName = "userName"; + std::string userGender = "userGender"; // this key will be missing + } keys; // to keep keys in one place + + setValue(keys.userId, "6"); + setValue(keys.userName, "Peter"); + + auto userId = getValue(keys.userId); + cout << "userId = " << userId << endl; // userId = 6 + + auto userName = getValue(keys.userName); + cout << "userName = " << userName << endl; // userName = Peter + + auto userGender = getValue(keys.userGender); + cout << "userGender = " << userGender << endl; // userGender = + + auto kvsCount = storedKeysCount(); + cout << "kvsCount = " << kvsCount << endl; // kvsCount = 2 +} diff --git a/libs/sqlite_orm-1.8.2/examples/left_and_inner_join.cpp b/libs/sqlite_orm-1.8.2/examples/left_and_inner_join.cpp new file mode 100644 index 0000000..6f5e90a --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/left_and_inner_join.cpp @@ -0,0 +1,330 @@ +/** + * Example is imlemented from here http://www.sqlitetutorial.net/sqlite-left-join/ + * In this example you got to download db file 'chinook.db' first from here + * http://www.sqlitetutorial.net/sqlite-sample-database/ an place the file near executable. + */ + +#include +#include +#include +#include + +using std::cout; +using std::endl; + +struct Artist { + std::unique_ptr artistId; + std::unique_ptr name; +}; + +struct Album { + std::unique_ptr albumId; + std::unique_ptr title; + std::unique_ptr artistId; +}; + +struct Track { + int trackId; + std::string name; + std::unique_ptr albumId; + int mediaTypeId; + std::unique_ptr genreId; + std::unique_ptr composer; + long milliseconds; + std::unique_ptr bytes; + double unitPrice; +}; + +inline auto initStorage(const std::string& path) { + using namespace sqlite_orm; + return make_storage(path, + make_table("artists", + make_column("ArtistId", &Artist::artistId, primary_key()), + make_column("Name", &Artist::name)), + make_table("albums", + make_column("AlbumId", &Album::albumId, primary_key()), + make_column("Title", &Album::title), + make_column("ArtistId", &Album::artistId)), + make_table("tracks", + make_column("TrackId", &Track::trackId, primary_key()), + make_column("Name", &Track::name), + make_column("AlbumId", &Track::albumId), + make_column("MediaTypeId", &Track::mediaTypeId), + make_column("GenreId", &Track::genreId), + make_column("Composer", &Track::composer), + make_column("Milliseconds", &Track::milliseconds), + make_column("Bytes", &Track::bytes), + make_column("UnitPrice", &Track::unitPrice))); +} + +int main(int, char**) { + + auto storage = initStorage("chinook.db"); + + using namespace sqlite_orm; + + // SELECT + // artists.ArtistId, + // albumId + // FROM + // artists + // LEFT JOIN albums ON albums.artistid = artists.artistid + // ORDER BY + // albumid; + auto rows = storage.select(columns(&Artist::artistId, &Album::albumId), + 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); + if(artistId) { + cout << *artistId; + } else { + cout << "null"; + } + cout << '\t'; + auto& albumId = std::get<1>(row); + if(albumId) { + cout << *albumId; + } else { + cout << "null"; + } + cout << endl; + } + + cout << endl; + + // SELECT + // artists.ArtistId, + // albumId + // FROM + // artists + // LEFT JOIN albums ON albums.artistid = artists.artistid + // WHERE + // albumid IS NULL; + rows = storage.select(columns(&Artist::artistId, &Album::albumId), + 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); + if(artistId) { + cout << *artistId; + } else { + cout << "null"; + } + cout << '\t'; + auto& albumId = std::get<1>(row); + if(albumId) { + cout << *albumId; + } else { + cout << "null"; + } + cout << endl; + } + + cout << endl; + + // SELECT + // trackid, + // name, + // title + // FROM + // tracks + // INNER JOIN albums ON albums.albumid = tracks.albumid; + 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) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t'; + if(std::get<2>(row)) { + cout << *std::get<2>(row); + } else { + cout << "null"; + } + cout << endl; + } + cout << endl; + + // SELECT + // trackid, + // name, + // tracks.AlbumId, + // albums.AlbumId, + // title + // FROM + // tracks + // INNER JOIN albums ON albums.albumid = tracks.albumid; + auto innerJoinRows1 = + 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) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t'; + if(std::get<2>(row)) { + cout << *std::get<2>(row); + } else { + cout << "null"; + } + cout << '\t'; + if(std::get<3>(row)) { + cout << *std::get<3>(row); + } else { + cout << "null"; + } + cout << '\t'; + if(std::get<4>(row)) { + cout << *std::get<4>(row); + } else { + cout << "null"; + } + cout << '\t'; + cout << endl; + } + cout << endl; + + // SELECT + // trackid, + // tracks.name AS Track, + // albums.title AS Album, + // artists.name AS Artist + // FROM + // tracks + // INNER JOIN albums ON albums.albumid = tracks.albumid + // INNER JOIN artists ON artists.artistid = albums.artistid; + auto innerJoinRows2 = storage.select(columns(&Track::trackId, &Track::name, &Album::title, &Artist::name), + 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) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t'; + if(std::get<2>(row)) { + cout << *std::get<2>(row); + } else { + cout << "null"; + } + cout << '\t'; + if(std::get<3>(row)) { + cout << *std::get<3>(row); + } else { + cout << "null"; + } + cout << '\t'; + } + cout << endl; + + { + /** + * JOIN has many usages so this is another example from here + * http://www.w3resource.com/sqlite/sqlite-left-join.php and here + * http://www.w3resource.com/sqlite/sqlite-natural-join.php + */ + + struct Doctor { + int id; + std::string name; + std::string degree; + }; + + struct Visit { + int doctorId; + std::string patientName; + std::string vdate; + }; + + struct A { + int id; + std::string des1; + std::string des2; + }; + + struct B { + int id; + std::string des3; + std::string des4; + }; + + auto storage2 = make_storage("doctors.sqlite", + make_table("doctors", + make_column("doctor_id", &Doctor::id, primary_key()), + make_column("doctor_name", &Doctor::name), + make_column("degree", &Doctor::degree)), + make_table("visits", + make_column("doctor_id", &Visit::doctorId), + make_column("patient_name", &Visit::patientName), + make_column("vdate", &Visit::vdate)), + make_table("table_a", + make_column("id", &A::id, primary_key()), + make_column("des1", &A::des1), + make_column("des2", &A::des2)), + make_table("table_b", + make_column("id", &B::id, primary_key()), + make_column("des3", &B::des3), + make_column("des4", &B::des4))); + storage2.sync_schema(); + + storage2.replace(Doctor{ + 210, + "Dr. John Linga", + "MD", + }); + storage2.replace(Doctor{211, "Dr. Peter Hall", "MBBS"}); + storage2.replace(Doctor{212, "Dr. Ke Gee", "MD"}); + storage2.replace(Doctor{213, "Dr. Pat Fay", "MD"}); + + storage2.replace(Visit{210, "Julia Nayer", "2013-10-15"}); + storage2.replace(Visit{214, "TJ Olson", "2013-10-14"}); + storage2.replace(Visit{215, "John Seo", "2013-10-15"}); + storage2.replace(Visit{212, "James Marlow", "2013-10-16"}); + storage2.replace(Visit{212, "Jason Mallin", "2013-10-12"}); + + storage2.replace(A{100, "desc11", "desc12"}); + storage2.replace(A{101, "desc21", "desc22"}); + storage2.replace(A{102, "desc31", "desc32"}); + + storage2.replace(B{101, "desc41", "desc42"}); + storage2.replace(B{103, "desc51", "desc52"}); + storage2.replace(B{105, "desc61", "desc62"}); + + // SELECT a.doctor_id,a.doctor_name, + // c.patient_name,c.vdate + // FROM doctors a + // LEFT JOIN visits c + // 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) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) + << endl; + } + cout << endl; + + // SELECT a.doctor_id,a.doctor_name, + // c.patient_name,c.vdate + // FROM doctors a + // JOIN visits c + // 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) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) + << endl; + } + cout << endl; + + // SELECT doctor_id,doctor_name, + // patient_name,vdate + // FROM doctors + // LEFT JOIN visits + // 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) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << '\t' << std::get<3>(row) + << endl; + } + cout << endl; + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/multi_table_select.cpp b/libs/sqlite_orm-1.8.2/examples/multi_table_select.cpp new file mode 100644 index 0000000..e3c1d27 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/multi_table_select.cpp @@ -0,0 +1,91 @@ +/** + * Example implemented from here http://sqlite.awardspace.info/syntax/sqlitepg05.htm + */ + +#include +#include +#include + +using std::cout; +using std::endl; + +struct ReqEquip { + int reqNumber; + std::string requestor; + std::string auth; + std::string reqDate; +}; + +struct ReqDetail { + int reqNumber; + int stockNumber; + int quantity; + double itemCost; +}; + +int main(int, char**) { + + using namespace sqlite_orm; + auto storage = make_storage("multi_table_select.sqlite", + make_table("ReqEquip", + make_column("ReqNumber", &ReqEquip::reqNumber, primary_key()), + make_column("Requestor", &ReqEquip::requestor), + make_column("Auth", &ReqEquip::auth), + make_column("ReqDate", &ReqEquip::reqDate)), + make_table("ReqDetail", + make_column("ReqNumber", &ReqDetail::reqNumber), + make_column("StockNumber", &ReqDetail::stockNumber), + make_column("Quantity", &ReqDetail::quantity), + make_column("ItemCost", &ReqDetail::itemCost))); + + storage.sync_schema(); + storage.remove_all(); + storage.remove_all(); + + storage.replace(ReqEquip{1000, "Carl Jones", "A. Robinson Mgr", "2007/10/30"}); + storage.replace(ReqEquip{1001, "Peter Smith", "A. Robinson Mgr", "2007/11/05"}); + storage.replace(ReqEquip{1002, "Carl Jones", "A. Robinson Mgr", "2007/11/06"}); + + storage.replace(ReqDetail{1000, 51013, 2, 7.52}); + storage.replace(ReqDetail{1000, 51002, 4, .75}); + storage.replace(ReqDetail{1000, 43512, 4, 1.52}); + storage.replace(ReqDetail{1001, 23155, 1, 9.82}); + storage.replace(ReqDetail{1001, 43111, 1, 3.69}); + storage.replace(ReqDetail{1002, 51001, 1, .75}); + storage.replace(ReqDetail{1002, 23155, 1, 9.82}); + + cout << endl; + + // SELECT ReqEquip.ReqNumber, ReqEquip.Requestor, ReqDetail.Quantity, ReqDetail.StockNumber + // FROM ReqEquip, ReqDetail + // WHERE ReqEquip.ReqNumber = ReqDetail.ReqNumber; + auto rows = storage.select( + columns(&ReqEquip::reqNumber, &ReqEquip::requestor, &ReqDetail::quantity, &ReqDetail::stockNumber), + where(c(&ReqEquip::reqNumber) == &ReqDetail::reqNumber)); + for(auto& row: rows) { + cout << std::get<0>(row) << '\t'; + cout << std::get<1>(row) << '\t'; + cout << std::get<2>(row) << '\t'; + cout << std::get<3>(row) << '\t'; + cout << endl; + } + + cout << endl; + + // SELECT ReqEquip.ReqNumber, ReqEquip.Requestor, ReqDetail.Quantity, ReqDetail.StockNumber + // FROM ReqEquip, ReqDetail + // WHERE ReqEquip.ReqNumber = ReqDetail.ReqNumber AND ReqEquip.ReqNumber = 1000; + + 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) { + cout << std::get<0>(row) << '\t'; + cout << std::get<1>(row) << '\t'; + cout << std::get<2>(row) << '\t'; + cout << std::get<3>(row) << '\t'; + cout << endl; + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/natural_join.cpp b/libs/sqlite_orm-1.8.2/examples/natural_join.cpp new file mode 100644 index 0000000..f66ca2b --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/natural_join.cpp @@ -0,0 +1,106 @@ +/** + * Implemented this example https://www.w3resource.com/sqlite/sqlite-natural-join.php + */ + +#include +#include +#include + +using std::cout; +using std::endl; + +struct Doctor { + int doctor_id; + std::string doctor_name; + std::string degree; +}; + +struct Speciality { + int spl_id; + std::string spl_descrip; + int doctor_id; +}; + +struct Visit { + int doctor_id; + std::string patient_name; + std::string vdate; +}; + +int main() { + using namespace sqlite_orm; + + auto storage = make_storage("natural_join.sqlite", + make_table("doctors", + make_column("doctor_id", &Doctor::doctor_id, primary_key()), + make_column("doctor_name", &Doctor::doctor_name), + make_column("degree", &Doctor::degree)), + make_table("speciality", + make_column("spl_id", &Speciality::spl_id, primary_key()), + make_column("spl_descrip", &Speciality::spl_descrip), + make_column("doctor_id", &Speciality::doctor_id)), + make_table("visits", + make_column("doctor_id", &Visit::doctor_id), + make_column("patient_name", &Visit::patient_name), + make_column("vdate", &Visit::vdate))); + storage.sync_schema(); + storage.remove_all(); + storage.remove_all(); + storage.remove_all(); + + storage.replace(Doctor{210, "Dr. John Linga", "MD"}); + storage.replace(Doctor{211, "Dr. Peter Hall", "MBBS"}); + storage.replace(Doctor{212, "Dr. Ke Gee", "MD"}); + storage.replace(Doctor{213, "Dr. Pat Fay", "MD"}); + + storage.replace(Speciality{1, "CARDIO", 211}); + storage.replace(Speciality{2, "NEURO", 213}); + storage.replace(Speciality{3, "ARTHO", 212}); + storage.replace(Speciality{4, "GYNO", 210}); + + storage.replace(Visit{210, "Julia Nayer", "2013-10-15"}); + storage.replace(Visit{214, "TJ Olson", "2013-10-14"}); + storage.replace(Visit{215, "John Seo", "2013-10-15"}); + storage.replace(Visit{212, "James Marlow", "2013-10-16"}); + storage.replace(Visit{212, "Jason Mallin", "2013-10-12"}); + + { + // SELECT doctor_id,doctor_name,degree,patient_name,vdate + // FROM doctors + // NATURAL JOIN visits + // WHERE doctors.degree="MD"; + auto rows = storage.select( + columns(&Doctor::doctor_id, &Doctor::doctor_name, &Doctor::degree, &Visit::patient_name, &Visit::vdate), + natural_join(), + where(c(&Doctor::degree) == "MD")); + cout << "rows count = " << rows.size() << endl; + 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; + } + } + cout << endl; + { + // SELECT doctor_id,doctor_name,degree,spl_descrip,patient_name,vdate + // FROM doctors + // NATURAL JOIN speciality + // NATURAL JOIN visits + // WHERE doctors.degree='MD'; + auto rows = storage.select(columns(&Doctor::doctor_id, + &Doctor::doctor_name, + &Doctor::degree, + &Speciality::spl_descrip, + &Visit::patient_name, + &Visit::vdate), + natural_join(), + natural_join(), + where(c(&Doctor::degree) == "MD")); + cout << "rows count = " << rows.size() << endl; + 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; + } + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/nullable_enum_binding.cpp b/libs/sqlite_orm-1.8.2/examples/nullable_enum_binding.cpp new file mode 100644 index 0000000..8625af0 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/nullable_enum_binding.cpp @@ -0,0 +1,155 @@ + +#include +#include +#include +#include + +using std::cout; +using std::endl; + +/** + * Gender is gonna me stored as a nullable TEXT. + * None is gonna be NULL, + * Male - 'male' string, + * Female - 'female' string + */ +enum class Gender { + None, + Male, + Female, +}; + +std::unique_ptr GenderToString(Gender gender) { + switch(gender) { + case Gender::Female: + return std::make_unique("female"); + case Gender::Male: + return std::make_unique("male"); + case Gender::None: + return {}; + } + throw std::domain_error("Invalid Gender enum"); +} + +std::unique_ptr GenderFromString(const std::string& s) { + if(s == "female") { + return std::make_unique(Gender::Female); + } else if(s == "male") { + return std::make_unique(Gender::Male); + } + return nullptr; +} + +struct User { + int id; + std::string name; + Gender gender; +}; + +namespace sqlite_orm { + + template<> + struct type_printer : public text_printer {}; + + template<> + struct statement_binder { + + int bind(sqlite3_stmt* stmt, int index, const Gender& value) { + if(auto str = GenderToString(value)) { + return statement_binder().bind(stmt, index, *str); + } else { + return statement_binder().bind(stmt, index, nullptr); + } + } + }; + + template<> + struct field_printer { + std::string operator()(const Gender& t) const { + if(auto res = GenderToString(t)) { + return *res; + } else { + return "None"; + } + } + }; + + template<> + struct row_extractor { + Gender extract(const char* row_value) { + if(row_value) { + if(auto gender = GenderFromString(row_value)) { + return *gender; + } else { + throw std::runtime_error("incorrect gender string (" + std::string(row_value) + ")"); + } + } else { + return Gender::None; + } + } + + Gender extract(sqlite3_stmt* stmt, int columnIndex) { + auto str = sqlite3_column_text(stmt, columnIndex); + return this->extract((const char*)str); + } + }; + + /** + * This is where sqlite_orm lib understands that your type is nullable - by + * specializing type_is_nullable and deriving from std::true_type. + */ + template<> + struct type_is_nullable : 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 { + return g != Gender::None; + } + }; +} + +int main(int, char**) { + using namespace sqlite_orm; + auto storage = make_storage("nullable_enum.sqlite", + make_table("users", + make_column("id", &User::id, primary_key()), + make_column("name", &User::name), + make_column("gender", &User::gender))); + storage.sync_schema(); + storage.remove_all(); + + storage.insert(User{ + -1, + "Creeper", + Gender::Male, + }); + storage.insert(User{ + -1, + "Witch", + Gender::Female, + }); + storage.insert(User{ + -1, + "Enderman", + Gender::None, + }); + + cout << "All users :" << endl; + 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) { + 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) { + cout << storage.dump(user) << endl; + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/pointer_passing_interface.cpp b/libs/sqlite_orm-1.8.2/examples/pointer_passing_interface.cpp new file mode 100644 index 0000000..f9c6862 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/pointer_passing_interface.cpp @@ -0,0 +1,228 @@ +/** + * Example of how to use the pointer-passing interface, creating an ad-hoc sqlite extension. + * + * It demonstrates how to modularize turning a seralized representation of an object + * into C/C++ native object instance, plus invoking operations on that object. + * This approach is much better in regard to encapsulation in contrary to defining a set of + * functions which always only accept the serialized representation of an object. + * + * This example assumes an application that deals with a fixed set of c++ system error categories and codes, + * which are also stored in a result table: + * - stores results of some type, along with a error category enumeration and an error code. + * - registers a sqlite scalar function getting the pointer to the error category. + * - registers another set of sqlite scalar functions that accept a pointer to an error category. + * - registers a sqlite scalar function creating a std::error_code. + * - registers a sqlite scalar function comparing two std::error_code. + * + * Note: pointers are only accessible within application code, and therefore unleakable. + */ +#include +#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +#define ENABLE_THIS_EXAMPLE +#endif + +#ifdef ENABLE_THIS_EXAMPLE +#include +#include +#include +#include +#include +#include +#include + +using namespace sqlite_orm; + +using std::cout; +using std::default_delete; +using std::endl; +using std::error_category; +using std::error_code; +using std::make_unique; +using std::min; + +// name for our pointer value types +inline constexpr const char ecat_pvt_name[] = "ecat"; +inline constexpr const char ecode_pvt_name[] = "ecode"; +// c++ integral constant for our pointer value types +using ecat_pvt = std::integral_constant; +using ecode_pvt = std::integral_constant; + +// a fixed set of error categories the application is dealing with +enum class app_error_category : unsigned int { + generic, + system, + io, + future, + sqlite_orm, +}; + +// map app_error_category enum to std::error_category +// sorted by app_error_category +using ecat_map_t = std::tuple; +extern const std::array ecat_map; + +const std::array ecat_map = {{ + {app_error_category::generic, std::generic_category()}, + {app_error_category::system, std::system_category()}, + {app_error_category::io, std::iostream_category()}, + {app_error_category::future, std::future_category()}, + {app_error_category::sqlite_orm, sqlite_orm::get_sqlite_error_category()}, +}}; + +template +struct str_alias : alias_tag { + static constexpr char str[] = {A, L..., '\0'}; + static constexpr const char* get() { + return str; + } +}; +#endif + +int main() { +#ifdef ENABLE_THIS_EXAMPLE + // table structure for results of some type + struct Result { + int id = 0; + int errorValue = 0; + unsigned int errorCategory = 0; + }; + using ecat_arg_t = pointer_arg; + using ecode_arg_t = pointer_arg; + + // function returning a pointer to a std::error_category, + // which is only visible to functions accepting pointer values of type "ecat" + struct get_error_category_fn { + using ecat_binding = static_pointer_binding; + + ecat_binding operator()(unsigned int errorCategory) const { + size_t idx = min(errorCategory, ecat_map.size()); + const error_category* ecat = idx != ecat_map.size() ? &get(ecat_map[idx]) : nullptr; + return statically_bindable_pointer(ecat); + } + + static constexpr const char* name() { + return "get_error_category"; + } + }; + + // function accepting a pointer to a std::error_category, + // returns the category's name + struct error_category_name_fn { + std::string operator()(ecat_arg_t pv) const { + if(const error_category* ec = pv) { + return ec->name(); + } + return {}; + } + + static constexpr const char* name() { + return "error_category_name"; + } + }; + + // function accepting a pointer to a std::error_category and an error code, + // returns the error message + struct error_category_message_fn { + std::string operator()(ecat_arg_t pv, int errorValue) const { + if(const error_category* ec = pv) { + return ec->message(errorValue); + } + return {}; + } + + static constexpr const char* name() { + return "error_category_message"; + } + }; + + // function returning an error_code object from an error value + struct make_error_code_fn { + using ecode_binding = pointer_binding>; + + ecode_binding operator()(int errorValue, unsigned int errorCategory) const { + size_t idx = min(errorCategory, ecat_map.size()); + error_code* ec = idx != ecat_map.size() + ? new error_code{errorValue, get(ecat_map[idx])} + : nullptr; + return bindable_pointer(ec, default_delete{}); + } + + static constexpr const char* name() { + return "make_error_code"; + } + }; + + // function comparing two error_code objects + struct equal_error_code_fn { + bool operator()(ecode_arg_t pv1, ecode_arg_t pv2) const { + error_code *ec1 = pv1, *ec2 = pv2; + if(ec1 && ec2) { + return *ec1 == *ec2; + } + return false; + } + + static constexpr const char* name() { + return "equal_error_code"; + } + }; + + auto storage = make_storage({}, + make_table("result", + make_column("id", &Result::id, primary_key()), + make_column("error_value", &Result::errorValue), + make_column("error_category", &Result::errorCategory))); + storage.sync_schema(); + + storage.transaction([&storage] { + storage.replace(Result{1, 2, 0}); + storage.replace(Result{2, 0, 1}); + storage.replace(Result{3, 1, 2}); + storage.replace(Result{4, 1, 3}); + storage.replace(Result{5, 0, 4}); + // error category out of bounds, get_error_category() will return null + storage.replace(Result{6, 0, 5}); + return true; + }); + + storage.create_scalar_function(); + storage.create_scalar_function(); + storage.create_scalar_function(); + storage.create_scalar_function(); + storage.create_scalar_function(); + + // list all results including error category name and error message + { + + // SELECT id, not error_value as ok, error_category, error_value, + // equal_error_code(make_error_code(error_value, error_category), ?), + // error_category_name(get_error_category(error_category)), + // error_category_message(get_error_category(error_category), error_value) + // FROM result + // ORDER BY not error_value, error_category, error_value, id; + auto rows = + storage.select(columns(&Result::id, + as>(c(&Result::errorValue) == 0), + &Result::errorValue, + &Result::errorCategory, + as>(func( + func(&Result::errorValue, &Result::errorCategory), + bindable_pointer(make_unique()))), + func(func(&Result::errorCategory)), + func(func(&Result::errorCategory), + &Result::errorValue)), + multi_order_by(order_by(c(&Result::errorValue) == 0), + order_by(&Result::errorCategory), + order_by(&Result::errorValue), + order_by(&Result::id))); + for(auto& row: rows) { + cout << std::get<0>(row) << ' ' << std::get<1>(row) << ' ' << std::get<2>(row) << ' ' << std::get<3>(row) + << ' ' << std::get<4>(row) << " \"" << std::get<5>(row) << "\"" + << " \"" << std::get<6>(row) << "\"" << endl; + } + cout << endl; + } + +#endif + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/prepared_statement.cpp b/libs/sqlite_orm-1.8.2/examples/prepared_statement.cpp new file mode 100644 index 0000000..e637e72 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/prepared_statement.cpp @@ -0,0 +1,227 @@ +/** + * There are two member functions in storage_t class which you need to use to operate with + * prepared statements: storage_t::prepare and storage_t::execute. + * Also if you need to rebind arguments just use get(statement) = ... syntax + * just like you do with std::tuple. + * Once a statement is prepared it holds a connection to a database inside. This connection will be open + * until at least one statement object exists. + */ +#include +#include + +using namespace sqlite_orm; + +using std::cout; +using std::endl; + +struct Doctor { + int doctor_id = 0; + std::string doctor_name; + std::string degree; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Doctor() = default; + Doctor(int doctor_id, std::string doctor_name, std::string degree) : + doctor_id{doctor_id}, doctor_name{std::move(doctor_name)}, degree{std::move(degree)} {} +#endif +}; + +struct Speciality { + int spl_id = 0; + std::string spl_descrip; + int doctor_id = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Speciality() = default; + Speciality(int spl_id, std::string spl_descrip, int doctor_id) : + spl_id{spl_id}, spl_descrip{std::move(spl_descrip)}, doctor_id{doctor_id} {} +#endif +}; + +struct Visit { + int doctor_id = 0; + std::string patient_name; + std::string vdate; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Visit() = default; + Visit(int doctor_id, std::string patient_name, std::string vdate) : + doctor_id{doctor_id}, patient_name{std::move(patient_name)}, vdate{std::move(vdate)} {} +#endif +}; + +int main() { + auto storage = make_storage("prepared.sqlite", + make_table("doctors", + make_column("doctor_id", &Doctor::doctor_id, primary_key()), + make_column("doctor_name", &Doctor::doctor_name), + make_column("degree", &Doctor::degree)), + make_table("speciality", + make_column("spl_id", &Speciality::spl_id, primary_key()), + make_column("spl_descrip", &Speciality::spl_descrip), + make_column("doctor_id", &Speciality::doctor_id)), + make_table("visits", + make_column("doctor_id", &Visit::doctor_id), + make_column("patient_name", &Visit::patient_name), + make_column("vdate", &Visit::vdate))); + storage.sync_schema(); + storage.remove_all(); + storage.remove_all(); + storage.remove_all(); + { + // first we create a statement object for replace query with a doctor object + auto replaceStatement = storage.prepare(replace(Doctor{210, "Dr. John Linga", "MD"})); + + cout << "replaceStatement = " << replaceStatement.sql() << endl; + + // next we execute our statement + storage.execute(replaceStatement); + + // now 'doctors' table has one row [210, 'Dr. John Linga', 'MD'] + // Next we shall reuse the statement to replace another doctor + // replaceStatement contains a doctor inside it which can be obtained + // with get<0>(statement) function + + get<0>(replaceStatement) = {211, "Dr. Peter Hall", "MBBS"}; + storage.execute(replaceStatement); + + // now 'doctors' table has two rows. + // Next we shall reuse the statement again with member assignment + + auto& doctor = get<0>(replaceStatement); // doctor is Doctor & + doctor.doctor_id = 212; + doctor.doctor_name = "Dr. Ke Gee"; + doctor.degree = "MD"; + storage.execute(replaceStatement); + + // now 'doctors' table has three rows. + } + { + // also prepared statement can store arguments by reference. To do this you can + // pass a std::reference_wrapper instead of object value. + Doctor doctorToReplace{213, "Dr. Pat Fay", "MD"}; + auto replaceStatementByRef = storage.prepare(replace(std::ref(doctorToReplace))); + cout << "replaceStatementByRef = " << replaceStatementByRef.sql() << endl; + storage.execute(replaceStatementByRef); + + // now 'doctors' table has four rows. + // next we shall change doctorToReplace object and then execute our statement. + // Statement will be affected cause it stores a reference to the doctor + doctorToReplace.doctor_id = 214; + doctorToReplace.doctor_name = "Mosby"; + doctorToReplace.degree = "MBBS"; + + storage.execute(replaceStatementByRef); + + // and now 'doctors' table has five rows + } + + cout << "Doctors count = " << storage.count() << endl; + for(auto& doctor: storage.iterate()) { + cout << storage.dump(doctor) << endl; + } + + { + auto insertStatement = storage.prepare(insert(Speciality{1, "CARDIO", 211})); + cout << "insertStatement = " << insertStatement.sql() << endl; + storage.execute(insertStatement); + get<0>(insertStatement) = {2, "NEURO", 213}; + storage.execute(insertStatement); + get<0>(insertStatement) = {3, "ARTHO", 212}; + storage.execute(insertStatement); + get<0>(insertStatement) = {4, "GYNO", 210}; + storage.execute(insertStatement); + } + + cout << "Specialities count = " << storage.count() << endl; + for(auto& speciality: storage.iterate()) { + cout << storage.dump(speciality) << endl; + } + { + // let's insert (replace) 5 visits. We create two vectors with 2 visits each + std::vector visits; + visits.push_back({210, "Julia Nayer", "2013-10-15"}); + visits.push_back({214, "TJ Olson", "2013-10-14"}); + + // let's make a statement + auto replaceRangeStatement = storage.prepare(replace_range(visits.begin(), visits.end())); + cout << "replaceRangeStatement = " << replaceRangeStatement.sql() << endl; + + // replace two objects + storage.execute(replaceRangeStatement); + + std::vector visits2; + visits2.push_back({215, "John Seo", "2013-10-15"}); + visits2.push_back({212, "James Marlow", "2013-10-16"}); + + // reassign iterators to point to other visits. Beware that if end - begin + // will have different distance then you'll get a runtime error cause statement is + // already compiled with a fixed amount of arguments to bind + get<0>(replaceRangeStatement) = visits2.begin(); + get<1>(replaceRangeStatement) = visits2.end(); + + storage.execute(replaceRangeStatement); + + storage.replace(Visit{212, "Jason Mallin", "2013-10-12"}); + } + cout << "Visits count = " << storage.count() << endl; + for(auto& visit: storage.iterate()) { + cout << storage.dump(visit) << endl; + } + { + // SELECT doctor_id + // FROM visits + // WHERE LENGTH(patient_name) > 8 + auto selectStatement = storage.prepare(select(&Visit::doctor_id, where(length(&Visit::patient_name) > 8))); + cout << "selectStatement = " << selectStatement.sql() << endl; + { + auto rows = storage.execute(selectStatement); + cout << "rows count = " << rows.size() << endl; + for(auto& id: rows) { + cout << id << endl; + } + } + + // same statement, other bound values + // SELECT doctor_id + // FROM visits + // WHERE LENGTH(patient_name) > 11 + { + get<0>(selectStatement) = 11; + auto rows = storage.execute(selectStatement); + cout << "rows count = " << rows.size() << endl; + for(auto& id: rows) { + cout << id << endl; + } + } + } + { + // SELECT rowid, 'Doctor ' || doctor_name + // FROM doctors + // WHERE degree LIKE '%S' + auto selectStatement = storage.prepare( + select(columns(rowid(), "Doctor " || c(&Doctor::doctor_name)), where(like(&Doctor::degree, "%S")))); + cout << "selectStatement = " << selectStatement.sql() << endl; + { + auto rows = storage.execute(selectStatement); + cout << "rows count = " << rows.size() << endl; + for(auto& row: rows) { + cout << get<0>(row) << '\t' << get<1>(row) << endl; + } + } + // SELECT rowid, 'Nice ' || doctor_name + // FROM doctors + // WHERE degree LIKE '%D' + get<0>(selectStatement) = "Nice "; + get<1>(selectStatement) = "%D"; + { + auto rows = storage.execute(selectStatement); + cout << "rows count = " << rows.size() << endl; + for(auto& row: rows) { + cout << get<0>(row) << '\t' << get<1>(row) << endl; + } + } + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/private_class_members.cpp b/libs/sqlite_orm-1.8.2/examples/private_class_members.cpp new file mode 100644 index 0000000..00a4294 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/private_class_members.cpp @@ -0,0 +1,87 @@ +/** + * Sometimes data model classes may have private/protected fields to + * be mapped to a storage. In this case you have to pass setter and getter + * function instead of member pointer. Also to specify a column in select + * or where condition you have to add getter or setter function pointer. Both of them + * are mapped to the same column. + * getter must have definition like this: `const F& O::*() const` + * setter: `void O::*(F)` + */ + +#include +#include +#include + +using std::cout; +using std::endl; + +class Player { + int id = 0; + std::string name; + + public: + Player() {} + + Player(std::string name_) : name(std::move(name_)) {} + + Player(int id_, std::string name_) : id(id_), name(std::move(name_)) {} + + std::string getName() const { + return this->name; + } + + void setName(std::string name) { + this->name = std::move(name); + } + + int getId() const { + return this->id; + } + + void setId(int id) { + this->id = id; + } +}; + +int main(int, char**) { + using namespace sqlite_orm; + auto storage = make_storage( + "private.sqlite", + make_table( + "players", + make_column("id", + &Player::setId, // setter + &Player::getId, // getter + primary_key()), + make_column("name", + &Player::getName, // BTW order doesn't matter: setter can be placed before getter or opposite. + &Player::setName))); + storage.sync_schema(); + storage.remove_all(); + + auto soloId = storage.insert(Player("Solo")); + + auto playersCount = storage.count(); + cout << "players count = " << playersCount << endl; + assert(playersCount == 1); + + cout << "solo = " << storage.dump(storage.get(soloId)) << endl; + + auto deadpoolId = storage.insert(Player("Deadpool")); + + cout << "deadpool = " << storage.dump(storage.get(deadpoolId)) << endl; + + playersCount = storage.count(); + cout << "players count = " << playersCount << endl; + assert(playersCount == 2); + + auto idsOnly = storage.select(&Player::getId); // or storage.select(&Player::setId); + cout << "idsOnly count = " << idsOnly.size() << endl; + + 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) { + cout << storage.dump(player) << endl; + } +} diff --git a/libs/sqlite_orm-1.8.2/examples/select.cpp b/libs/sqlite_orm-1.8.2/examples/select.cpp new file mode 100644 index 0000000..eaf6cbf --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/select.cpp @@ -0,0 +1,171 @@ +/**** + Implemented example from here https://www.tutorialspoint.com/sqlite/sqlite_select_query.htm. + */ + +#include + +#include + +using namespace sqlite_orm; +using std::cout; +using std::endl; +using std::make_unique; +using std::string; + +void all_employees() { + struct Employee { + int id; + std::string name; + int age; + std::unique_ptr address; // optional + std::unique_ptr salary; // optional + }; + + auto storage = make_storage("select.sqlite", + 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(); + storage.remove_all(); // remove all old employees in case they exist in db.. + + // create employees.. + Employee paul{-1, "Paul", 32, make_unique("California"), make_unique(20000.0)}; + Employee allen{-1, "Allen", 25, make_unique("Texas"), make_unique(15000.0)}; + Employee teddy{-1, "Teddy", 23, make_unique("Norway"), make_unique(20000.0)}; + Employee mark{-1, "Mark", 25, make_unique("Rich-Mond"), make_unique(65000.0)}; + Employee david{-1, "David", 27, make_unique("Texas"), make_unique(85000.0)}; + Employee kim{-1, "Kim", 22, make_unique("South-Hall"), make_unique(45000.0)}; + Employee james{-1, "James", 24, make_unique("Houston"), make_unique(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); + + // print users.. + cout << "paul = " << storage.dump(paul) << endl; + cout << "allen = " << storage.dump(allen) << endl; + cout << "teddy = " << storage.dump(teddy) << endl; + cout << "mark = " << storage.dump(mark) << endl; + cout << "david = " << storage.dump(david) << endl; + cout << "kim = " << storage.dump(kim) << endl; + cout << "james = " << storage.dump(james) << endl; + + // select all employees.. + auto allEmployees = storage.get_all(); + + cout << "allEmployees[0] = " << storage.dump(allEmployees[0]) << endl; + cout << "allEmployees count = " << allEmployees.size() << endl; + + // now let's select id, name and salary.. + auto idsNamesSalarys = storage.select(columns(&Employee::id, &Employee::name, &Employee::salary)); + for(auto& row: idsNamesSalarys) { // row's type is tuple> + cout << "id = " << get<0>(row) << ", name = " << get<1>(row) << ", salary = "; + if(get<2>(row)) { + cout << *get<2>(row); + } else { + cout << "null"; + } + cout << endl; + } + + cout << endl; + + auto allEmployeeTuples = storage.select(asterisk()); + cout << "allEmployeeTuples count = " << allEmployeeTuples.size() << endl; + for(auto& row: allEmployeeTuples) { // row's type is std::tuple, + // std::unique_ptr> + cout << get<0>(row) << '\t' << get<1>(row) << '\t' << get<2>(row) << '\t'; + if(auto& value = get<3>(row)) { + cout << *value; + } else { + cout << "null"; + } + cout << '\t'; + if(auto& value = get<4>(row)) { + cout << *value; + } else { + cout << "null"; + } + cout << '\t' << endl; + } + + cout << endl; + + auto allEmployeeObjects = storage.select(object()); + cout << "allEmployeeObjects count = " << allEmployeeObjects.size() << endl; + for(auto& employee: allEmployeeObjects) { + cout << employee.id << '\t' << employee.name << '\t' << employee.age << '\t'; + if(auto& value = employee.address) { + cout << *value; + } else { + cout << "null"; + } + cout << '\t'; + if(auto& value = employee.salary) { + cout << *value; + } else { + cout << "null"; + } + cout << '\t' << endl; + } + + cout << endl; +} + +void all_artists() { + struct Artist { + int id; + std::string name; + }; + + struct Album { + int id; + int artist_id; + }; + + auto storage = make_storage("", + make_table("artists", + make_column("id", &Artist::id, primary_key().autoincrement()), + make_column("name", &Artist::name)), + make_table("albums", + make_column("id", &Album::id, primary_key().autoincrement()), + make_column("artist_id", &Album::artist_id), + foreign_key(&Album::artist_id).references(&Artist::id))); + storage.sync_schema(); + storage.transaction([&storage] { + auto artistPk = storage.insert(Artist{-1, "Artist"}); + storage.insert(Album{-1, artistPk}); + storage.insert(Album{-1, artistPk}); + return true; + }); + + // SELECT artists.*, albums.* FROM artists JOIN albums ON albums.artist_id = artist.id + + cout << "artists.*, albums.*\n"; + // row's type is std::tuple + for(auto& row: storage.select(columns(asterisk(), asterisk()), + join(on(c(&Album::artist_id) == &Artist::id)))) { + cout << get<0>(row) << '\t' << get<1>(row) << '\t' << get<2>(row) << '\t' << get<3>(row) << '\n'; + } + cout << endl; +} + +int main() { + + try { + all_employees(); + all_artists(); + } catch(const std::system_error& e) { + cout << "[" << e.code() << "] " << e.what(); + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/self_join.cpp b/libs/sqlite_orm-1.8.2/examples/self_join.cpp new file mode 100644 index 0000000..d135e56 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/self_join.cpp @@ -0,0 +1,234 @@ +// +// This example is obtained from here http://www.sqlitetutorial.net/sqlite-self-join/ +// + +#include +#include +#include + +using std::cout; +using std::endl; + +struct Employee { + int employeeId; + std::string lastName; + std::string firstName; + std::string title; + std::unique_ptr reportsTo; + std::string birthDate; + std::string hireDate; + std::string address; + std::string city; + std::string state; + std::string country; + std::string postalCode; + std::string phone; + std::string fax; + std::string email; +}; + +/** + * This is how custom alias is made: + * 1) it must have a `type` alias which is equal to your mapped class + * 2) it must have a static function with `get()` signature and return type with `operator<<` + */ +template +struct custom_alias : sqlite_orm::alias_tag { + using type = T; + + static const std::string& get() { + static const std::string res = "emp"; + return res; + } +}; + +int main() { + using namespace sqlite_orm; + auto storage = + make_storage("self_join.sqlite", + make_table("employees", + make_column("EmployeeId", &Employee::employeeId, primary_key().autoincrement()), + make_column("LastName", &Employee::lastName), + make_column("FirstName", &Employee::firstName), + make_column("Title", &Employee::title), + make_column("ReportsTo", &Employee::reportsTo), + make_column("BirthDate", &Employee::birthDate), + make_column("HireDate", &Employee::hireDate), + make_column("Address", &Employee::address), + make_column("City", &Employee::city), + make_column("State", &Employee::state), + make_column("Country", &Employee::country), + make_column("PostalCode", &Employee::postalCode), + make_column("Phone", &Employee::phone), + make_column("Fax", &Employee::fax), + make_column("Email", &Employee::email), + foreign_key(&Employee::reportsTo).references(&Employee::employeeId))); + storage.sync_schema(); + storage.remove_all(); + + storage.begin_transaction(); + storage.replace(Employee{ + 1, + "Adams", + "Andrew", + "General Manager", + {}, + "1962-02-18 00:00:00", + "2002-08-14 00:00:00", + "11120 Jasper Ave NW", + "Edmonton", + "AB", + "Canada", + "T5K 2N1", + "+1 (780) 428-9482", + "+1 (780) 428-3457", + "andrew@chinookcorp.com", + }); + storage.replace(Employee{ + 2, + "Edwards", + "Nancy", + "Sales Manager", + std::make_unique(1), + "1958-12-08 00:00:00", + "2002-05-01 00:00:00", + "825 8 Ave SW", + "Calgary", + "AB", + "Canada", + "T2P 2T3", + "+1 (403) 262-3443", + "+1 (403) 262-3322", + "nancy@chinookcorp.com", + }); + storage.replace(Employee{3, + "Peacock", + "Jane", + "Sales Support Agent", + std::make_unique(2), + "1973-08-29 00:00:00", + "2002-04-01 00:00:00", + "1111 6 Ave SW", + "Calgary", + "AB", + "Canada", + "T2P 5M5", + "+1 (403) 262-3443", + "+1 (403) 262-6712", + "jane@chinookcorp.com"}); + storage.replace(Employee{4, + "Park", + "Margaret", + "Sales Support Agent", + std::make_unique(2), + "1947-09-19 00:00:00", + "2003-05-03 00:00:00", + "683 10 Street SW", + "Calgary", + "AB", + "Canada", + "T2P 5G3", + "+1 (403) 263-4423", + "+1 (403) 263-4289", + "margaret@chinookcorp.com"}); + storage.replace(Employee{5, + "Johnson", + "Steve", + "Sales Support Agent", + std::make_unique(2), + "1965-03-03 00:00:00", + "2003-10-17 00:00:00", + "7727B 41 Ave", + "Calgary", + "AB", + "Canada", + "T3B 1Y7", + "1 (780) 836-9987", + "1 (780) 836-9543", + "steve@chinookcorp.com"}); + storage.replace(Employee{6, + "Mitchell", + "Michael", + "IT Manager", + std::make_unique(1), + "1973-07-01 00:00:00", + "2003-10-17 00:00:00", + "5827 Bowness Road NW", + "Calgary", + "AB", + "Canada", + "T3B 0C5", + "+1 (403) 246-9887", + "+1 (403) 246-9899", + "michael@chinookcorp.com"}); + storage.replace(Employee{7, + "King", + "Robert", + "IT Staff", + std::make_unique(6), + "1970-05-29 00:00:00", + "2004-01-02 00:00:00", + "590 Columbia Boulevard West", + "Lethbridge", + "AB", + "Canada", + "T1K 5N8", + "+1 (403) 456-9986", + "+1 (403) 456-8485", + "robert@chinookcorp.com"}); + storage.replace(Employee{8, + "Callahan", + "Laura", + "IT Staff", + std::make_unique(6), + "1968-01-09 00:00:00", + "2004-03-04 00:00:00", + "923 7 ST NW", + "Lethbridge", + "AB", + "Canada", + "T1H 1Y8", + "+1 (403) 467-3351", + "+1 (403) 467-8772", + "laura@chinookcorp.com"}); + storage.commit(); + + { + // SELECT m.FirstName || ' ' || m.LastName, + // employees.FirstName || ' ' || employees.LastName + // FROM employees + // INNER JOIN employees m + // ON m.ReportsTo = employees.EmployeeId + using als = alias_m; + auto firstNames = storage.select( + columns(alias_column(&Employee::firstName) || c(" ") || alias_column(&Employee::lastName), + &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) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << endl; + } + + assert(storage.count() == storage.count>()); + } + { // same query with custom alias 'emp' + cout << endl; + + // SELECT emp.FirstName || ' ' || emp.LastName, + // employees.FirstName || ' ' || employees.LastName + // FROM employees + // INNER JOIN employees emp + // ON emp.ReportsTo = employees.EmployeeId + using als = custom_alias; + auto firstNames = storage.select( + columns(alias_column(&Employee::firstName) || c(" ") || alias_column(&Employee::lastName), + &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) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << endl; + } + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/subentities.cpp b/libs/sqlite_orm-1.8.2/examples/subentities.cpp new file mode 100644 index 0000000..8894c10 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/subentities.cpp @@ -0,0 +1,115 @@ +/** + * Our goal is to manage a Student class. Student has basic id, name and roll_number and one more thing: + * vector of marks. Mark is a subentity with one to many relation. This example shows how to manage this kind of case. + * First of all we got to understand how to keep data in the db. We need two tables: `students` and `marks`. Students + * table has column equal to all Student class members exept marks. Marks table has two columns: student_id and value + * itself. We create two functions here: inserting/updating student (with his/her marks) and getting student (also with + * his/her marks). Schema is: `CREATE TABLE students (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL, roll_no + * INTEGER NOT NULL)` `CREATE TABLE marks (mark INTEGER NOT NULL, student_id INTEGER NOT NULL)` One of the main ideas of + * `sqlite_orm` is to give a developer ability to name tables/columns as he/she wants. Many other ORM libraries manage + * subentities automatically and it is not correct cause developer must understand how everything works inside sqlite + * otherwise his/her app might not work properly. Also developer must know schema in case he or she needs a strict + * access with sqlite client. + */ + +#include +#include + +using std::cout; +using std::endl; + +class Mark { + public: + int value; + int student_id; +}; + +class Student { + public: + int id; + std::string name; + int roll_number; + std::vector marks; +}; + +using namespace sqlite_orm; +auto storage = + make_storage("subentities.sqlite", + make_table("students", + make_column("id", &Student::id, primary_key()), + make_column("name", &Student::name), + make_column("roll_no", &Student::roll_number)), + 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) { + auto studentId = student.id; + if(storage.count(where(c(&Student::id) == student.id))) { + storage.update(student); + } else { + studentId = storage.insert(student); + } + // insert all marks within a transaction + storage.transaction([&] { + storage.remove_all(where(c(&Mark::student_id) == studentId)); + for(auto& mark: student.marks) { + storage.insert(Mark{mark, studentId}); + } + return true; + }); + return studentId; +} + +/** + * To get student from db we have to execute two queries: + * `SELECT * FROM students WHERE id = ?` + * `SELECT mark FROM marks WHERE student_id = ?` + */ +Student getStudent(int studentId) { + auto res = storage.get(studentId); + res.marks = storage.select(&Mark::value, where(c(&Mark::student_id) == studentId)); + return res; // must be moved automatically by compiler +} + +int main(int, char**) { + decltype(Student::id) mikeId; + decltype(Student::id) annaId; + + { + storage.sync_schema(); // create tables if they don't exist + + Student mike{-1, "Mike", 123, {}}; // create student named `Mike` without marks and without id + mike.marks = {3, 4, 5}; + mike.id = addStudent(mike); + mikeId = mike.id; + + // also let's create another students with marks.. + Student anna{-1, "Anna", 555, {}}; + anna.marks.push_back(6); + anna.marks.push_back(7); + anna.id = addStudent(anna); + annaId = anna.id; + } + // now let's assume we forgot about object `mike`, let's try to get him with his marks + // assume we know `mikeId` variable only + + { + auto mike = getStudent(mikeId); + cout << "mike = " << storage.dump(mike) << endl; + cout << "mike.marks = "; + for(auto& m: mike.marks) { + cout << m << " "; + } + cout << endl; + + auto anna = getStudent(annaId); + cout << "anna = " << storage.dump(anna) << endl; + cout << "anna.marks = "; + for(auto& m: anna.marks) { + cout << m << " "; + } + cout << endl; + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/subquery.cpp b/libs/sqlite_orm-1.8.2/examples/subquery.cpp new file mode 100644 index 0000000..c296203 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/subquery.cpp @@ -0,0 +1,1388 @@ +/** + * Implemented from here https://www.w3resource.com/sqlite/sqlite-subqueries.php + */ +#include +#include +#include +#include + +using std::cout; +using std::endl; + +struct Employee { + int id; + std::string firstName; + std::string lastName; + std::string email; + std::string phoneNumber; + std::string hireDate; + std::string jobId; + long salary; + std::unique_ptr commission_pct; + std::unique_ptr managerId; + int departmentId; +}; + +struct Department { + int id; + std::string name; + int managerId; + int locationId; +}; + +struct JobHistory { + decltype(Employee::id) employeeId; + std::string startDate; + std::string endDate; + decltype(Employee::jobId) jobId; + decltype(Employee::departmentId) departmentId; +}; + +int main(int, char**) { + using namespace sqlite_orm; + auto storage = make_storage("subquery.sqlite", + make_table("employees", + make_column("EMPLOYEE_ID", &Employee::id, primary_key()), + make_column("FIRST_NAME", &Employee::firstName), + make_column("LAST_NAME", &Employee::lastName), + make_column("EMAIL", &Employee::email), + make_column("PHONE_NUMBER", &Employee::phoneNumber), + make_column("HIRE_DATE", &Employee::hireDate), + make_column("JOB_ID", &Employee::jobId), + make_column("SALARY", &Employee::salary), + make_column("COMMISSION_PCT", &Employee::commission_pct), + make_column("MANAGER_ID", &Employee::managerId), + make_column("DEPARTMENT_ID", &Employee::departmentId)), + 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)), + make_table("job_history", + make_column("employee_id", &JobHistory::employeeId), + make_column("start_date", &JobHistory::startDate), + make_column("end_date", &JobHistory::endDate), + make_column("job_id", &JobHistory::jobId), + make_column("department_id", &JobHistory::departmentId))); + storage.sync_schema(); + storage.remove_all(); + + storage.replace( + Employee{100, "Steven", "King", "SKING", "515.123.4567", "17-Jun-87", "AD_PRES", 24000, {}, {}, 90}); + storage.replace(Employee{101, + "Neena", + "Kochhar", + "NKOCHHAR", + "515.123.4568", + "21-Sep-89", + "AD_VP", + 17000, + {}, + std::make_unique(100), + 90}); + storage.replace(Employee{102, + "Lex", + "De Haan", + "LDEHAAN", + "515.123.4569", + "13-Jan-93", + "AD_VP", + 17000, + {}, + std::make_unique(100), + 90}); + storage.replace(Employee{103, + "Alexander", + "Hunold", + "AHUNOLD", + "590.423.4567", + "3-Jan-90", + "IT_PROG", + 9000, + {}, + std::make_unique(102), + 60}); + storage.replace(Employee{104, + "Bruce", + "Ernst", + "BERNST", + "590.423.4568", + "21-May-91", + "IT_PROG", + 6000, + {}, + std::make_unique(103), + 60}); + storage.replace(Employee{105, + "David", + "Austin", + "DAUSTIN", + "590.423.4569", + "25-Jun-97", + "IT_PROG", + 4800, + {}, + std::make_unique(103), + 60}); + storage.replace(Employee{106, + "Valli", + "Pataballa", + "VPATABAL", + "590.423.4560", + "5-Feb-98", + "IT_PROG", + 4800, + {}, + std::make_unique(103), + 60}); + storage.replace(Employee{107, + "Diana", + "Lorentz", + "DLORENTZ", + "590.423.5567", + "7-Feb-99", + "IT_PROG", + 4200, + {}, + std::make_unique(103), + 60}); + storage.replace(Employee{108, + "Nancy", + "Greenberg", + "NGREENBE", + "515.124.4569", + "17-Aug-94", + "FI_MGR", + 12000, + {}, + std::make_unique(101), + 100}); + storage.replace(Employee{109, + "Daniel", + "Faviet", + "DFAVIET", + "515.124.4169", + "16-Aug-94", + "FI_ACCOUNT", + 9000, + {}, + std::make_unique(108), + 100}); + storage.replace(Employee{110, + "John", + "Chen", + "JCHEN", + "515.124.4269", + "28-Sep-97", + "FI_ACCOUNT", + 8200, + {}, + std::make_unique(108), + 100}); + storage.replace(Employee{111, + "Ismael", + "Sciarra", + "ISCIARRA", + "515.124.4369", + "30-Sep-97", + "FI_ACCOUNT", + 7700, + {}, + std::make_unique(108), + 100}); + storage.replace(Employee{112, + "Jose Manuel", + "Urman", + "JMURMAN", + "515.124.4469", + "7-Mar-98", + "FI_ACCOUNT", + 7800, + {}, + std::make_unique(108), + 100}); + storage.replace(Employee{113, + "Luis", + "Popp", + "LPOPP", + "515.124.4567", + "7-Dec-99", + "FI_ACCOUNT", + 6900, + {}, + std::make_unique(108), + 100}); + storage.replace(Employee{114, + "Den", + "Raphaely", + "DRAPHEAL", + "515.127.4561", + "7-Dec-94", + "PU_MAN", + 11000, + {}, + std::make_unique(100), + 30}); + storage.replace(Employee{115, + "Alexander", + "Khoo", + "AKHOO", + "515.127.4562", + "18-May-95", + "PU_CLERK", + 3100, + {}, + std::make_unique(114), + 30}); + storage.replace(Employee{116, + "Shelli", + "Baida", + "SBAIDA", + "515.127.4563", + "24-Dec-97", + "PU_CLERK", + 2900, + {}, + std::make_unique(114), + 30}); + storage.replace(Employee{117, + "Sigal", + "Tobias", + "STOBIAS", + "515.127.4564", + "24-Jul-97", + "PU_CLERK", + 2800, + {}, + std::make_unique(114), + 30}); + storage.replace(Employee{118, + "Guy", + "Himuro", + "GHIMURO", + "515.127.4565", + "15-Nov-98", + "PU_CLERK", + 2600, + {}, + std::make_unique(114), + 30}); + storage.replace(Employee{119, + "Karen", + "Colmenares", + "KCOLMENA", + "515.127.4566", + "10-Aug-99", + "PU_CLERK", + 2500, + {}, + std::make_unique(114), + 30}); + storage.replace(Employee{120, + "Matthew", + "Weiss", + "MWEISS", + "650.123.1234", + "18-Jul-96", + "ST_MAN", + 8000, + {}, + std::make_unique(100), + 50}); + storage.replace(Employee{121, + "Adam", + "Fripp", + "AFRIPP", + "650.123.2234", + "10-Apr-97", + "ST_MAN", + 8200, + {}, + std::make_unique(100), + 50}); + storage.replace(Employee{122, + "Payam", + "Kaufling", + "PKAUFLIN", + "650.123.3234", + "1-May-95", + "ST_MAN", + 7900, + {}, + std::make_unique(100), + 50}); + storage.replace(Employee{123, + "Shanta", + "Vollman", + "SVOLLMAN", + "650.123.4234", + "10-Oct-97", + "ST_MAN", + 6500, + {}, + std::make_unique(100), + 50}); + storage.replace(Employee{124, + "Kevin", + "Mourgos", + "KMOURGOS", + "650.123.5234", + "16-Nov-99", + "ST_MAN", + 5800, + {}, + std::make_unique(100), + 50}); + storage.replace(Employee{125, + "Julia", + "Nayer", + "JNAYER", + "650.124.1214", + "16-Jul-97", + "ST_CLERK", + 3200, + {}, + std::make_unique(120), + 50}); + storage.replace(Employee{126, + "Irene", + "Mikkilineni", + "IMIKKILI", + "650.124.1224", + "28-Sep-98", + "ST_CLERK", + 2700, + {}, + std::make_unique(120), + 50}); + storage.replace(Employee{127, + "James", + "Landry", + "JLANDRY", + "650.124.1334", + "14-Jan-99", + "ST_CLERK", + 2400, + {}, + std::make_unique(120), + 50}); + storage.replace(Employee{128, + "Steven", + "Markle", + "SMARKLE", + "650.124.1434", + "8-Mar-00", + "ST_CLERK", + 2200, + {}, + std::make_unique(120), + 50}); + storage.replace(Employee{129, + "Laura", + "Bissot", + "LBISSOT", + "650.124.5234", + "20-Aug-97", + "ST_CLERK", + 3300, + {}, + std::make_unique(121), + 50}); + storage.replace(Employee{130, + "Mozhe", + "Atkinson", + "MATKINSO", + "650.124.6234", + "30-Oct-97", + "ST_CLERK", + 2800, + {}, + std::make_unique(121), + 50}); + storage.replace(Employee{131, + "James", + "Marlow", + "JAMRLOW", + "650.124.7234", + "16-Feb-97", + "ST_CLERK", + 2500, + {}, + std::make_unique(121), + 50}); + storage.replace(Employee{132, + "TJ", + "Olson", + "TJOLSON", + "650.124.8234", + "10-Apr-99", + "ST_CLERK", + 2100, + {}, + std::make_unique(121), + 50}); + storage.replace(Employee{133, + "Jason", + "Mallin", + "JMALLIN", + "650.127.1934", + "14-Jun-96", + "ST_CLERK", + 3300, + {}, + std::make_unique(122), + 50}); + storage.replace(Employee{134, + "Michael", + "Rogers", + "MROGERS", + "650.127.1834", + "26-Aug-98", + "ST_CLERK", + 2900, + {}, + std::make_unique(122), + 50}); + storage.replace(Employee{135, + "Ki", + "Gee", + "KGEE", + "650.127.1734", + "12-Dec-99", + "ST_CLERK", + 2400, + {}, + std::make_unique(122), + 50}); + storage.replace(Employee{136, + "Hazel", + "Philtanker", + "HPHILTAN", + "650.127.1634", + "6-Feb-00", + "ST_CLERK", + 2200, + {}, + std::make_unique(122), + 50}); + storage.replace(Employee{137, + "Renske", + "Ladwig", + "RLADWIG", + "650.121.1234", + "14-Jul-95", + "ST_CLERK", + 3600, + {}, + std::make_unique(123), + 50}); + storage.replace(Employee{138, + "Stephen", + "Stiles", + "SSTILES", + "650.121.2034", + "26-Oct-97", + "ST_CLERK", + 3200, + {}, + std::make_unique(123), + 50}); + storage.replace(Employee{139, + "John", + "Seo", + "JSEO", + "650.121.2019", + "12-Feb-98", + "ST_CLERK", + 2700, + {}, + std::make_unique(123), + 50}); + storage.replace(Employee{140, + "Joshua", + "Patel", + "JPATEL", + "650.121.1834", + "6-Apr-98", + "ST_CLERK", + 2500, + {}, + std::make_unique(123), + 50}); + storage.replace(Employee{141, + "Trenna", + "Rajs", + "TRAJS", + "650.121.8009", + "17-Oct-95", + "ST_CLERK", + 3500, + {}, + std::make_unique(124), + 50}); + storage.replace(Employee{142, + "Curtis", + "Davies", + "CDAVIES", + "650.121.2994", + "29-Jan-97", + "ST_CLERK", + 3100, + {}, + std::make_unique(124), + 50}); + storage.replace(Employee{143, + "Randall", + "Matos", + "RMATOS", + "650.121.2874", + "15-Mar-98", + "ST_CLERK", + 2600, + {}, + std::make_unique(124), + 50}); + storage.replace(Employee{144, + "Peter", + "Vargas", + "PVARGAS", + "650.121.2004", + "9-Jul-98", + "ST_CLERK", + 2500, + {}, + std::make_unique(124), + 50}); + storage.replace(Employee{145, + "John", + "Russell", + "JRUSSEL", + "011.44.1344.429268", + "1-Oct-96", + "SA_MAN", + 14000, + std::make_unique(0.4), + std::make_unique(100), + 80}); + storage.replace(Employee{146, + "Karen", + "Partners", + "KPARTNER", + "011.44.1344.467268", + "5-Jan-97", + "SA_MAN", + 13500, + std::make_unique(0.3), + std::make_unique(100), + 80}); + storage.replace(Employee{147, + "Alberto", + "Errazuriz", + "AERRAZUR", + "011.44.1344.429278", + "10-Mar-97", + "SA_MAN", + 12000, + std::make_unique(0.3), + std::make_unique(100), + 80}); + storage.replace(Employee{148, + "Gerald", + "Cambrault", + "GCAMBRAU", + "011.44.1344.619268", + "15-Oct-99", + "SA_MAN", + 11000, + std::make_unique(0.3), + std::make_unique(100), + 80}); + storage.replace(Employee{149, + "Eleni", + "Zlotkey", + "EZLOTKEY", + "011.44.1344.429018", + "29-Jan-00", + "SA_MAN", + 10500, + std::make_unique(0.2), + std::make_unique(100), + 80}); + storage.replace(Employee{150, + "Peter", + "Tucker", + "PTUCKER", + "011.44.1344.129268", + "30-Jan-97", + "SA_REP", + 10000, + std::make_unique(0.3), + std::make_unique(145), + 80}); + storage.replace(Employee{151, + "David", + "Bernstein", + "DBERNSTE", + "011.44.1344.345268", + "24-Mar-97", + "SA_REP", + 9500, + std::make_unique(0.25), + std::make_unique(145), + 80}); + storage.replace(Employee{152, + "Peter", + "Hall", + "PHALL", + "011.44.1344.478968", + "20-Aug-97", + "SA_REP", + 9000, + std::make_unique(0.25), + std::make_unique(145), + 80}); + storage.replace(Employee{153, + "Christopher", + "Olsen", + "COLSEN", + "011.44.1344.498718", + "30-Mar-98", + "SA_REP", + 8000, + std::make_unique(0.2), + std::make_unique(145), + 80}); + storage.replace(Employee{154, + "Nanette", + "Cambrault", + "NCAMBRAU", + "011.44.1344.987668", + "9-Dec-98", + "SA_REP", + 7500, + std::make_unique(0.2), + std::make_unique(145), + 80}); + storage.replace(Employee{155, + "Oliver", + "Tuvault", + "OTUVAULT", + "011.44.1344.486508", + "23-Nov-99", + "SA_REP", + 7000, + std::make_unique(0.15), + std::make_unique(145), + 80}); + storage.replace(Employee{156, + "Janette", + "King", + "JKING", + "011.44.1345.429268", + "30-Jan-96", + "SA_REP", + 10000, + std::make_unique(0.35), + std::make_unique(146), + 80}); + storage.replace(Employee{157, + "Patrick", + "Sully", + "PSULLY", + "011.44.1345.929268", + "4-Mar-96", + "SA_REP", + 9500, + std::make_unique(0.35), + std::make_unique(146), + 80}); + storage.replace(Employee{158, + "Allan", + "McEwen", + "AMCEWEN", + "011.44.1345.829268", + "1-Aug-96", + "SA_REP", + 9000, + std::make_unique(0.35), + std::make_unique(146), + 80}); + storage.replace(Employee{159, + "Lindsey", + "Smith", + "LSMITH", + "011.44.1345.729268", + "10-Mar-97", + "SA_REP", + 8000, + std::make_unique(0.3), + std::make_unique(146), + 80}); + storage.replace(Employee{160, + "Louise", + "Doran", + "LDORAN", + "011.44.1345.629268", + "15-Dec-97", + "SA_REP", + 7500, + std::make_unique(0.3), + std::make_unique(146), + 80}); + storage.replace(Employee{161, + "Sarath", + "Sewall", + "SSEWALL", + "011.44.1345.529268", + "3-Nov-98", + "SA_REP", + 7000, + std::make_unique(0.25), + std::make_unique(146), + 80}); + storage.replace(Employee{162, + "Clara", + "Vishney", + "CVISHNEY", + "011.44.1346.129268", + "11-Nov-97", + "SA_REP", + 10500, + std::make_unique(0.25), + std::make_unique(147), + 80}); + storage.replace(Employee{163, + "Danielle", + "Greene", + "DGREENE", + "011.44.1346.229268", + "19-Mar-99", + "SA_REP", + 9500, + std::make_unique(0.15), + std::make_unique(147), + 80}); + storage.replace(Employee{164, + "Mattea", + "Marvins", + "MMARVINS", + "011.44.1346.329268", + "24-Jan-00", + "SA_REP", + 7200, + std::make_unique(0.1), + std::make_unique(147), + 80}); + storage.replace(Employee{165, + "David", + "Lee", + "DLEE", + "011.44.1346.529268", + "23-Feb-00", + "SA_REP", + 6800, + std::make_unique(0.1), + std::make_unique(147), + 80}); + storage.replace(Employee{166, + "Sundar", + "Ande", + "SANDE", + "011.44.1346.629268", + "24-Mar-00", + "SA_REP", + 6400, + std::make_unique(0.1), + std::make_unique(147), + 80}); + storage.replace(Employee{167, + "Amit", + "Banda", + "ABANDA", + "011.44.1346.729268", + "21-Apr-00", + "SA_REP", + 6200, + std::make_unique(0.1), + std::make_unique(147), + 80}); + storage.replace(Employee{168, + "Lisa", + "Ozer", + "LOZER", + "011.44.1343.929268", + "11-Mar-97", + "SA_REP", + 11500, + std::make_unique(0.25), + std::make_unique(148), + 80}); + storage.replace(Employee{169, + "Harrison", + "Bloom", + "HBLOOM", + "011.44.1343.829268", + "23-Mar-98", + "SA_REP", + 10000, + std::make_unique(0.2), + std::make_unique(148), + 80}); + storage.replace(Employee{170, + "Tayler", + "Fox", + "TFOX", + "011.44.1343.729268", + "24-Jan-98", + "SA_REP", + 9600, + std::make_unique(0.2), + std::make_unique(148), + 80}); + storage.replace(Employee{171, + "William", + "Smith", + "WSMITH", + "011.44.1343.629268", + "23-Feb-99", + "SA_REP", + 7400, + std::make_unique(0.15), + std::make_unique(148), + 80}); + storage.replace(Employee{172, + "Elizabeth", + "Bates", + "EBATES", + "011.44.1343.529268", + "24-Mar-99", + "SA_REP", + 7300, + std::make_unique(0.15), + std::make_unique(148), + 80}); + storage.replace(Employee{173, + "Sundita", + "Kumar", + "SKUMAR", + "011.44.1343.329268", + "21-Apr-00", + "SA_REP", + 6100, + std::make_unique(0.1), + std::make_unique(148), + 80}); + storage.replace(Employee{174, + "Ellen", + "Abel", + "EABEL", + "011.44.1644.429267", + "11-May-96", + "SA_REP", + 11000, + std::make_unique(0.3), + std::make_unique(149), + 80}); + storage.replace(Employee{175, + "Alyssa", + "Hutton", + "AHUTTON", + "011.44.1644.429266", + "19-Mar-97", + "SA_REP", + 8800, + std::make_unique(0.25), + std::make_unique(149), + 80}); + storage.replace(Employee{176, + "Jonathon", + "Taylor", + "JTAYLOR", + "011.44.1644.429265", + "24-Mar-98", + "SA_REP", + 8600, + std::make_unique(0.2), + std::make_unique(149), + 80}); + storage.replace(Employee{177, + "Jack", + "Livingston", + "JLIVINGS", + "011.44.1644.429264", + "23-Apr-98", + "SA_REP", + 8400, + std::make_unique(0.2), + std::make_unique(149), + 80}); + storage.replace(Employee{178, + "Kimberely", + "Grant", + "KGRANT", + "011.44.1644.429263", + "24-May-99", + "SA_REP", + 7000, + std::make_unique(0.15), + std::make_unique(149), + 80}); + storage.replace(Employee{179, + "Charles", + "Johnson", + "CJOHNSON", + "011.44.1644.429262", + "4-Jan-00", + "SA_REP", + 6200, + std::make_unique(0.1), + std::make_unique(149), + 80}); + storage.replace(Employee{180, + "Winston", + "Taylor", + "WTAYLOR", + "650.507.9876", + "24-Jan-98", + "SH_CLERK", + 3200, + {}, + std::make_unique(120), + 50}); + storage.replace(Employee{181, + "Jean", + "Fleaur", + "JFLEAUR", + "650.507.9877", + "23-Feb-98", + "SH_CLERK", + 3100, + {}, + std::make_unique(120), + 50}); + storage.replace(Employee{182, + "Martha", + "Sullivan", + "MSULLIVA", + "650.507.9878", + "21-Jun-99", + "SH_CLERK", + 2500, + {}, + std::make_unique(120), + 50}); + storage.replace(Employee{183, + "Girard", + "Geoni", + "GGEONI", + "650.507.9879", + "3-Feb-00", + "SH_CLERK", + 2800, + {}, + std::make_unique(120), + 50}); + storage.replace(Employee{184, + "Nandita", + "Sarchand", + "NSARCHAN", + "650.509.1876", + "27-Jan-96", + "SH_CLERK", + 4200, + {}, + std::make_unique(121), + 50}); + storage.replace(Employee{185, + "Alexis", + "Bull", + "ABULL", + "650.509.2876", + "20-Feb-97", + "SH_CLERK", + 4100, + {}, + std::make_unique(121), + 50}); + storage.replace(Employee{186, + "Julia", + "Dellinger", + "JDELLING", + "650.509.3876", + "24-Jun-98", + "SH_CLERK", + 3400, + {}, + std::make_unique(121), + 50}); + storage.replace(Employee{187, + "Anthony", + "Cabrio", + "ACABRIO", + "650.509.4876", + "7-Feb-99", + "SH_CLERK", + 3000, + {}, + std::make_unique(121), + 50}); + storage.replace(Employee{188, + "Kelly", + "Chung", + "KCHUNG", + "650.505.1876", + "14-Jun-97", + "SH_CLERK", + 3800, + {}, + std::make_unique(122), + 50}); + storage.replace(Employee{189, + "Jennifer", + "Dilly", + "JDILLY", + "650.505.2876", + "13-Aug-97", + "SH_CLERK", + 3600, + {}, + std::make_unique(122), + 50}); + storage.replace(Employee{190, + "Timothy", + "Gates", + "TGATES", + "650.505.3876", + "11-Jul-98", + "SH_CLERK", + 2900, + {}, + std::make_unique(122), + 50}); + storage.replace(Employee{191, + "Randall", + "Perkins", + "RPERKINS", + "650.505.4876", + "19-Dec-99", + "SH_CLERK", + 2500, + {}, + std::make_unique(122), + 50}); + storage.replace(Employee{192, + "Sarah", + "Bell", + "SBELL", + "650.501.1876", + "4-Feb-96", + "SH_CLERK", + 4000, + {}, + std::make_unique(123), + 50}); + storage.replace(Employee{193, + "Britney", + "Everett", + "BEVERETT", + "650.501.2876", + "3-Mar-97", + "SH_CLERK", + 3900, + {}, + std::make_unique(123), + 50}); + storage.replace(Employee{194, + "Samuel", + "McCain", + "SMCCAIN", + "650.501.3876", + "1-Jul-98", + "SH_CLERK", + 3200, + {}, + std::make_unique(123), + 50}); + storage.replace(Employee{195, + "Vance", + "Jones", + "VJONES", + "650.501.4876", + "17-Mar-99", + "SH_CLERK", + 2800, + {}, + std::make_unique(123), + 50}); + storage.replace(Employee{196, + "Alana", + "Walsh", + "AWALSH", + "650.507.9811", + "24-Apr-98", + "SH_CLERK", + 3100, + {}, + std::make_unique(124), + 50}); + storage.replace(Employee{197, + "Kevin", + "Feeney", + "KFEENEY", + "650.507.9822", + "23-May-98", + "SH_CLERK", + 3000, + {}, + std::make_unique(124), + 50}); + storage.replace(Employee{198, + "Donald", + "OConnell", + "DOCONNEL", + "650.507.9833", + "21-Jun-99", + "SH_CLERK", + 2600, + {}, + std::make_unique(124), + 50}); + storage.replace(Employee{199, + "Douglas", + "Grant", + "DGRANT", + "650.507.9844", + "13-Jan-00", + "SH_CLERK", + 2600, + {}, + std::make_unique(124), + 50}); + storage.replace(Employee{200, + "Jennifer", + "Whalen", + "JWHALEN", + "515.123.4444", + "17-Sep-87", + "AD_ASST", + 4400, + {}, + std::make_unique(101), + 10}); + storage.replace(Employee{201, + "Michael", + "Hartstein", + "MHARTSTE", + "515.123.5555", + "17-Feb-96", + "MK_MAN", + 13000, + {}, + std::make_unique(100), + 20}); + storage.replace(Employee{202, + "Pat", + "Fay", + "PFAY", + "603.123.6666", + "17-Aug-97", + "MK_REP", + 6000, + {}, + std::make_unique(201), + 20}); + storage.replace(Employee{203, + "Susan", + "Mavris", + "SMAVRIS", + "515.123.7777", + "7-Jun-94", + "HR_REP", + 6500, + {}, + std::make_unique(101), + 40}); + storage.replace(Employee{204, + "Hermann", + "Baer", + "HBAER", + "515.123.8888", + "7-Jun-94", + "PR_REP", + 10000, + {}, + std::make_unique(101), + 70}); + storage.replace(Employee{205, + "Shelley", + "Higgins", + "SHIGGINS", + "515.123.8080", + "7-Jun-94", + "AC_MGR", + 12000, + {}, + std::make_unique(101), + 110}); + storage.replace(Employee{206, + "William", + "Gietz", + "WGIETZ", + "515.123.8181", + "7-Jun-94", + "AC_ACCOUNT", + 8300, + {}, + std::make_unique(205), + 110}); + + 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 Relations", 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 Credit", 0, 1700}); + storage.replace(Department{150, "Shareholder Services", 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 Sales", 0, 1700}); + storage.replace(Department{250, "Retail Sales", 0, 1700}); + storage.replace(Department{260, "Recruiting", 0, 1700}); + storage.replace(Department{270, "Payroll", 0, 1700}); + + storage.replace(JobHistory{102, "1993-01-13", "1998-07-24", "IT_PROG", 60}); + storage.replace(JobHistory{101, "1989-09-21", "1993-10-27", "AC_ACCOUNT", 110}); + storage.replace(JobHistory{101, "1993-10-28", "1997-03-15", "AC_MGR", 110}); + storage.replace(JobHistory{201, "1996-02-17", "1999-12-19", "MK_REP", 20}); + storage.replace(JobHistory{114, "1998-03-24", "1999-12-31", "ST_CLERK", 50}); + storage.replace(JobHistory{122, "1999-01-01", "1999-12-31", "ST_CLERK", 50}); + storage.replace(JobHistory{200, "1987-09-17", "1993-06-17", "AD_ASST", 90}); + storage.replace(JobHistory{176, "1998-03-24", "1998-12-31", "SA_REP", 80}); + storage.replace(JobHistory{176, "1999-01-01", "1999-12-31", "SA_MAN", 80}); + storage.replace(JobHistory{200, "1994-07-01", "1998-12-31", "AC_ACCOUNT", 90}); + + { + // SELECT first_name, last_name, salary + // FROM employees + // WHERE salary >( + // SELECT salary + // FROM employees + // WHERE first_name='Alexander'); + auto rows = storage.select( + columns(&Employee::firstName, &Employee::lastName, &Employee::salary), + where(greater_than(&Employee::salary, + select(&Employee::salary, where(is_equal(&Employee::firstName, "Alexander")))))); + cout << "first_name last_name salary" << endl; + cout << "---------- ---------- ----------" << endl; + for(auto& row: rows) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << endl; + } + } + { + // SELECT employee_id,first_name,last_name,salary + // FROM employees + // WHERE salary > (SELECT AVG(SALARY) FROM employees); + auto rows = storage.select(columns(&Employee::id, &Employee::firstName, &Employee::lastName, &Employee::salary), + where(greater_than(&Employee::salary, select(avg(&Employee::salary))))); + cout << "employee_id first_name last_name salary" << endl; + cout << "----------- ---------- ---------- ----------" << endl; + 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; + } + } + { + // SELECT first_name, last_name, department_id + // FROM employees + // WHERE department_id IN + // (SELECT DEPARTMENT_ID FROM departments + // WHERE location_id=1700); + auto rows = storage.select( + columns(&Employee::firstName, &Employee::lastName, &Employee::departmentId), + from(), + 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) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << endl; + } + } + { + + // SELECT first_name, last_name, department_id + // FROM employees + // WHERE department_id NOT IN + // (SELECT DEPARTMENT_ID FROM departments + // WHERE manager_id + // BETWEEN 100 AND 200); + auto rows = + storage.select(columns(&Employee::firstName, &Employee::lastName, &Employee::departmentId), + from(), + where(not_in(&Employee::departmentId, + select(&Department::id, where(between(&Department::managerId, 100, 200)))))); + cout << "first_name last_name department_id" << endl; + cout << "---------- ---------- -------------" << endl; + for(auto& row: rows) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << endl; + } + } + { + // SELECT last_name, salary, department_id + // FROM employees e + // WHERE salary >(SELECT AVG(salary) + // FROM employees + // WHERE department_id = e.department_id); + using als = alias_e; + auto rows = storage.select( + columns(alias_column(&Employee::lastName), + alias_column(&Employee::salary), + alias_column(&Employee::departmentId)), + from(), + where(greater_than( + alias_column(&Employee::salary), + select(avg(&Employee::salary), + from(), + where(is_equal(&Employee::departmentId, alias_column(&Employee::departmentId))))))); + cout << "last_name salary department_id" << endl; + cout << "---------- ---------- -------------" << endl; + for(auto& row: rows) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << endl; + } + } + { + // SELECT first_name, last_name, employee_id, job_id + // FROM employees + // WHERE 1 <= + // (SELECT COUNT(*) FROM Job_history + // WHERE employee_id = employees.employee_id); + auto rows = + storage.select(columns(&Employee::firstName, &Employee::lastName, &Employee::id, &Employee::jobId), + from(), + where(lesser_or_equal(1, + select(count(), + from(), + where(is_equal(&Employee::id, &JobHistory::employeeId)))))); + cout << "first_name last_name employee_id job_id" << endl; + cout << "---------- ---------- ----------- ----------" << endl; + 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; + } + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/synchronous.cpp b/libs/sqlite_orm-1.8.2/examples/synchronous.cpp new file mode 100644 index 0000000..6147eff --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/synchronous.cpp @@ -0,0 +1,33 @@ + +#include +#include + +struct Query { + std::string src_ip; + uint16_t src_port; + uint16_t txn_id; + uint32_t tv_sec; + uint32_t tv_usec; + std::string name; + uint16_t type; +}; + +int main(int, char**) { + + using namespace sqlite_orm; + + auto storage = make_storage("synchronous.sqlite", + make_table("queries", + make_column("tv_sec", &Query::tv_sec), + make_column("tv_usec", &Query::tv_usec), + make_column("name", &Query::name), + make_column("type", &Query::type), + make_column("src_ip", &Query::src_ip), + make_column("src_port", &Query::src_port), + make_column("txn_id", &Query::txn_id))); + storage.sync_schema(); + storage.pragma.synchronous(0); + storage.remove_all(); + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/triggers.cpp b/libs/sqlite_orm-1.8.2/examples/triggers.cpp new file mode 100644 index 0000000..d2a2c2c --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/triggers.cpp @@ -0,0 +1,193 @@ +/** + * This example was grabbed from here https://www.sqlitetutorial.net/sqlite-trigger/ + */ + +#include +#include +#include + +using namespace sqlite_orm; +using std::cout; +using std::endl; + +struct Lead { + int id = 0; + std::string firstName; + std::string lastName; + std::string email; + std::string phone; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Lead() = default; + Lead(int id, std::string firstName, std::string lastName, std::string email, std::string phone) : + id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, email{std::move(email)}, + phone{std::move(phone)} {} +#endif +}; + +struct LeadLog { + int id = 0; + int oldId = 0; + int newId = 0; + std::string oldPhone; + std::string newPhone; + std::string oldEmail; + std::string newEmail; + std::string userAction; + std::string createdAt; +}; + +int main() { + auto storage = make_storage("", + + // CREATE TRIGGER validate_email_before_insert_leads + // BEFORE INSERT ON leads + // BEGIN + // SELECT + // CASE + // WHEN NEW.email NOT LIKE '%_@__%.__%' THEN + // RAISE (ABORT,'Invalid email address') + // END; + // END; + make_trigger("validate_email_before_insert_leads", + before() + .insert() + .on() + .begin(select(case_() + .when(not like(new_(&Lead::email), "%_@__%.__%"), + then(raise_abort("Invalid email address"))) + .end())) + .end()), + + // CREATE TRIGGER log_contact_after_update + // AFTER UPDATE ON leads + // WHEN old.phone <> new.phone + // OR old.email <> new.email + // BEGIN + // INSERT INTO lead_logs ( + // old_id, + // new_id, + // old_phone, + // new_phone, + // old_email, + // new_email, + // user_action, + // created_at + // ) + // VALUES + // ( + // old.id, + // new.id, + // old.phone, + // new.phone, + // old.email, + // new.email, + // 'UPDATE', + // DATETIME('NOW') + // ) ; + // END; + make_trigger("log_contact_after_update", + after() + .update() + .on() + .when(is_not_equal(old(&Lead::phone), new_(&Lead::phone)) and + is_not_equal(old(&Lead::email), new_(&Lead::email))) + .begin(insert(into(), + columns(&LeadLog::oldId, + &LeadLog::newId, + &LeadLog::oldPhone, + &LeadLog::newPhone, + &LeadLog::oldEmail, + &LeadLog::newEmail, + &LeadLog::userAction, + &LeadLog::createdAt), + values(std::make_tuple(old(&Lead::id), + new_(&Lead::id), + old(&Lead::phone), + new_(&Lead::phone), + old(&Lead::email), + new_(&Lead::email), + "UPDATE", + datetime("NOW"))))) + .end()), + + // CREATE TABLE leads ( + // id integer PRIMARY KEY, + // first_name text NOT NULL, + // last_name text NOT NULL, + // email text NOT NULL, + // phone text NOT NULL + // ); + make_table("leads", + make_column("id", &Lead::id, primary_key()), + make_column("first_name", &Lead::firstName), + make_column("last_name", &Lead::lastName), + make_column("email", &Lead::email), + make_column("phone", &Lead::phone)), + + // CREATE TABLE lead_logs ( + // id INTEGER PRIMARY KEY, + // old_id int, + // new_id int, + // old_phone text, + // new_phone text, + // old_email text, + // new_email text, + // user_action text, + // created_at text + // ); + make_table("lead_logs", + make_column("id", &LeadLog::id, primary_key()), + make_column("old_id", &LeadLog::oldId), + make_column("new_id", &LeadLog::newId), + make_column("old_phone", &LeadLog::oldPhone), + make_column("new_phone", &LeadLog::newPhone), + make_column("old_email", &LeadLog::oldEmail), + make_column("new_email", &LeadLog::newEmail), + make_column("user_action", &LeadLog::userAction), + make_column("created_at", &LeadLog::createdAt))); + storage.sync_schema(); + + // Insert a row with an invalid email into the leads table: + // + // INSERT INTO leads (first_name, last_name, email, phone) + // VALUES('John', 'Doe', 'jjj', '4089009334'); + try { + storage.insert(Lead{0, "John", "Doe", "jjj", "4089009334"}); + } catch(const std::system_error& systemError) { + cout << "error: " << systemError.what() << endl; + } + + // Insert a row with a valid email. + // INSERT INTO leads (first_name, last_name, email, phone) + // VALUES ('John', 'Doe', 'john.doe@sqlitetutorial.net', '4089009334'); + storage.insert(Lead{0, "John", "Doe", "john.doe@sqlitetutorial.net", "4089009334"}); + + cout << "Leads:" << endl; + for(auto& lead: storage.iterate()) { + cout << storage.dump(lead) << endl; + } + + // UPDATE leads + // SET last_name = 'Smith' + // WHERE id = 1; + storage.update_all(set(c(&Lead::lastName) = "Smith"), where(c(&Lead::id) == 1)); + + cout << "Logs count = " << storage.count() << endl; + + // UPDATE leads + // SET + // phone = '4089998888', + // email = 'john.smith@sqlitetutorial.net' + // WHERE id = 1; + storage.update_all(set(c(&Lead::phone) = "4089998888", c(&Lead::email) = "john.smith@sqlitetutorial.net"), + where(c(&Lead::id) == 1)); + + cout << "Logs count = " << storage.count() << endl; + + for(auto& leadLog: storage.iterate()) { + cout << storage.dump(leadLog) << endl; + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/union.cpp b/libs/sqlite_orm-1.8.2/examples/union.cpp new file mode 100644 index 0000000..4c5b588 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/union.cpp @@ -0,0 +1,113 @@ +/** + * This example was grabbed from here https://www.tutorialspoint.com/sqlite/sqlite_unions_clause.htm + */ + +#include +#include +#include +#include +#include + +using std::cout; +using std::endl; + +int main() { + + struct Employee { + int id; + std::string name; + int age; + std::string address; + double salary; + }; + + struct Department { + int id; + std::string dept; + int employeeId; + }; + + using namespace sqlite_orm; + + auto storage = make_storage("union.sqlite", + 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)), + make_table("DEPARTMENT", + make_column("ID", &Department::id, primary_key()), + make_column("DEPT", &Department::dept), + make_column("EMP_ID", &Department::employeeId), + foreign_key(&Department::employeeId).references(&Employee::id))); + storage.sync_schema(); + + // order matters + storage.remove_all(); + storage.remove_all(); + + storage.replace(Employee{1, "Paul", 32, "California", 20000.0}); + storage.replace(Employee{2, "Allen", 25, "Texas", 15000.0}); + storage.replace(Employee{3, "Teddy", 23, "Norway", 20000.0}); + storage.replace(Employee{4, "Mark", 25, "Rich-Mond", 65000.0}); + storage.replace(Employee{5, "David", 27, "Texas", 85000.0}); + storage.replace(Employee{6, "Kim", 22, "South-Hall", 45000.0}); + storage.replace(Employee{7, "James", 24, "Houston", 10000.0}); + + storage.replace(Department{1, "IT Billing", 1}); + storage.replace(Department{2, "Engineering", 2}); + storage.replace(Department{3, "Finance", 7}); + storage.replace(Department{4, "Engineering", 3}); + storage.replace(Department{5, "Finance", 4}); + storage.replace(Department{6, "Engineering", 5}); + storage.replace(Department{7, "Finance", 6}); + + { + // SELECT EMP_ID, NAME, DEPT + // FROM COMPANY + // INNER JOIN DEPARTMENT + // ON COMPANY.ID = DEPARTMENT.EMP_ID + // UNION + // SELECT EMP_ID, NAME, DEPT + // FROM COMPANY + // LEFT OUTER JOIN DEPARTMENT + // ON COMPANY.ID = DEPARTMENT.EMP_ID; + auto rows = storage.select( + union_(select(columns(&Department::employeeId, &Employee::name, &Department::dept), + 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)))))); + + assert(rows.size() == 7); + std::sort(rows.begin(), rows.end(), [](auto& lhs, auto& rhs) { + return std::get<0>(lhs) < std::get<0>(rhs); + }); + for(auto& row: rows) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << endl; + } + cout << endl; + } + { + // SELECT EMP_ID, NAME, DEPT + // FROM COMPANY + // INNER JOIN DEPARTMENT + // ON COMPANY.ID = DEPARTMENT.EMP_ID + // UNION ALL + // SELECT EMP_ID, NAME, DEPT + // FROM COMPANY + // LEFT OUTER JOIN DEPARTMENT + // ON COMPANY.ID = DEPARTMENT.EMP_ID; + auto rows = storage.select( + union_all(select(columns(&Department::employeeId, &Employee::name, &Department::dept), + 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) { + cout << std::get<0>(row) << '\t' << std::get<1>(row) << '\t' << std::get<2>(row) << endl; + } + cout << endl; + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/unique.cpp b/libs/sqlite_orm-1.8.2/examples/unique.cpp new file mode 100644 index 0000000..aa477bb --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/unique.cpp @@ -0,0 +1,42 @@ + +#include +#include +#include +#include + +using std::cerr; +using std::cout; +using std::endl; + +struct Entry { + int id; + std::string uniqueColumn; + std::unique_ptr nullableColumn; +}; + +int main(int, char**) { + using namespace sqlite_orm; + auto storage = make_storage("unique.sqlite", + make_table("unique_test", + make_column("id", &Entry::id, primary_key().autoincrement()), + make_column("unique_text", &Entry::uniqueColumn, unique()), + make_column("nullable_text", &Entry::nullableColumn))); + storage.sync_schema(); + storage.remove_all(); + + try { + auto sameString = "Bebe Rexha"; + + auto id1 = storage.insert(Entry{0, sameString, std::make_unique("The way I are")}); + cout << "inserted " << storage.dump(storage.get(id1)) << endl; + + // it's ok but the next line will throw std::system_error + + 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) { + cerr << e.what() << endl; + } + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/update.cpp b/libs/sqlite_orm-1.8.2/examples/update.cpp new file mode 100644 index 0000000..ed3a273 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/update.cpp @@ -0,0 +1,80 @@ +/** + * Example implemented from here https://www.tutorialspoint.com/sqlite/sqlite_update_query.htm + */ + +#include +#include +#include + +using std::cout; +using std::endl; + +struct Employee { + int id; + std::string name; + int age; + std::string address; + double salary; +}; + +inline auto initStorage(const std::string& path) { + using namespace sqlite_orm; + return make_storage(path, + 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))); +} + +using Storage = decltype(initStorage("")); + +static std::unique_ptr stor; + +int main(int, char**) { + stor = std::make_unique(initStorage("update.sqlite")); + stor->sync_schema(); + stor->remove_all(); + + // insert initial values + stor->replace(Employee{1, "Paul", 32, "California", 20000.0}); + stor->replace(Employee{2, "Allen", 25, "Texas", 15000.0}); + stor->replace(Employee{3, "Teddy", 23, "Norway", 20000.0}); + stor->replace(Employee{4, "Mark", 25, "Rich-Mond", 65000.0}); + stor->replace(Employee{5, "David", 27, "Texas", 85000.0}); + stor->replace(Employee{6, "Kim", 22, "South-Hall", 45000.0}); + stor->replace(Employee{7, "James", 24, "Houston", 10000.0}); + + // show 'COMPANY' table contents + for(auto& employee: stor->iterate()) { + cout << stor->dump(employee) << endl; + } + cout << endl; + + // 'UPDATE COMPANY SET ADDRESS = 'Texas' WHERE ID = 6' + + auto employee6 = stor->get(6); + employee6.address = "Texas"; + stor->update( + 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()) { + cout << stor->dump(employee) << endl; + } + cout << endl; + + // 'UPDATE COMPANY SET ADDRESS = 'Texas', SALARY = 20000.00 WHERE AGE < 30' + using namespace sqlite_orm; + stor->update_all(set(c(&Employee::address) = "Texas", c(&Employee::salary) = 20000.00), + where(c(&Employee::age) < 30)); + + // show 'COMPANY' table contents one more time + for(auto& employee: stor->iterate()) { + cout << stor->dump(employee) << endl; + } + cout << endl; + + return 0; +} diff --git a/libs/sqlite_orm-1.8.2/examples/user_defined_functions.cpp b/libs/sqlite_orm-1.8.2/examples/user_defined_functions.cpp new file mode 100644 index 0000000..34bb30c --- /dev/null +++ b/libs/sqlite_orm-1.8.2/examples/user_defined_functions.cpp @@ -0,0 +1,171 @@ +/** + * 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; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Table() = default; + Table(int a, int b, int c) : a{a}, b{b}, c{c} {} +#endif + }; + + 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/libs/sqlite_orm-1.8.2/include/sqlite_orm/sqlite_orm.h b/libs/sqlite_orm-1.8.2/include/sqlite_orm/sqlite_orm.h new file mode 100644 index 0000000..ae74309 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/include/sqlite_orm/sqlite_orm.h @@ -0,0 +1,19505 @@ +#pragma once + +#if defined(_MSC_VER) +__pragma(push_macro("min")) +#undef min +__pragma(push_macro("max")) +#undef max +#endif // defined(_MSC_VER) +#pragma once + +// #include "cxx_universal.h" + +/* + * This header makes central C++ functionality on which sqlite_orm depends universally available: + * - alternative operator representations + * - ::size_t, ::ptrdiff_t, ::nullptr_t + * - C++ core language feature macros + * - macros for dealing with compiler quirks + */ + +#include // alternative operator representations +#include // sqlite_orm is using size_t, ptrdiff_t, nullptr_t everywhere, pull it in early + +// earlier clang versions didn't make nullptr_t available in the global namespace via stddef.h, +// though it should have according to C++ documentation (see https://en.cppreference.com/w/cpp/types/nullptr_t#Notes). +// actually it should be available when including stddef.h +using std::nullptr_t; + +// #include "cxx_core_features.h" + +/* + * This header detects core C++ language features on which sqlite_orm depends. + * May be updated/overwritten by cxx_compiler_quirks.h + */ + +#ifdef __has_cpp_attribute +#define SQLITE_ORM_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr) +#else +#define SQLITE_ORM_HAS_CPP_ATTRIBUTE(attr) 0L +#endif + +#ifdef __has_include +#define SQLITE_ORM_HAS_INCLUDE(file) __has_include(file) +#else +#define SQLITE_ORM_HAS_INCLUDE(file) 0L +#endif + +#if __cpp_aggregate_nsdmi >= 201304L +#define SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED +#endif + +#if __cpp_constexpr >= 201304L +#define SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED +#endif + +#if __cpp_noexcept_function_type >= 201510L +#define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED +#endif + +#if __cpp_aggregate_bases >= 201603L +#define SQLITE_ORM_AGGREGATE_BASES_SUPPORTED +#endif + +#if __cpp_fold_expressions >= 201603L +#define SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED +#endif + +#if __cpp_inline_variables >= 201606L +#define SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +#endif + +#if __cpp_if_constexpr >= 201606L +#define SQLITE_ORM_IF_CONSTEXPR_SUPPORTED +#endif + +#if __cpp_inline_variables >= 201606L +#define SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +#endif + +#if __cpp_generic_lambdas >= 201707L +#define SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED +#else +#endif + +#if __cpp_consteval >= 201811L +#define SQLITE_ORM_CONSTEVAL_SUPPORTED +#endif + +#if __cpp_aggregate_paren_init >= 201902L +#define SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED +#endif + +#if __cpp_concepts >= 201907L +#define SQLITE_ORM_CONCEPTS_SUPPORTED +#endif + +// #include "cxx_compiler_quirks.h" + +/* + * This header defines macros for circumventing compiler quirks on which sqlite_orm depends. + * May amend cxx_core_features.h + */ + +#ifdef __clang__ +#define SQLITE_ORM_DO_PRAGMA(...) _Pragma(#__VA_ARGS__) +#endif + +#ifdef __clang__ +#define SQLITE_ORM_CLANG_SUPPRESS(warnoption, ...) \ + SQLITE_ORM_DO_PRAGMA(clang diagnostic push) \ + SQLITE_ORM_DO_PRAGMA(clang diagnostic ignored warnoption) \ + __VA_ARGS__ \ + SQLITE_ORM_DO_PRAGMA(clang diagnostic pop) + +#else +#define SQLITE_ORM_CLANG_SUPPRESS(warnoption, ...) __VA_ARGS__ +#endif + +// clang has the bad habit of diagnosing missing brace-init-lists when constructing aggregates with base classes. +// This is a false positive, since the C++ standard is quite clear that braces for nested or base objects may be omitted, +// see https://en.cppreference.com/w/cpp/language/aggregate_initialization: +// "The braces around the nested initializer lists may be elided (omitted), +// in which case as many initializer clauses as necessary are used to initialize every member or element of the corresponding subaggregate, +// and the subsequent initializer clauses are used to initialize the following members of the object." +// In this sense clang should only warn about missing field initializers. +// Because we know what we are doing, we suppress the diagnostic message +#define SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(...) SQLITE_ORM_CLANG_SUPPRESS("-Wmissing-braces", __VA_ARGS__) + +#if defined(_MSC_VER) && (_MSC_VER < 1920) +#define SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION +#endif + +// clang 10 chokes on concepts that don't depend on template parameters; +// when it tries to instantiate an expression in a requires expression, which results in an error, +// the compiler reports an error instead of dismissing the templated function. +#if defined(SQLITE_ORM_CONCEPTS_SUPPORTED) && (defined(__clang__) && (__clang_major__ == 10)) +#define SQLITE_ORM_BROKEN_NONTEMPLATE_CONCEPTS +#endif + +#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +#define SQLITE_ORM_INLINE_VAR inline +#else +#define SQLITE_ORM_INLINE_VAR +#endif + +#if SQLITE_ORM_HAS_CPP_ATTRIBUTE(no_unique_address) >= 201803L +#define SQLITE_ORM_NOUNIQUEADDRESS [[no_unique_address]] +#else +#define SQLITE_ORM_NOUNIQUEADDRESS +#endif + +#ifdef SQLITE_ORM_CONSTEVAL_SUPPORTED +#define SQLITE_ORM_CONSTEVAL consteval +#else +#define SQLITE_ORM_CONSTEVAL constexpr +#endif +#pragma once + +#include // std::enable_if, std::is_same + +// #include "functional/cxx_type_traits_polyfill.h" + +#include + +// #include "cxx_universal.h" + +namespace sqlite_orm { + namespace internal { + namespace polyfill { +#if __cpp_lib_void_t >= 201411L + using std::void_t; +#else + template + using void_t = void; +#endif + +#if __cpp_lib_bool_constant >= 201505L + using std::bool_constant; +#else + template + using bool_constant = std::integral_constant; +#endif + +#if __cpp_lib_logical_traits >= 201510L && __cpp_lib_type_trait_variable_templates >= 201510L + using std::conjunction; + using std::conjunction_v; + using std::disjunction; + using std::disjunction_v; + using std::negation; + using std::negation_v; +#else + template + struct conjunction : std::true_type {}; + template + struct conjunction : B1 {}; + template + struct conjunction : std::conditional_t, B1> {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool conjunction_v = conjunction::value; + + template + struct disjunction : std::false_type {}; + template + struct disjunction : B1 {}; + template + struct disjunction : std::conditional_t> {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool disjunction_v = disjunction::value; + + template + struct negation : bool_constant {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool negation_v = negation::value; +#endif + +#if __cpp_lib_remove_cvref >= 201711L + using std::remove_cvref, std::remove_cvref_t; +#else + template + struct remove_cvref : std::remove_cv> {}; + + template + using remove_cvref_t = typename remove_cvref::type; +#endif + +#if __cpp_lib_type_identity >= 201806L + using std::type_identity, std::type_identity_t; +#else + template + struct type_identity { + using type = T; + }; + + template + using type_identity_t = typename type_identity::type; +#endif + +#if 0 // __cpp_lib_detect >= 0L // library fundamentals TS v2, [meta.detect] + using std::nonesuch; + using std::detector; + using std::is_detected, std::is_detected_v; + using std::detected, std::detected_t; + using std::detected_or, std::detected_or_t; +#else + struct nonesuch { + ~nonesuch() = delete; + nonesuch(const nonesuch&) = delete; + void operator=(const nonesuch&) = delete; + }; + + template class Op, class... Args> + struct detector { + using value_t = std::false_type; + using type = Default; + }; + + template class Op, class... Args> + struct detector>, Op, Args...> { + using value_t = std::true_type; + using type = Op; + }; + + template class Op, class... Args> + using is_detected = typename detector::value_t; + + template class Op, class... Args> + using detected = detector; + + template class Op, class... Args> + using detected_t = typename detector::type; + + template class Op, class... Args> + using detected_or = detector; + + template class Op, class... Args> + using detected_or_t = typename detected_or::type; + + template class Op, class... Args> + SQLITE_ORM_INLINE_VAR constexpr bool is_detected_v = is_detected::value; +#endif + +#if 0 // proposed but not pursued + using std::is_specialization_of, std::is_specialization_of_t, std::is_specialization_of_v; +#else + // is_specialization_of: https://github.com/cplusplus/papers/issues/812 + + template class Primary> + SQLITE_ORM_INLINE_VAR constexpr bool is_specialization_of_v = false; + + template class Primary, class... Types> + SQLITE_ORM_INLINE_VAR constexpr bool is_specialization_of_v, Primary> = true; + + template class Primary> + struct is_specialization_of : bool_constant> {}; +#endif + + template + SQLITE_ORM_INLINE_VAR constexpr bool always_false_v = false; + + template + using index_constant = std::integral_constant; + } + } + + namespace polyfill = internal::polyfill; +} + +namespace sqlite_orm { + // C++ generic traits used throughout the library + namespace internal { + template + using is_any_of = polyfill::disjunction...>; + + // enable_if for types + template class Op, class... Args> + using match_if = std::enable_if_t::value>; + + // enable_if for types + template class Op, class... Args> + using match_if_not = std::enable_if_t>>; + + // enable_if for types + template class Primary> + using match_specialization_of = std::enable_if_t>; + + // enable_if for functions + template class Op, class... Args> + using satisfies = std::enable_if_t::value, bool>; + + // enable_if for functions + template class Op, class... Args> + using satisfies_not = std::enable_if_t>::value, bool>; + + // enable_if for functions + template class Primary> + using satisfies_is_specialization_of = std::enable_if_t, bool>; + } + + // type name template aliases for syntactic sugar + namespace internal { + template + using type_t = typename T::type; + + template + using field_type_t = typename T::field_type; + + template + using constraints_type_t = typename T::constraints_type; + + template + using object_type_t = typename T::object_type; + + template + using elements_type_t = typename T::elements_type; + + template + using target_type_t = typename T::target_type; + + template + using on_type_t = typename T::on_type; + } +} +#pragma once + +#include +#include // std::error_code, std::system_error +#include // std::string +#include +#include // std::ostringstream +#include + +namespace sqlite_orm { + + /** @short Enables classifying sqlite error codes. + + @note We don't bother listing all possible values; + this also allows for compatibility with + 'Construction rules for enum class values (P0138R2)' + */ + enum class sqlite_errc {}; + + enum class orm_error_code { + not_found = 1, + type_is_not_mapped_to_storage, + trying_to_dereference_null_iterator, + too_many_tables_specified, + incorrect_set_fields_specified, + column_not_found, + table_has_no_primary_key_column, + cannot_start_a_transaction_within_a_transaction, + no_active_transaction, + incorrect_journal_mode_string, + invalid_collate_argument_enum, + 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, + value_is_null, + no_tables_specified, + }; + +} + +namespace std { + template<> + struct is_error_code_enum<::sqlite_orm::sqlite_errc> : true_type {}; + + template<> + struct is_error_code_enum<::sqlite_orm::orm_error_code> : true_type {}; +} + +namespace sqlite_orm { + + class orm_error_category : public std::error_category { + public: + const char* name() const noexcept override final { + return "ORM error"; + } + + std::string message(int c) const override final { + switch(static_cast(c)) { + case orm_error_code::not_found: + return "Not found"; + case orm_error_code::type_is_not_mapped_to_storage: + return "Type is not mapped to storage"; + case orm_error_code::trying_to_dereference_null_iterator: + return "Trying to dereference null iterator"; + case orm_error_code::too_many_tables_specified: + return "Too many tables specified"; + case orm_error_code::incorrect_set_fields_specified: + return "Incorrect set fields specified"; + case orm_error_code::column_not_found: + return "Column not found"; + case orm_error_code::table_has_no_primary_key_column: + return "Table has no primary key column"; + case orm_error_code::cannot_start_a_transaction_within_a_transaction: + return "Cannot start a transaction within a transaction"; + case orm_error_code::no_active_transaction: + return "No active transaction"; + case orm_error_code::invalid_collate_argument_enum: + return "Invalid collate_argument enum"; + case orm_error_code::failed_to_init_a_backup: + return "Failed to init a backup"; + case orm_error_code::unknown_member_value: + 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"; + case orm_error_code::value_is_null: + return "Value is null"; + case orm_error_code::no_tables_specified: + return "No tables specified"; + default: + return "unknown error"; + } + } + }; + + class sqlite_error_category : public std::error_category { + public: + const char* name() const noexcept override final { + return "SQLite error"; + } + + std::string message(int c) const override final { + return sqlite3_errstr(c); + } + }; + + 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() { + static sqlite_error_category res; + return res; + } + + inline std::error_code make_error_code(sqlite_errc ev) noexcept { + return {static_cast(ev), get_sqlite_error_category()}; + } + + inline std::error_code make_error_code(orm_error_code ev) noexcept { + return {static_cast(ev), get_orm_error_category()}; + } + + template + 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)...}); + stream << sqlite3_errmsg(db); + return stream.str(); + } + + template + [[noreturn]] void throw_error(sqlite3* db, T&&... args) { + throw std::system_error{sqlite_errc(sqlite3_errcode(db)), get_error_message(db, std::forward(args)...)}; + } + + inline std::system_error sqlite_to_system_error(int ev) { + return {sqlite_errc(ev)}; + } + + inline std::system_error sqlite_to_system_error(sqlite3* db) { + return {sqlite_errc(sqlite3_errcode(db)), sqlite3_errmsg(db)}; + } + + [[noreturn]] inline void throw_translated_sqlite_error(int ev) { + throw sqlite_to_system_error(ev); + } + + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3* db) { + throw sqlite_to_system_error(db); + } + + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt* stmt) { + throw sqlite_to_system_error(sqlite3_db_handle(stmt)); + } +} +#pragma once + +#include // std::string +#include // std::shared_ptr, std::unique_ptr +#include // std::vector +// #include "functional/cxx_optional.h" + +// #include "cxx_core_features.h" + +#if SQLITE_ORM_HAS_INCLUDE() +#include +#endif + +#if __cpp_lib_optional >= 201606L +#define SQLITE_ORM_OPTIONAL_SUPPORTED +#endif + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + +// #include "is_std_ptr.h" + +#include +#include + +namespace sqlite_orm { + + /** + * Specialization for optional type (std::shared_ptr / std::unique_ptr). + */ + template + struct is_std_ptr : std::false_type {}; + + template + struct is_std_ptr> : std::true_type { + using element_type = typename std::shared_ptr::element_type; + + static std::shared_ptr make(std::remove_cv_t&& v) { + return std::make_shared(std::move(v)); + } + }; + + template + struct is_std_ptr> : std::true_type { + using element_type = typename std::unique_ptr::element_type; + + static auto make(std::remove_cv_t&& v) { + return std::make_unique(std::move(v)); + } + }; +} + +namespace sqlite_orm { + + /** + * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) + */ + template + struct type_printer {}; + + struct integer_printer { + const std::string& print() const { + static const std::string res = "INTEGER"; + return res; + } + }; + + struct text_printer { + const std::string& print() const { + static const std::string res = "TEXT"; + return res; + } + }; + + struct real_printer { + const std::string& print() const { + static const std::string res = "REAL"; + return res; + } + }; + + struct blob_printer { + const std::string& print() const { + static const std::string res = "BLOB"; + return res; + } + }; + + // Note: char, unsigned/signed char are used for storing integer values, not char values. + template + struct type_printer>, + std::is_integral>>> : integer_printer { + }; + + template + struct type_printer::value>> : real_printer {}; + + template + struct type_printer, + std::is_base_of, + std::is_base_of>>> : text_printer {}; + + template + struct type_printer::value>> : type_printer {}; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct type_printer>> + : type_printer {}; +#endif + + template<> + struct type_printer, void> : blob_printer {}; +} +#pragma once + +namespace sqlite_orm { + + namespace internal { + + enum class collate_argument { + binary, + nocase, + rtrim, + }; + } + +} +#pragma once + +#include // std::system_error +#include // std::ostream +#include // std::string +#include // std::tuple, std::make_tuple +#include // std::is_base_of, std::false_type, std::true_type + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/mpl.h" + +/* + * Symbols for 'template metaprogramming' (compile-time template programming), + * inspired by the MPL of Aleksey Gurtovoy and David Abrahams. + * + * Currently, the focus is on facilitating advanced type filtering, + * such as filtering columns by constraints having various traits. + * Hence it contains only a very small subset of a full MPL. + * + * Two key concepts are critical to understanding: + * 1. A 'metafunction' is a class template that represents a function invocable at compile-time. + * 2. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming. + * More precisely, it's a class with a nested metafunction called "fn" + * Correspondingly, a metafunction class invocation is defined as invocation of its nested "fn" metafunction. + * 3. A 'metafunction operation' is an alias template that represents a function whose instantiation already yields a type. + * + * Conventions: + * - "Fn" is the name for a metafunction template template parameter. + * - "FnCls" is the name for a metafunction class template parameter. + * - "_fn" is a suffix for a type that accepts metafunctions and turns them into metafunction classes. + * - "higher order" denotes a metafunction that operates on another metafunction (i.e. takes it as an argument). + */ + +#include // std::false_type, std::true_type + +// #include "cxx_universal.h" + +// #include "cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { + namespace mpl { + template class Fn> + struct indirectly_test_metafunction; + + /* + * Determines whether a class template has a nested metafunction `fn`. + * + * Implementation note: the technique of specialiazing on the inline variable must come first because + * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_metafunction_class_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_metafunction_class_v>> = + true; + + template + struct is_metafunction_class : polyfill::bool_constant> {}; + + /* + * Invoke metafunction. + */ + template class Fn, class... Args> + using invoke_fn_t = typename Fn::type; + +#ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template class Op, class... Args> + struct wrap_op { + using type = Op; + }; + + /* + * Invoke metafunction operation. + * + * Note: legacy compilers need an extra layer of indirection, otherwise type replacement may fail + * if alias template `Op` has a dependent expression in it. + */ + template class Op, class... Args> + using invoke_op_t = typename wrap_op::type; +#else + /* + * Invoke metafunction operation. + */ + template class Op, class... Args> + using invoke_op_t = Op; +#endif + + /* + * Invoke metafunction class by invoking its nested metafunction. + */ + template + using invoke_t = typename FnCls::template fn::type; + + /* + * Instantiate metafunction class' nested metafunction. + */ + template + using instantiate = typename FnCls::template fn; + + /* + * Wrap given type such that `typename T::type` is valid. + */ + template + struct type_wrap : polyfill::type_identity {}; + template + struct type_wrap> : T {}; + + /* + * Turn metafunction into a metafunction class. + * + * Invocation of the nested metafunction `fn` is SFINAE-friendly (detection idiom). + * This is necessary because `fn` is a proxy to the originally quoted metafunction, + * and the instantiation of the metafunction might be an invalid expression. + */ + template class Fn> + struct quote_fn { + template class, class...> + struct invoke_fn; + + template class F, class... Args> + struct invoke_fn>, F, Args...> { + using type = type_wrap>; + }; + + template + using fn = typename invoke_fn::type; + }; + + /* + * Indirection wrapper for higher-order metafunctions, + * specialized on the argument indexes where metafunctions appear. + */ + template + struct higherorder; + + template<> + struct higherorder<0u> { + /* + * Turn higher-order metafunction into a metafunction class. + */ + template class Fn, class... Args2> class HigherFn> + struct quote_fn { + template + struct fn : HigherFn {}; + }; + }; + + /* + * Metafunction class that extracts the nested metafunction of its metafunction class argument, + * quotes the extracted metafunction and passes it on to the next metafunction class + * (kind of the inverse of quoting). + */ + template + struct pass_extracted_fn_to { + template + struct fn : FnCls::template fn {}; + + // extract, quote, pass on + template class Fn, class... Args> + struct fn> : FnCls::template fn> {}; + }; + + /* + * Metafunction class that invokes the specified metafunction operation, + * and passes its result on to the next metafunction class. + */ + template class Op, class FnCls> + struct pass_result_to { + // call Op, pass on its result + template + struct fn : FnCls::template fn> {}; + }; + + /* + * Bind arguments at the front of a metafunction class. + * Metafunction class equivalent to std::bind_front(). + */ + template + struct bind_front { + template + struct fn : FnCls::template fn {}; + }; + + /* + * Bind arguments at the back of a metafunction class. + * Metafunction class equivalent to std::bind_back() + */ + template + struct bind_back { + template + struct fn : FnCls::template fn {}; + }; + + /* + * Metafunction class equivalent to polyfill::always_false. + * It ignores arguments passed to the metafunction, + * and always returns the given type. + */ + template + struct always { + template + struct fn : type_wrap {}; + }; + + /* + * Unary metafunction class equivalent to std::type_identity. + */ + struct identity { + template + struct fn : type_wrap {}; + }; + + /* + * Metafunction class equivalent to std::negation. + */ + template + struct not_ { + template + struct fn : polyfill::negation> {}; + }; + + /* + * Metafunction class equivalent to std::conjunction + */ + template + struct conjunction { + template + struct fn : polyfill::conjunction...> {}; + }; + + /* + * Metafunction class equivalent to std::disjunction. + */ + template + struct disjunction { + template + struct fn : polyfill::disjunction...> {}; + }; + +#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + /* + * Metafunction equivalent to std::conjunction. + */ + template class... TraitFn> + using conjunction_fn = conjunction...>; + + /* + * Metafunction equivalent to std::disjunction. + */ + template class... TraitFn> + using disjunction_fn = disjunction...>; +#else + template class... TraitFn> + struct conjunction_fn : conjunction...> {}; + + template class... TraitFn> + struct disjunction_fn : disjunction...> {}; +#endif + + /* + * Convenience template alias for binding arguments at the front of a metafunction. + */ + template class Fn, class... Bound> + using bind_front_fn = bind_front, Bound...>; + + /* + * Convenience template alias for binding arguments at the back of a metafunction. + */ + template class Fn, class... Bound> + using bind_back_fn = bind_back, Bound...>; + + /* + * Convenience template alias for binding a metafunction at the front of a higher-order metafunction. + */ + template class Fn, class... Args2> class HigherFn, + template + class BoundFn, + class... Bound> + using bind_front_higherorder_fn = + bind_front::quote_fn, quote_fn, Bound...>; + } + } + + namespace mpl = internal::mpl; + + // convenience metafunction classes + namespace internal { + /* + * Trait metafunction class that checks if a type has the specified trait. + */ + template class TraitFn> + using check_if = mpl::quote_fn; + + /* + * Trait metafunction class that checks if a type doesn't have the specified trait. + */ + template class TraitFn> + using check_if_not = mpl::not_>; + + /* + * Trait metafunction class that checks if a type is the same as the specified type. + */ + template + using check_if_is_type = mpl::bind_front_fn; + + /* + * Trait metafunction class that checks if a type's template matches the specified template + * (similar to `is_specialization_of`). + */ + template class Template> + using check_if_is_template = + mpl::pass_extracted_fn_to>>; + } +} + +// #include "tuple_helper/same_or_void.h" + +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 = A; + }; + + template + struct same_or_void : same_or_void {}; + + } +} + +// #include "tuple_helper/tuple_traits.h" + +#include // std::is_same +#include + +// #include "../functional/cxx_type_traits_polyfill.h" + +// #include "../functional/mpl.h" + +namespace sqlite_orm { + namespace internal { + /* + * Higher-order trait metafunction that checks whether a tuple contains a type with given trait. + */ + template class TraitFn, class Tuple> + struct tuple_has {}; + template class TraitFn, class... Types> + struct tuple_has> : polyfill::disjunction...> {}; + + /* + * Trait metafunction class that checks whether a tuple contains a type with given trait. + */ + template class TraitFn> + using check_if_tuple_has = mpl::bind_front_higherorder_fn; + + /* + * Trait metafunction class that checks whether a tuple doesn't contain a type with given trait. + */ + template class TraitFn> + using check_if_tuple_has_not = mpl::not_>; + + /* + * Metafunction class that checks whether a tuple contains given type. + */ + template + using check_if_tuple_has_type = mpl::bind_front_higherorder_fn::template fn>; + + /* + * Metafunction class that checks whether a tuple contains a given template. + * + * Note: we are using 2 small tricks: + * 1. A template template parameter can be treated like a metafunction, so we can just "quote" a 'primary' + * template into the MPL system (e.g. `std::vector`). + * 2. This metafunction class does the opposite of the trait function `is_specialization`: + * `is_specialization` tries to instantiate the primary template template parameter using the + * template parameters of a template type, then compares both instantiated types. + * Here instead, `pass_extracted_fn_to` extracts the template template parameter from a template type, + * then compares the resulting template template parameters. + */ + template class Primary> + using check_if_tuple_has_template = + mpl::bind_front_higherorder_fn::template fn>; + } +} +// #include "tuple_helper/tuple_filter.h" + +#include // std::integral_constant, std::index_sequence, std::conditional, std::declval +#include // std::tuple + +// #include "../functional/cxx_universal.h" + +// #include "../functional/index_sequence_util.h" + +#include // std::index_sequence, std::make_index_sequence + +// #include "../functional/cxx_universal.h" + +namespace sqlite_orm { + namespace internal { + /** + * Get the first value of an index_sequence. + */ + template + SQLITE_ORM_CONSTEVAL size_t first_index_sequence_value(std::index_sequence) { + return I; + } + + template + struct flatten_idxseq { + using type = std::index_sequence<>; + }; + + template + struct flatten_idxseq> { + using type = std::index_sequence; + }; + + template + struct flatten_idxseq, std::index_sequence, Seq...> + : flatten_idxseq, Seq...> {}; + + template + using flatten_idxseq_t = typename flatten_idxseq::type; + } +} + +namespace sqlite_orm { + namespace internal { + + template + using tuple_cat_t = decltype(std::tuple_cat(std::declval()...)); + + template + struct conc_tuple { + using type = tuple_cat_t; + }; + + template + struct tuple_from_index_sequence; + + template + struct tuple_from_index_sequence> { + using type = std::tuple...>; + }; + + template + using tuple_from_index_sequence_t = typename tuple_from_index_sequence::type; + + template class Pred, template class Proj, class Seq> + struct filter_tuple_sequence; + +#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template class Pred, template class Proj, size_t... Idx> + struct filter_tuple_sequence> + : flatten_idxseq>>::value, + std::index_sequence, + std::index_sequence<>>...> {}; +#else + template class Pred, class SFINAE = void> + struct tuple_seq_single { + using type = std::index_sequence<>; + }; + + template class Pred> + struct tuple_seq_single::value>> { + using type = std::index_sequence; + }; + + template class Pred, template class Proj, size_t... Idx> + struct filter_tuple_sequence> + : flatten_idxseq>, Pred>::type...> {}; +#endif + + template + class Pred, + template class Proj = polyfill::type_identity_t, + class Seq = std::make_index_sequence::value>> + using filter_tuple_sequence_t = typename filter_tuple_sequence::type; + + template + class Pred, + template class FilterProj = polyfill::type_identity_t, + class Seq = std::make_index_sequence::value>> + using filter_tuple_t = tuple_from_index_sequence_t>; + + template + class Pred, + template class FilterProj = polyfill::type_identity_t> + struct count_tuple : std::integral_constant::size()> {}; + + /* + * Count a tuple, picking only those elements specified in the index sequence. + * + * Implementation note: must be distinct from `count_tuple` because legacy compilers have problems + * with a default Sequence in function template parameters [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. + */ + template + class Pred, + class Seq, + template class FilterProj = polyfill::type_identity_t> + struct count_filtered_tuple + : std::integral_constant::size()> {}; + } +} + +// #include "type_traits.h" + +// #include "collate_argument.h" + +// #include "error_code.h" + +// #include "table_type_of.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct column_pointer; + + template + struct indexed_column_t; + + /** + * 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_of::type` is `User` + * - `table_type_of::type` is `User` + * - `table_type_of::type` is `User` + */ + template + struct table_type_of; + + template + struct table_type_of { + using type = O; + }; + + template + struct table_type_of> { + using type = T; + }; + + template + struct table_type_of> { + using type = typename table_type_of::type; + }; + + template + using table_type_of_t = typename table_type_of::type; + } +} + +// #include "type_printer.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * AUTOINCREMENT constraint class. + */ + struct autoincrement_t {}; + + enum class conflict_clause_t { + rollback, + abort, + fail, + ignore, + replace, + }; + + struct primary_key_base { + enum class order_by { + unspecified, + ascending, + descending, + }; + struct { + order_by asc_option = order_by::unspecified; + conflict_clause_t conflict_clause = conflict_clause_t::rollback; + bool conflict_clause_is_on = false; + } options; + }; + + template + struct primary_key_with_autoincrement { + using primary_key_type = T; + + primary_key_type primary_key; + + primary_key_with_autoincrement(primary_key_type primary_key_) : primary_key(primary_key_) {} + }; + + /** + * PRIMARY KEY constraint class. + * Cs is parameter pack which contains columns (member pointers and/or function pointers). Can be empty when + * used within `make_column` function. + */ + template + struct primary_key_t : primary_key_base { + using self = primary_key_t; + using order_by = primary_key_base::order_by; + using columns_tuple = std::tuple; + + columns_tuple columns; + + primary_key_t(decltype(columns) columns) : columns(std::move(columns)) {} + + self asc() const { + auto res = *this; + res.options.asc_option = order_by::ascending; + return res; + } + + self desc() const { + auto res = *this; + res.options.asc_option = order_by::descending; + return res; + } + + primary_key_with_autoincrement autoincrement() const { + return {*this}; + } + + self on_conflict_rollback() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::rollback; + return res; + } + + self on_conflict_abort() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::abort; + return res; + } + + self on_conflict_fail() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::fail; + return res; + } + + self on_conflict_ignore() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::ignore; + return res; + } + + self on_conflict_replace() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::replace; + return res; + } + }; + + struct unique_base { + operator std::string() const { + return "UNIQUE"; + } + }; + + /** + * UNIQUE constraint class. + */ + template + struct unique_t : unique_base { + using columns_tuple = std::tuple; + + columns_tuple columns; + + unique_t(columns_tuple columns_) : columns(std::move(columns_)) {} + }; + + /** + * DEFAULT constraint class. + * T is a value type. + */ + template + struct default_t { + using value_type = T; + + value_type value; + + operator std::string() const { + return "DEFAULT"; + } + }; + +#if SQLITE_VERSION_NUMBER >= 3006019 + + /** + * 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 + struct foreign_key_t; + + enum class foreign_key_action { + none, // not specified + no_action, + restrict_, + set_null, + set_default, + cascade, + }; + + inline std::ostream& operator<<(std::ostream& os, foreign_key_action action) { + switch(action) { + case foreign_key_action::no_action: + os << "NO ACTION"; + break; + case foreign_key_action::restrict_: + os << "RESTRICT"; + break; + case foreign_key_action::set_null: + os << "SET NULL"; + break; + case foreign_key_action::set_default: + os << "SET DEFAULT"; + break; + case foreign_key_action::cascade: + os << "CASCADE"; + break; + case foreign_key_action::none: + break; + } + return os; + } + + struct on_update_delete_base { + const bool update; // true if update and false if delete + + operator std::string() const { + if(this->update) { + return "ON UPDATE"; + } else { + return "ON DELETE"; + } + } + }; + + /** + * F - foreign key class + */ + template + struct on_update_delete_t : on_update_delete_base { + using foreign_key_type = F; + + 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_) {} + + foreign_key_action _action = foreign_key_action::none; + + 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; + } + + 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; + } + + 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; + } + + 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; + } + + 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; + } + + operator bool() const { + return this->_action != foreign_key_action::none; + } + }; + + template + bool operator==(const on_update_delete_t& lhs, const on_update_delete_t& rhs) { + return lhs._action == rhs._action; + } + + template + struct foreign_key_t, std::tuple> { + using columns_type = std::tuple; + 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; + + /** + * Holds obect type of all source columns. + */ + using source_type = typename same_or_void...>::type; + + 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 + bool operator==(const foreign_key_t& lhs, const foreign_key_t& rhs) { + return lhs.columns == rhs.columns && lhs.references == rhs.references && lhs.on_update == rhs.on_update && + lhs.on_delete == rhs.on_delete; + } + + /** + * 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; + + tuple_type columns; + + template + foreign_key_t, std::tuple> references(Rs... refs) { + return {std::move(this->columns), std::make_tuple(std::forward(refs)...)}; + } + }; +#endif + + struct collate_constraint_t { + collate_argument argument = collate_argument::binary; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + collate_constraint_t(collate_argument argument) : argument{argument} {} +#endif + + operator std::string() const { + return "COLLATE " + this->string_from_collate_argument(this->argument); + } + + static std::string string_from_collate_argument(collate_argument argument) { + switch(argument) { + case collate_argument::binary: + return "BINARY"; + case collate_argument::nocase: + return "NOCASE"; + case collate_argument::rtrim: + return "RTRIM"; + } + throw std::system_error{orm_error_code::invalid_collate_argument_enum}; + } + }; + + template + struct check_t { + using expression_type = T; + + expression_type expression; + }; + + struct basic_generated_always { + enum class storage_type { + not_specified, + virtual_, + stored, + }; + + bool full = true; + storage_type storage = storage_type::not_specified; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + basic_generated_always(bool full, storage_type storage) : full{full}, storage{storage} {} +#endif + }; + + template + struct generated_always_t : basic_generated_always { + using expression_type = T; + + expression_type expression; + + generated_always_t(expression_type expression_, bool full, storage_type storage) : + basic_generated_always{full, storage}, expression(std::move(expression_)) {} + + generated_always_t virtual_() { + return {std::move(this->expression), this->full, storage_type::virtual_}; + } + + generated_always_t stored() { + return {std::move(this->expression), this->full, storage_type::stored}; + } + }; + + } + + namespace internal { + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = polyfill::is_specialization_of_v; + + template + using is_foreign_key = polyfill::bool_constant>; + + template + struct is_primary_key : std::false_type {}; + + template + struct is_primary_key> : std::true_type {}; + + template + struct is_primary_key> : std::true_type {}; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = is_primary_key::value; + + template + using is_generated_always = polyfill::is_specialization_of; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = is_generated_always::value; + + template + using is_autoincrement = std::is_same; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_autoincrement_v = is_autoincrement::value; + + /** + * PRIMARY KEY INSERTABLE traits. + */ + template + struct is_primary_key_insertable + : polyfill::disjunction< + mpl::instantiate, + check_if_tuple_has_template, + check_if_tuple_has_template>, + constraints_type_t>, + std::is_base_of>>> { + + static_assert(tuple_has>::value, "an unexpected type was passed"); + }; + + template + using is_constraint = + mpl::instantiate, + check_if, + check_if, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_type, +#if SQLITE_VERSION_NUMBER >= 3031000 + check_if, +#endif + // dummy tail because of SQLITE_VERSION_NUMBER checks above + mpl::always>, + T>; + } + +#if SQLITE_VERSION_NUMBER >= 3031000 + template + internal::generated_always_t generated_always_as(T expression) { + return {std::move(expression), true, internal::basic_generated_always::storage_type::not_specified}; + } + + template + internal::generated_always_t as(T expression) { + return {std::move(expression), false, internal::basic_generated_always::storage_type::not_specified}; + } +#endif +#if SQLITE_VERSION_NUMBER >= 3006019 + + /** + * FOREIGN KEY constraint construction function that takes member pointer as argument + * Available in SQLite 3.6.19 or higher + */ + template + internal::foreign_key_intermediate_t foreign_key(Cs... columns) { + return {std::make_tuple(std::forward(columns)...)}; + } +#endif + + /** + * UNIQUE constraint builder function. + */ + template + internal::unique_t unique(Args... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + inline internal::unique_t<> unique() { + return {{}}; + } + + /** + * AUTOINCREMENT keyword. [Deprecation notice] Use `primary_key().autoincrement()` instead of using this function. + * This function will be removed in 1.9 + */ + [[deprecated("Use primary_key().autoincrement()` instead")]] inline internal::autoincrement_t autoincrement() { + return {}; + } + + template + internal::primary_key_t primary_key(Cs... cs) { + return {std::make_tuple(std::forward(cs)...)}; + } + + inline internal::primary_key_t<> primary_key() { + return {{}}; + } + + template + internal::default_t default_value(T t) { + return {std::move(t)}; + } + + inline internal::collate_constraint_t collate_nocase() { + return {internal::collate_argument::nocase}; + } + + inline internal::collate_constraint_t collate_binary() { + return {internal::collate_argument::binary}; + } + + inline internal::collate_constraint_t collate_rtrim() { + return {internal::collate_argument::rtrim}; + } + + template + internal::check_t check(T t) { + return {std::move(t)}; + } +} +#pragma once + +#include // std::false_type, std::true_type, std::enable_if +#include // std::shared_ptr, std::unique_ptr +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + + /** + * 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 : std::false_type { + bool operator()(const T&) const { + return true; + } + }; + + /** + * This is a specialization for std::shared_ptr, std::unique_ptr, std::optional, which are nullable in sqlite_orm. + */ + template + struct type_is_nullable, +#endif + polyfill::is_specialization_of, + polyfill::is_specialization_of>>> : std::true_type { + bool operator()(const T& t) const { + return static_cast(t); + } + }; + +} +#pragma once + +#include // std::false_type, std::true_type +#include // std::move +// #include "functional/cxx_optional.h" + +// #include "tags.h" + +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 {}; + } +} + +// #include "serialize_result_type.h" + +// #include "functional/cxx_string_view.h" + +// #include "cxx_core_features.h" + +#if SQLITE_ORM_HAS_INCLUDE() +#include +#endif + +#if __cpp_lib_string_view >= 201606L +#define SQLITE_ORM_STRING_VIEW_SUPPORTED +#endif + +#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // std::string +#endif + +namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + using serialize_result_type = std::string_view; + using serialize_arg_type = std::string_view; +#else + using serialize_result_type = std::string; + using serialize_arg_type = const std::string&; +#endif + } +} + +namespace sqlite_orm { + + namespace internal { + + /** + * Inherit this class to support arithmetic types overloading + */ + struct arithmetic_t {}; + + template + struct binary_operator : Ds... { + using left_type = L; + using right_type = R; + + left_type lhs; + right_type rhs; + + binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} + }; + + struct conc_string { + serialize_result_type serialize() const { + return "||"; + } + }; + + /** + * Result of concatenation || operator + */ + template + using conc_t = binary_operator; + + struct add_string { + serialize_result_type serialize() const { + return "+"; + } + }; + + /** + * Result of addition + operator + */ + template + using add_t = binary_operator; + + struct sub_string { + serialize_result_type serialize() const { + return "-"; + } + }; + + /** + * Result of substitute - operator + */ + template + using sub_t = binary_operator; + + struct mul_string { + serialize_result_type serialize() const { + return "*"; + } + }; + + /** + * Result of multiply * operator + */ + template + using mul_t = binary_operator; + + struct div_string { + serialize_result_type serialize() const { + return "/"; + } + }; + + /** + * Result of divide / operator + */ + template + using div_t = binary_operator; + + struct mod_operator_string { + serialize_result_type serialize() const { + return "%"; + } + }; + + /** + * Result of mod % operator + */ + template + using mod_t = binary_operator; + + struct bitwise_shift_left_string { + serialize_result_type serialize() const { + return "<<"; + } + }; + + /** + * Result of bitwise shift left << operator + */ + template + using bitwise_shift_left_t = binary_operator; + + struct bitwise_shift_right_string { + serialize_result_type serialize() const { + return ">>"; + } + }; + + /** + * Result of bitwise shift right >> operator + */ + template + using bitwise_shift_right_t = binary_operator; + + struct bitwise_and_string { + serialize_result_type serialize() const { + return "&"; + } + }; + + /** + * Result of bitwise and & operator + */ + template + using bitwise_and_t = binary_operator; + + struct bitwise_or_string { + serialize_result_type serialize() const { + return "|"; + } + }; + + /** + * Result of bitwise or | operator + */ + template + using bitwise_or_t = binary_operator; + + struct bitwise_not_string { + serialize_result_type serialize() const { + return "~"; + } + }; + + /** + * Result of bitwise not ~ operator + */ + template + 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_)) {} + }; + + struct assign_string { + serialize_result_type serialize() 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; + + } + + /** + * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT + * name || '@gmail.com' FROM users + */ + template + internal::conc_t conc(L l, R r) { + return {std::move(l), std::move(r)}; + } + + /** + * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users + */ + template + internal::add_t add(L l, R r) { + return {std::move(l), std::move(r)}; + } + + /** + * 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)}; + } + + template + internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_and_t bitwise_and(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_or_t bitwise_or(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_not_t bitwise_not(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::is_same, std::is_member_object_pointer + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "type_traits.h" + +// #include "member_traits/member_traits.h" + +#include // std::enable_if, std::is_function, std::true_type, std::false_type + +// #include "../functional/cxx_universal.h" + +// #include "../functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { + // SFINAE friendly trait to get a member object pointer's field type + template + struct object_field_type {}; + + template + using object_field_type_t = typename object_field_type::type; + + template + struct object_field_type : std::enable_if::value, F> {}; + + // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified return type) + template + struct getter_field_type {}; + + template + using getter_field_type_t = typename getter_field_type::type; + + template + struct getter_field_type : getter_field_type {}; + + template + struct getter_field_type : polyfill::remove_cvref {}; + + template + struct getter_field_type : polyfill::remove_cvref {}; + +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct getter_field_type : polyfill::remove_cvref {}; + + template + struct getter_field_type : polyfill::remove_cvref {}; +#endif + + // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) + template + struct setter_field_type {}; + + template + using setter_field_type_t = typename setter_field_type::type; + + template + struct setter_field_type : setter_field_type {}; + + template + struct setter_field_type : polyfill::remove_cvref {}; + +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct setter_field_type : polyfill::remove_cvref {}; +#endif + + template + struct is_getter : std::false_type {}; + template + struct is_getter>> : std::true_type {}; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_getter_v = is_getter::value; + + template + struct is_setter : std::false_type {}; + template + struct is_setter>> : std::true_type {}; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_setter_v = is_setter::value; + + template + struct member_field_type : object_field_type, getter_field_type, setter_field_type {}; + + template + using member_field_type_t = typename member_field_type::type; + + template + struct member_object_type {}; + + template + struct member_object_type : polyfill::type_identity {}; + + template + using member_object_type_t = typename member_object_type::type; + } +} + +// #include "type_is_nullable.h" + +// #include "constraints.h" + +namespace sqlite_orm { + + namespace internal { + + struct column_identifier { + + /** + * Column name. + */ + std::string name; + }; + + struct empty_setter {}; + + /* + * Encapsulates object member pointers that are used as column fields, + * and whose object is mapped to storage. + * + * G is a member object pointer or member function pointer + * S is a member function pointer or `empty_setter` + */ + template + struct column_field { + using member_pointer_t = G; + using setter_type = S; + using object_type = member_object_type_t; + using field_type = member_field_type_t; + + /** + * Member pointer used to read a field value. + * If it is a object member pointer it is also used to write a field value. + */ + const member_pointer_t member_pointer; + + /** + * Setter member function to write a field value + */ + SQLITE_ORM_NOUNIQUEADDRESS + const setter_type setter; + + /** + * Simplified interface for `NOT NULL` constraint + */ + constexpr bool is_not_null() const { + return !type_is_nullable::value; + } + }; + + /* + * Encapsulates a tuple of column constraints. + * + * Op... is a constraints pack, e.g. primary_key_t, unique_t etc + */ + template + struct column_constraints { + using constraints_type = std::tuple; + + SQLITE_ORM_NOUNIQUEADDRESS + constraints_type constraints; + + /** + * Checks whether contraints are of trait `Trait` + */ + template class Trait> + constexpr bool is() const { + return tuple_has::value; + } + + constexpr bool is_generated() const { +#if SQLITE_VERSION_NUMBER >= 3031000 + return is(); +#else + return false; +#endif + } + + /** + * Simplified interface for `DEFAULT` constraint + * @return string representation of default value if it exists otherwise nullptr + */ + std::unique_ptr default_value() const; + }; + + /** + * Column definition. + * + * It is a composition of orthogonal information stored in different base classes. + */ + template + struct column_t : column_identifier, column_field, column_constraints { +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + column_t(std::string name, G memberPointer, S setter, std::tuple op) : + column_identifier{std::move(name)}, column_field{memberPointer, setter}, + column_constraints{std::move(op)} {} +#endif + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_column_v = polyfill::is_specialization_of_v; + + template + using is_column = polyfill::bool_constant>; + + template + using col_index_sequence_with_field_type = + filter_tuple_sequence_t::template fn, + field_type_t, + filter_tuple_sequence_t>; + + template class TraitFn> + using col_index_sequence_with = filter_tuple_sequence_t::template fn, + constraints_type_t, + filter_tuple_sequence_t>; + + template class TraitFn> + using col_index_sequence_excluding = filter_tuple_sequence_t::template fn, + constraints_type_t, + filter_tuple_sequence_t>; + } + + /** + * Column builder function. You should use it to create columns instead of constructor + */ + template = true> + internal::column_t make_column(std::string name, M m, Op... constraints) { + static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::move(name), m, {}, std::make_tuple(constraints...)}); + } + + /** + * Column builder function with setter and getter. You should use it to create columns instead of constructor + */ + template = true, + internal::satisfies = true> + internal::column_t make_column(std::string name, S setter, G getter, Op... constraints) { + static_assert(std::is_same, internal::getter_field_type_t>::value, + "Getter and setter must get and set same data type"); + static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), getter, setter, std::make_tuple(constraints...)}); + } + + /** + * Column builder function with getter and setter (reverse order). You should use it to create columns instead of + * constructor + */ + template = true, + internal::satisfies = true> + internal::column_t make_column(std::string name, G getter, S setter, Op... constraints) { + static_assert(std::is_same, internal::getter_field_type_t>::value, + "Getter and setter must get and set same data type"); + static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), getter, setter, std::make_tuple(constraints...)}); + } +} +#pragma once + +#include // std::wstring_convert +#include // std::string +#include // std::stringstream +#include // std::vector +#include // std::shared_ptr, std::unique_ptr +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::codecvt_utf8_utf16 +#endif // SQLITE_ORM_OMITS_CODECVT +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "is_std_ptr.h" + +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 + struct field_printer; + + namespace internal { + template + SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v{})>> = true + // Also see implementation note for `is_bindable_v` + ; + template + using is_printable = polyfill::bool_constant>; + } + + template + struct field_printer::value>> { + std::string operator()(const T& t) const { + std::stringstream ss; + ss << t; + return ss.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 ss; + ss << +t; + return ss.str(); + } + }; + + /** + * 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 ss; + ss << +t; + return ss.str(); + } + }; + + /** + * char is neither signed char nor unsigned char so it has its own specialization + */ + template<> + struct field_printer { + std::string operator()(const char& t) const { + std::stringstream ss; + ss << +t; + return ss.str(); + } + }; + + template + struct field_printer::value>> { + std::string operator()(std::string string) const { + return string; + } + }; + + 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(); + } + }; +#ifndef SQLITE_ORM_OMITS_CODECVT + /** + * Specialization for std::wstring (UTF-16 assumed). + */ + template + struct field_printer::value>> { + std::string operator()(const std::wstring& wideString) const { + std::wstring_convert> converter; + return converter.to_bytes(wideString); + } + }; +#endif // SQLITE_ORM_OMITS_CODECVT + template<> + struct field_printer { + std::string operator()(const 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< + T, + std::enable_if_t, + internal::is_printable>>>> { + using unqualified_type = std::remove_cv_t; + + std::string operator()(const T& t) const { + if(t) { + return field_printer()(*t); + } else { + return field_printer{}(nullptr); + } + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct field_printer< + T, + std::enable_if_t, + internal::is_printable>>>> { + using unqualified_type = std::remove_cv_t; + + std::string operator()(const T& t) const { + if(t.has_value()) { + return field_printer()(*t); + } else { + return field_printer{}(std::nullopt); + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} +#pragma once + +#include // std::string +#include // std::enable_if, std::is_same, std::remove_const +#include // std::vector +#include // std::tuple +#include // std::stringstream + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.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 + */ +#ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template class Base> + struct is_base_of_template_impl { + template + static constexpr std::true_type test(const Base&); + + static constexpr std::false_type test(...); + }; + + template class C> + using is_base_of_template = decltype(is_base_of_template_impl::test(std::declval())); +#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 + + template class C> + SQLITE_ORM_INLINE_VAR constexpr bool is_base_of_template_v = is_base_of_template::value; + } +} + +// #include "type_traits.h" + +// #include "collate_argument.h" + +// #include "constraints.h" + +// #include "optional_container.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * 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; + + type field; + + template + void apply(const L& l) const { + l(this->field); + } + }; + + template<> + struct optional_container { + using type = void; + + template + void apply(const L&) const { + //.. + } + }; + } +} + +// #include "serializer_context.h" + +namespace sqlite_orm { + + namespace internal { + + struct serializer_context_base { + bool replace_bindable_with_question = false; + bool skip_table_name = true; + bool use_parentheses = true; + }; + + template + struct serializer_context : serializer_context_base { + using db_objects_type = DBOs; + + const db_objects_type& db_objects; + + serializer_context(const db_objects_type& dbObjects) : db_objects{dbObjects} {} + }; + + template + struct serializer_context_builder { + using storage_type = S; + using db_objects_type = typename storage_type::db_objects_type; + + serializer_context_builder(const storage_type& storage_) : storage{storage_} {} + + serializer_context operator()() const { + return {obtain_db_objects(this->storage)}; + } + + const storage_type& storage; + }; + + } + +} + +// #include "tags.h" + +// #include "alias_traits.h" + +#include // std::remove_const, std::is_base_of, std::is_same + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + +namespace sqlite_orm { + + /** @short Base class for a custom table alias, column alias or expression alias. + */ + struct alias_tag {}; + + namespace internal { + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_alias_v = std::is_base_of::value; + + template + using is_alias = polyfill::bool_constant>; + + /** @short Alias of a column in a record set, see `orm_column_alias`. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_column_alias_v = + polyfill::conjunction_v, polyfill::negation>>; + + template + using is_column_alias = is_alias; + + /** @short Alias of any type of record set, see `orm_recordset_alias`. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_recordset_alias_v = + polyfill::conjunction_v, polyfill::is_detected>; + + template + using is_recordset_alias = polyfill::bool_constant>; + + /** @short Alias of a concrete table, see `orm_table_alias`. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_table_alias_v = polyfill::conjunction_v< + is_recordset_alias, + polyfill::negation, std::remove_const_t>>>; + + template + using is_table_alias = polyfill::bool_constant>; + } +} + +// #include "expression.h" + +#include +#include // std::move, std::forward +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_universal.h" + +// #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=(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)}; + } +} + +// #include "type_printer.h" + +// #include "literal.h" + +namespace sqlite_orm { + namespace internal { + + /* + * Protect an otherwise bindable element so that it is always serialized as a literal value. + */ + template + struct literal_holder { + using type = T; + + type value; + }; + + } +} + +namespace sqlite_orm { + + namespace internal { + + struct limit_string { + operator std::string() const { + return "LIMIT"; + } + }; + + /** + * Stores LIMIT/OFFSET info + */ + template + struct limit_t : limit_string { + T lim; + optional_container off; + + limit_t() = default; + + limit_t(decltype(lim) lim_) : lim(std::move(lim_)) {} + + limit_t(decltype(lim) lim_, decltype(off) off_) : lim(std::move(lim_)), off(std::move(off_)) {} + }; + + template + struct is_limit : std::false_type {}; + + template + struct is_limit> : std::true_type {}; + + /** + * Stores OFFSET only info + */ + template + struct offset_t { + T off; + }; + + template + using is_offset = polyfill::is_specialization_of; + + /** + * Collated something + */ + template + struct collate_t : public condition_t { + T expr; + collate_argument argument; + + collate_t(T expr_, collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} + + operator std::string() const { + return collate_constraint_t{this->argument}; + } + }; + + struct named_collate_base { + std::string name; + + operator std::string() const { + return "COLLATE " + this->name; + } + }; + + /** + * Collated something with custom collate function + */ + template + struct named_collate : named_collate_base { + T expr; + + named_collate(T expr_, std::string name_) : named_collate_base{std::move(name_)}, expr(std::move(expr_)) {} + }; + + struct negated_condition_string { + operator std::string() const { + return "NOT"; + } + }; + + /** + * Result of not operator + */ + 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 + * 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; + + 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_)) {} + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_of_template_v; + + template + using is_binary_condition = polyfill::bool_constant>; + + struct and_condition_string { + operator std::string() const { + return "AND"; + } + }; + + /** + * Result of and operator + */ + template + 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"; + } + }; + + /** + * Result of or operator + */ + template + 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 "="; + } + }; + + /** + * = and == operators object + */ + template + struct is_equal_t : binary_condition, negatable_t { + using self = is_equal_t; + + 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}; + } + + named_collate collate(std::string name) const { + return {*this, std::move(name)}; + } + + template + named_collate collate() const { + std::stringstream ss; + ss << C::name() << std::flush; + return {*this, ss.str()}; + } + }; + + struct is_not_equal_string { + operator std::string() const { + return "!="; + } + }; + + /** + * != operator object + */ + template + struct is_not_equal_t : binary_condition, negatable_t { + using self = is_not_equal_t; + + 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 greater_than_string { + operator std::string() const { + return ">"; + } + }; + + /** + * > operator object. + */ + template + struct greater_than_t : binary_condition, negatable_t { + using self = greater_than_t; + + 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 greater_or_equal_string { + operator std::string() const { + return ">="; + } + }; + + /** + * >= operator object. + */ + template + struct greater_or_equal_t : binary_condition, negatable_t { + using self = greater_or_equal_t; + + 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 lesser_than_string { + operator std::string() const { + return "<"; + } + }; + + /** + * < operator object. + */ + template + struct lesser_than_t : binary_condition, negatable_t { + using self = lesser_than_t; + + 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 lesser_or_equal_string { + operator std::string() const { + return "<="; + } + }; + + /** + * <= operator object. + */ + template + struct lesser_or_equal_t : binary_condition, negatable_t { + using self = lesser_or_equal_t; + + 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 in_base { + bool negative = false; // used in not_in + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + in_base(bool negative) : negative{negative} {} +#endif + }; + + /** + * IN operator object. + */ + template + struct dynamic_in_t : condition_t, in_base, negatable_t { + using self = dynamic_in_t; + + L left; // left expression + A argument; // 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 left_, decltype(argument) argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} + }; + + struct is_null_string { + operator std::string() const { + return "IS NULL"; + } + }; + + /** + * IS NULL operator object. + */ + template + struct is_null_t : is_null_string, negatable_t { + using self = is_null_t; + + T t; + + is_null_t(T t_) : t(std::move(t_)) {} + }; + + struct is_not_null_string { + operator std::string() const { + return "IS NOT NULL"; + } + }; + + /** + * IS NOT NULL operator object. + */ + template + struct is_not_null_t : is_not_null_string, negatable_t { + using self = is_not_null_t; + + T t; + + is_not_null_t(T t_) : t(std::move(t_)) {} + }; + + struct order_by_base { + int asc_desc = 0; // 1: asc, -1: desc + std::string _collate_argument; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + order_by_base() = default; + + order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : + asc_desc(asc_desc_), _collate_argument(std::move(_collate_argument_)) {} +#endif + }; + + struct order_by_string { + operator std::string() const { + return "ORDER BY"; + } + }; + + /** + * ORDER BY argument holder. + */ + template + struct order_by_t : order_by_base, order_by_string { + using expression_type = O; + using self = order_by_t; + + expression_type expression; + + order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} + + self asc() const { + auto res = *this; + res.asc_desc = 1; + return res; + } + + self desc() const { + auto res = *this; + res.asc_desc = -1; + return res; + } + + self collate_binary() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); + return res; + } + + self collate_nocase() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); + return res; + } + + self collate_rtrim() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); + return res; + } + + self collate(std::string name) const { + auto res = *this; + res._collate_argument = std::move(name); + return res; + } + + template + self collate() const { + std::stringstream ss; + ss << C::name() << std::flush; + return this->collate(ss.str()); + } + }; + + /** + * ORDER BY pack holder. + */ + template + struct multi_order_by_t : order_by_string { + using args_type = std::tuple; + + args_type args; + + multi_order_by_t(args_type args_) : args{std::move(args_)} {} + }; + + struct dynamic_order_by_entry_t : order_by_base { + std::string name; + + dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) : + order_by_base{asc_desc_, std::move(collate_argument_)}, name(std::move(name_)) {} + }; + + /** + * C - serializer 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; + + 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.expression, newContext); + this->entries.emplace_back(std::move(columnName), + order_by.asc_desc, + std::move(order_by._collate_argument)); + } + + const_iterator begin() const { + return this->entries.begin(); + } + + const_iterator end() const { + return this->entries.end(); + } + + void clear() { + this->entries.clear(); + } + + protected: + std::vector entries; + context_t context; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_order_by_v = + polyfill::disjunction_v, + polyfill::is_specialization_of, + polyfill::is_specialization_of>; + + template + using is_order_by = polyfill::bool_constant>; + + struct between_string { + operator std::string() const { + return "BETWEEN"; + } + }; + + /** + * BETWEEN operator object. + */ + 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; + + between_t(expression_type expr_, lower_type b1_, upper_type b2_) : + expr(std::move(expr_)), b1(std::move(b1_)), b2(std::move(b2_)) {} + }; + + struct like_string { + operator std::string() const { + return "LIKE"; + } + }; + + /** + * 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; + + arg_t arg; + pattern_t pattern; + optional_container arg3; // not escape cause escape exists as a function here + + 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 { + optional_container newArg3{std::move(c)}; + return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; + } + }; + + struct glob_string { + operator std::string() const { + return "GLOB"; + } + }; + + template + struct glob_t : condition_t, glob_string, internal::negatable_t { + using self = glob_t; + using arg_t = A; + using pattern_t = T; + + arg_t arg; + pattern_t pattern; + + glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {} + }; + + struct cross_join_string { + operator std::string() const { + return "CROSS JOIN"; + } + }; + + /** + * CROSS JOIN holder. + * T is joined type which represents any mapped table. + */ + template + struct cross_join_t : cross_join_string { + using type = T; + }; + + struct natural_join_string { + operator std::string() const { + return "NATURAL JOIN"; + } + }; + + /** + * NATURAL JOIN holder. + * T is joined type which represents any mapped table. + */ + template + struct natural_join_t : natural_join_string { + using type = T; + }; + + struct left_join_string { + operator std::string() const { + return "LEFT JOIN"; + } + }; + + /** + * 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; + + on_type constraint; + + left_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct join_string { + operator std::string() const { + return "JOIN"; + } + }; + + /** + * 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; + + on_type constraint; + + join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct left_outer_join_string { + operator std::string() const { + return "LEFT OUTER JOIN"; + } + }; + + /** + * 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; + + on_type constraint; + + left_outer_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct on_string { + operator std::string() const { + return "ON"; + } + }; + + /** + * 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; + + arg_type arg; + + on_t(arg_type arg_) : arg(std::move(arg_)) {} + }; + + /** + * USING argument holder. + */ + template + struct using_t { + column_pointer column; + + operator std::string() const { + return "USING"; + } + }; + + struct inner_join_string { + operator std::string() const { + return "INNER JOIN"; + } + }; + + /** + * 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; + + on_type constraint; + + inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + 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 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 from_t { + using tuple_type = std::tuple; + }; + + template + using is_from = polyfill::is_specialization_of; + + template + using is_constrained_join = polyfill::is_detected; + } + + /** + * Explicit FROM function. Usage: + * `storage.select(&User::id, from());` + */ + template + internal::from_t from() { + static_assert(sizeof...(Tables) > 0, ""); + return {}; + } + + template = true> + internal::negated_condition_t operator!(T arg) { + return {std::move(arg)}; + } + + // Deliberately put operators for `expression_t` into the internal namespace + // to facilitate ADL (Argument Dependent Lookup) + namespace internal { + /** + * Cute operators for columns + */ + template + lesser_than_t operator<(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + lesser_than_t operator<(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + lesser_or_equal_t operator<=(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + lesser_or_equal_t operator<=(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + greater_than_t operator>(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + greater_than_t operator>(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + greater_or_equal_t operator>=(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + greater_or_equal_t operator>=(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + is_equal_t operator==(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + is_equal_t operator==(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + is_not_equal_t operator!=(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + is_not_equal_t operator!=(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + conc_t operator||(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + conc_t operator||(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + conc_t operator||(expression_t l, expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } + + template = true> + conc_t operator||(E expr, R r) { + return {std::move(expr), std::move(r)}; + } + + template = true> + conc_t operator||(L l, E expr) { + return {std::move(l), std::move(expr)}; + } + + template + add_t operator+(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + add_t operator+(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + add_t operator+(expression_t l, expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } + + template + sub_t operator-(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + sub_t operator-(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + sub_t operator-(expression_t l, expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } + + template + mul_t operator*(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + mul_t operator*(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + mul_t operator*(expression_t l, expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } + + template + div_t operator/(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + div_t operator/(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + div_t operator/(expression_t l, expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } + + template + mod_t operator%(expression_t expr, R r) { + return {std::move(expr.value), std::move(r)}; + } + + template + mod_t operator%(L l, expression_t expr) { + return {std::move(l), std::move(expr.value)}; + } + + template + mod_t operator%(expression_t l, expression_t r) { + return {std::move(l.value), std::move(r.value)}; + } + } + + template + internal::using_t using_(F O::*p) { + return {p}; + } + template + internal::using_t using_(internal::column_pointer cp) { + return {std::move(cp)}; + } + + template + internal::on_t on(T t) { + return {std::move(t)}; + } + + template + internal::cross_join_t cross_join() { + return {}; + } + + template + internal::natural_join_t natural_join() { + return {}; + } + + template + internal::left_join_t left_join(O o) { + return {std::move(o)}; + } + + template + internal::join_t join(O o) { + return {std::move(o)}; + } + + template + internal::left_outer_join_t left_outer_join(O o) { + return {std::move(o)}; + } + + template + internal::inner_join_t inner_join(O o) { + return {std::move(o)}; + } + + template + internal::offset_t offset(T off) { + return {std::move(off)}; + } + + template + internal::limit_t limit(T lim) { + return {std::move(lim)}; + } + + template = true> + internal::limit_t limit(O off, T lim) { + return {std::move(lim), {std::move(off)}}; + } + + template + internal::limit_t limit(T lim, internal::offset_t offt) { + return {std::move(lim), {std::move(offt.off)}}; + } + + template, + std::is_base_of>, + bool> = true> + 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, + std::is_base_of>, + bool> = true> + 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 + internal::is_not_null_t is_not_null(T t) { + return {std::move(t)}; + } + + 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, column alias or expression + * + * Examples: + * storage.select(&User::name, order_by(&User::id)) + * storage.select(as(&User::name), order_by(get())) + */ + template> = true> + internal::order_by_t order_by(O o) { + return {std::move(o)}; + } + + /** + * ORDER BY positional ordinal + * + * Examples: + * storage.select(&User::name, order_by(1)) + */ + template> = true> + 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::serializer_context_builder builder(storage); + return builder(); + } + + /** + * 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)}}; + } + + /** + * 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, std::remove_const +#include // std::index_sequence, std::make_index_sequence +#include // std::string +#include // std::stringstream +#include // std::copy_n + +// #include "functional/cxx_universal.h" +// ::size_t +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + +// #include "alias_traits.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * This is a common built-in class used for character based table aliases. + * For convenience there exist public type aliases `alias_a`, `alias_b`, ... + * The easiest way to create a table alias is using `"z"_alias.for_()`. + */ + template + struct recordset_alias : alias_tag { + using type = T; + + static std::string get() { + return {A, X...}; + } + }; + + /** + * 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; + }; + + /* + * Encapsulates extracting the alias identifier of a non-alias. + */ + template + struct alias_extractor { + static std::string extract() { + return {}; + } + + static std::string as_alias() { + return {}; + } + }; + + /* + * Encapsulates extracting the alias identifier of an alias. + * + * `extract()` always returns the alias identifier. + * `as_alias()` is used in contexts where a table is aliased. + */ + template + struct alias_extractor> { + static std::string extract() { + std::stringstream ss; + ss << A::get(); + return ss.str(); + } + + // for column and regular table aliases -> alias identifier + template, A> = true> + static std::string as_alias() { + return alias_extractor::extract(); + } + }; + + /** + * Used to store alias for expression + */ + template + struct as_t { + using alias_type = T; + using expression_type = E; + + expression_type expression; + }; + + /** + * This is a common built-in class used for custom single-character column aliases. + * For convenience there exist type aliases `colalias_a`, `colalias_b`, ... + * The easiest way to create a column alias is using `"xyz"_col`. + */ + template + struct column_alias : alias_tag { + static std::string get() { + return {A, X...}; + } + }; + + template + struct alias_holder { + using type = T; + + alias_holder() = default; + }; + } + + /** + * @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'. + */ + template, bool> = true> + internal::alias_column_t alias_column(C c) { + using aliased_type = internal::type_t; + static_assert(std::is_same, aliased_type>::value, + "Column must be from aliased table"); + return {c}; + } + + /** + * Alias a column expression. + */ + template = true> + internal::as_t as(E expression) { + return {std::move(expression)}; + } + + template = true> + internal::alias_holder get() { + return {}; + } + + template + using alias_a = internal::recordset_alias; + template + using alias_b = internal::recordset_alias; + template + using alias_c = internal::recordset_alias; + template + using alias_d = internal::recordset_alias; + template + using alias_e = internal::recordset_alias; + template + using alias_f = internal::recordset_alias; + template + using alias_g = internal::recordset_alias; + template + using alias_h = internal::recordset_alias; + template + using alias_i = internal::recordset_alias; + template + using alias_j = internal::recordset_alias; + template + using alias_k = internal::recordset_alias; + template + using alias_l = internal::recordset_alias; + template + using alias_m = internal::recordset_alias; + template + using alias_n = internal::recordset_alias; + template + using alias_o = internal::recordset_alias; + template + using alias_p = internal::recordset_alias; + template + using alias_q = internal::recordset_alias; + template + using alias_r = internal::recordset_alias; + template + using alias_s = internal::recordset_alias; + template + using alias_t = internal::recordset_alias; + template + using alias_u = internal::recordset_alias; + template + using alias_v = internal::recordset_alias; + template + using alias_w = internal::recordset_alias; + template + using alias_x = internal::recordset_alias; + template + using alias_y = internal::recordset_alias; + template + using alias_z = internal::recordset_alias; + + using colalias_a = internal::column_alias<'a'>; + using colalias_b = internal::column_alias<'b'>; + using colalias_c = internal::column_alias<'c'>; + using colalias_d = internal::column_alias<'d'>; + using colalias_e = internal::column_alias<'e'>; + using colalias_f = internal::column_alias<'f'>; + using colalias_g = internal::column_alias<'g'>; + using colalias_h = internal::column_alias<'h'>; + using colalias_i = internal::column_alias<'i'>; +} +#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 "functional/cxx_type_traits_polyfill.h" + +// #include "conditions.h" + +// #include "is_base_of_template.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "serialize_result_type.h" + +// #include "operators.h" + +// #include "ast/into.h" + +// #include "../functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { + + template + struct into_t { + using type = T; + }; + + template + using is_into = polyfill::is_specialization_of; + } + + template + internal::into_t into() { + return {}; + } +} + +namespace sqlite_orm { + + using int64 = sqlite_int64; + using uint64 = sqlite_uint64; + + namespace internal { + + 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 size_t args_size = std::tuple_size::value; + + args_type args; + + built_in_function_t(args_type&& args_) : args(std::move(args_)) {} + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_built_in_function_v = is_base_of_template_v; + + template + using is_built_in_function = polyfill::bool_constant>; + + template + struct filtered_aggregate_function { + using function_type = F; + using where_expression = W; + + function_type function; + where_expression where; + }; + + template + struct where_t; + + template + struct built_in_aggregate_function_t : built_in_function_t { + using super = built_in_function_t; + + using super::super; + + template + filtered_aggregate_function, W> filter(where_t wh) { + return {*this, std::move(wh.expression)}; + } + }; + + 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 nullif_string { + serialize_result_type serialize() const { + return "NULLIF"; + } + }; + + 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; + + template + filtered_aggregate_function, W> filter(where_t wh) { + return {*this, std::move(wh.expression)}; + } + }; + + /** + * 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 json_group_object_string { + serialize_result_type serialize() const { + return "JSON_GROUP_OBJECT"; + } + }; +#endif // SQLITE_ENABLE_JSON1 + + template + using field_type_or_type_t = polyfill::detected_or_t>; + } + + /** + * Cute operators for core functions + */ + template = true> + internal::lesser_than_t operator<(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template = true> + internal::lesser_or_equal_t operator<=(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template = true> + internal::greater_than_t operator>(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template = true> + internal::greater_or_equal_t operator>=(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template = true> + internal::is_equal_t operator==(F f, R r) { + return {std::move(f), std::move(r)}; + } + + template = true> + 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::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::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::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::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::built_in_function_t changes() { + return {{}}; + } + + /** + * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim + */ + template + 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::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::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::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::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::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::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::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::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::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::is_into>::value == 0, bool> = true> + internal::built_in_function_t 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::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::built_in_function_t round(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + +#if SQLITE_VERSION_NUMBER >= 3007016 + + /** + * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char + */ + template + 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::built_in_function_t random() { + return {{}}; + } + +#endif + + /** + * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce + */ + template + auto coalesce(Args... args) + -> internal::built_in_function_t::value, + std::common_type...>, + polyfill::type_identity>::type, + internal::coalesce_string, + Args...> { + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * IFNULL(X,Y) function https://www.sqlite.org/lang_corefunc.html#ifnull + */ + template + auto ifnull(X x, Y y) -> internal::built_in_function_t< + typename std::conditional_t< // choose R or common type + std::is_void::value, + std::common_type, internal::field_type_or_type_t>, + polyfill::type_identity>::type, + internal::ifnull_string, + X, + Y> { + return {std::make_tuple(std::move(x), std::move(y))}; + } + + /** + * NULLIF(X,Y) function https://www.sqlite.org/lang_corefunc.html#nullif + */ +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + /** + * NULLIF(X,Y) using common return type of X and Y + */ + template>, + polyfill::is_detected, + internal::field_type_or_type_t>>, + bool> = true> + auto nullif(X x, Y y) { + if constexpr(std::is_void_v) { + using F = internal::built_in_function_t< + std::optional, internal::field_type_or_type_t>>, + internal::nullif_string, + X, + Y>; + + return F{std::make_tuple(std::move(x), std::move(y))}; + } else { + using F = internal::built_in_function_t; + + return F{std::make_tuple(std::move(x), std::move(y))}; + } + } +#else + template + internal::built_in_function_t nullif(X x, Y y) { + return {std::make_tuple(std::move(x), std::move(y))}; + } +#endif + + /** + * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + 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::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::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::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::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::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::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::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 + /** + * SOUNDEX(X) function https://www.sqlite.org/lang_corefunc.html#soundex + */ + template + internal::built_in_function_t soundex(X x) { + return {std::tuple{std::forward(x)}}; + } +#endif + + /** + * TOTAL(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t total(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * SUM(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t, internal::sum_string, X> sum(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * COUNT(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t count(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * COUNT(*) without FROM function. + */ + inline internal::count_asterisk_without_type count() { + return {}; + } + + /** + * COUNT(*) with FROM function. Specified type T will be serializeed as + * a from argument. + */ + template + internal::count_asterisk_t count() { + return {}; + } + + /** + * AVG(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t avg(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * MAX(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t, internal::max_string, X> max(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * MIN(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t, internal::min_string, X> min(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * MAX(X, Y, ...) scalar function. + * The return type is the type of the first argument. + */ + template + internal::built_in_function_t, internal::max_string, X, Y, Rest...> + max(X x, Y y, Rest... rest) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; + } + + /** + * MIN(X, Y, ...) scalar function. + * The return type is the type of the first argument. + */ + template + internal::built_in_function_t, internal::min_string, X, Y, Rest...> + min(X x, Y y, Rest... rest) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; + } + + /** + * GROUP_CONCAT(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t group_concat(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * GROUP_CONCAT(X, Y) aggregate function. + */ + template + internal::built_in_aggregate_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, + std::is_base_of>, + bool> = true> + internal::add_t operator+(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template, + std::is_base_of>, + bool> = true> + internal::sub_t operator-(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template, + std::is_base_of>, + bool> = true> + internal::mul_t operator*(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template, + std::is_base_of>, + bool> = true> + internal::div_t operator/(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template, + std::is_base_of>, + bool> = true> + internal::mod_t operator%(L l, R r) { + return {std::move(l), std::move(r)}; + } +} +#pragma once + +namespace sqlite_orm { + + namespace internal { + + template + bool compare_any(const L& /*lhs*/, const R& /*rhs*/) { + return false; + } + template + bool compare_any(const O& lhs, const O& rhs) { + return lhs == rhs; + } + } +} +#pragma once + +#include // std::remove_const +#include // std::string +#include // std::move +#include // std::tuple, std::get, std::tuple_size +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "is_base_of_template.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "optional_container.h" + +// #include "ast/where.h" + +#include // std::false_type, std::true_type +#include // std::move + +// #include "../functional/cxx_universal.h" + +// #include "../functional/cxx_type_traits_polyfill.h" + +// #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 + SQLITE_ORM_INLINE_VAR constexpr bool is_where_v = polyfill::is_specialization_of_v; + + template + using is_where = polyfill::bool_constant>; + } + + /** + * 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)}; + } +} + +// #include "ast/group_by.h" + +#include // std::tuple, std::make_tuple +#include // std::true_type, std::false_type +#include // std::forward, std::move + +// #include "../functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { + + template + struct group_by_with_having { + using args_type = std::tuple; + using expression_type = T; + + args_type args; + expression_type expression; + }; + + /** + * GROUP BY pack holder. + */ + template + struct group_by_t { + using args_type = std::tuple; + + args_type args; + + template + group_by_with_having having(T expression) { + return {std::move(this->args), std::move(expression)}; + } + }; + + template + using is_group_by = polyfill::disjunction, + polyfill::is_specialization_of>; + + /** + * HAVING holder. + * T is having argument type. + */ + template + struct having_t { + using expression_type = T; + + expression_type expression; + }; + + template + using is_having = polyfill::is_specialization_of; + } + + /** + * 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)...)}; + } + + /** + * [Deprecation notice]: this function is deprecated and will be removed in v1.9. Please use `group_by(...).having(...)` instead. + * + * HAVING(expression). + * Example: storage.get_all(group_by(&Employee::name), having(greater_than(count(&Employee::name), 2))); + */ + template + [[deprecated("Use group_by(...).having(...) instead")]] internal::having_t having(T expression) { + return {std::move(expression)}; + } +} + +// #include "core_functions.h" + +// #include "alias_traits.h" + +// #include "column_pointer.h" + +#include // std::string +#include // std::move + +// #include "functional/cxx_core_features.h" + +// #include "conditions.h" + +// #include "alias_traits.h" + +namespace sqlite_orm { + namespace internal { + /** + * This class is used to store explicit mapped type T and its column descriptor (member pointer/getter/setter). + * Is useful when mapped type is derived from other type and base class has members mapped to a storage. + */ + template + struct column_pointer { + using self = column_pointer; + using type = T; + using field_type = F; + + field_type field; + + template + is_equal_t operator==(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + is_not_equal_t operator!=(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + lesser_than_t operator<(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + lesser_or_equal_t operator<=(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + greater_than_t operator>(R rhs) const { + return {*this, std::move(rhs)}; + } + + template + greater_or_equal_t operator>=(R rhs) const { + return {*this, std::move(rhs)}; + } + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_column_pointer_v = polyfill::is_specialization_of_v; + + template + using is_column_pointer = polyfill::bool_constant>; + + } +} + +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 : 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; + + columns_type columns; + bool distinct = false; + + static constexpr int count = std::tuple_size::value; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + columns_t(columns_type columns) : columns{std::move(columns)} {} +#endif + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_columns_v = polyfill::is_specialization_of_v; + + template + using is_columns = polyfill::bool_constant>; + + /** + * Subselect object type. + */ + template + struct select_t { + using return_type = T; + using conditions_type = std::tuple; + + return_type col; + conditions_type conditions; + bool highest_level = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + select_t(return_type col, conditions_type conditions) : + col{std::move(col)}, conditions{std::move(conditions)} {} +#endif + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_select_v = polyfill::is_specialization_of_v; + + template + using is_select = polyfill::bool_constant>; + + /** + * Base for UNION, UNION ALL, EXCEPT and INTERSECT + */ + template + struct compound_operator { + using left_type = L; + using right_type = R; + + left_type left; + right_type right; + + compound_operator(left_type l, right_type r) : left(std::move(l)), right(std::move(r)) { + this->left.highest_level = true; + this->right.highest_level = true; + } + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_of_template_v; + + template + using is_compound_operator = polyfill::bool_constant>; + + struct union_base { + bool all = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + union_base(bool all) : all{all} {} +#endif + + operator std::string() const { + if(!this->all) { + return "UNION"; + } else { + return "UNION ALL"; + } + } + }; + + /** + * UNION object type. + */ + template + struct union_t : public compound_operator, union_base { + using left_type = typename compound_operator::left_type; + using right_type = typename compound_operator::right_type; + + union_t(left_type l, right_type r, bool all_) : + compound_operator{std::move(l), std::move(r)}, union_base{all_} {} + + 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 : 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 "INTERSECT"; + } + }; + /** + * INTERSECT object type. + */ + template + 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; + }; + + /** + * Generic way to get DISTINCT value from any type. + */ + template + bool get_distinct(const T&) { + return false; + } + + template + bool get_distinct(const columns_t& cols) { + return cols.distinct; + } + + template + struct asterisk_t { + using type = T; + + bool defined_order = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + asterisk_t(bool definedOrder) : defined_order{definedOrder} {} +#endif + }; + + template + struct object_t { + using type = T; + + bool defined_order = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + object_t(bool definedOrder) : defined_order{definedOrder} {} +#endif + }; + + template + struct then_t { + using expression_type = T; + + expression_type expression; + }; + + template + struct simple_case_t { + using return_type = R; + using case_expression_type = T; + using args_type = std::tuple; + using else_expression_type = E; + + optional_container case_expression; + args_type args; + optional_container else_expression; + }; + + /** + * T is a case expression type + * E is else type (void is ELSE is omitted) + * Args... is a pack of WHEN expressions + */ + template + struct simple_case_builder { + using return_type = R; + using case_expression_type = T; + using args_type = std::tuple; + using else_expression_type = E; + + optional_container case_expression; + args_type args; + optional_container else_expression; + + template + simple_case_builder> when(W w, then_t

t) { + using result_args_type = std::tuple>; + std::pair newPair{std::move(w), std::move(t.expression)}; + result_args_type result_args = std::tuple_cat(std::move(this->args), std::make_tuple(newPair)); + std::get::value - 1>(result_args) = std::move(newPair); + return {std::move(this->case_expression), std::move(result_args), std::move(this->else_expression)}; + } + + simple_case_t end() { + return {std::move(this->case_expression), std::move(args), std::move(this->else_expression)}; + } + + template + simple_case_builder else_(El el) { + return {{std::move(this->case_expression)}, std::move(args), {std::move(el)}}; + } + }; + + template + void validate_conditions() { + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 WHERE blocks"); + 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)}; + } + + template + internal::simple_case_builder case_(T t) { + return {{std::move(t)}}; + } + + template + internal::simple_case_builder case_() { + return {}; + } + + template + internal::distinct_t distinct(T t) { + return {std::move(t)}; + } + + template + internal::all_t all(T t) { + return {std::move(t)}; + } + + template + internal::columns_t distinct(internal::columns_t cols) { + cols.distinct = true; + return cols; + } + + template + internal::columns_t columns(Args... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * Use it like this: + * struct MyType : BaseType { ... }; + * storage.select(column(&BaseType::id)); + */ + template + internal::column_pointer column(F f) { + return {std::move(f)}; + } + + /** + * Public function for subselect query. Is useful in UNION queries. + */ + template + internal::select_t select(T t, Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + return {std::move(t), std::make_tuple(std::forward(args)...)}; + } + + /** + * Public function for UNION operator. + * lhs and rhs are subselect objects. + * Look through example in examples/union.cpp + */ + template + internal::union_t union_(L lhs, R rhs) { + return {std::move(lhs), std::move(rhs)}; + } + + /** + * Public function for EXCEPT operator. + * lhs and rhs are subselect objects. + * Look through example in examples/except.cpp + */ + template + internal::except_t except(L lhs, R rhs) { + return {std::move(lhs), std::move(rhs)}; + } + + template + internal::intersect_t intersect(L lhs, R rhs) { + return {std::move(lhs), std::move(rhs)}; + } + + /** + * Public function for UNION ALL operator. + * lhs and rhs are subselect objects. + * Look through example in examples/union.cpp + */ + template + internal::union_t union_all(L lhs, R rhs) { + return {std::move(lhs), std::move(rhs), true}; + } + + /** + * `SELECT * FROM T` expression that fetches results as tuples. + * T is a type mapped to a storage, or an alias of it. + * The `definedOrder` parameter denotes the expected order of result columns. + * The default is the implicit order as returned by SQLite, which may differ from the defined order + * if the schema of a table has been changed. + * By specifying the defined order, the columns are written out in the resulting select SQL string. + * + * In pseudo code: + * select(asterisk(false)) -> SELECT * from User + * select(asterisk(true)) -> SELECT id, name from User + * + * Example: auto rows = storage.select(asterisk()); + * // decltype(rows) is std::vector> + * Example: auto rows = storage.select(asterisk(true)); + * // decltype(rows) is std::vector> + * + * If you need to fetch results as objects instead of tuples please use `object()`. + */ + template + internal::asterisk_t asterisk(bool definedOrder = false) { + return {definedOrder}; + } + + /** + * `SELECT * FROM T` expression that fetches results as objects of type T. + * T is a type mapped to a storage, or an alias of it. + * + * Example: auto rows = storage.select(object()); + * // decltype(rows) is std::vector, where the User objects are constructed from columns in implicitly stored order + * Example: auto rows = storage.select(object(true)); + * // decltype(rows) is std::vector, where the User objects are constructed from columns in declared make_table order + * + * If you need to fetch results as tuples instead of objects please use `asterisk()`. + */ + template + internal::object_t object(bool definedOrder = false) { + return {definedOrder}; + } +} +#pragma once + +#include // std::string + +// #include "functional/cxx_universal.h" + +namespace sqlite_orm { + + struct table_info { + int cid = 0; + std::string name; + std::string type; + bool notnull = false; + std::string dflt_value; + int pk = 0; + +#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) + 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(std::move(name_)), type(std::move(type_)), notnull(notnull_), dflt_value(std::move(dflt_value_)), + pk(pk_) {} +#endif + }; + + struct table_xinfo { + int cid = 0; + std::string name; + std::string type; + bool notnull = false; + std::string dflt_value; + int pk = 0; + int hidden = 0; // different than 0 => generated_always_as() - TODO verify + +#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) + table_xinfo(decltype(cid) cid_, + decltype(name) name_, + decltype(type) type_, + decltype(notnull) notnull_, + decltype(dflt_value) dflt_value_, + decltype(pk) pk_, + decltype(hidden) hidden_) : + cid(cid_), + name(std::move(name_)), type(std::move(type_)), notnull(notnull_), dflt_value(std::move(dflt_value_)), + pk(pk_), hidden{hidden_} {} +#endif + }; +} +#pragma once + +#include +#include +#include +#include + +// #include "functional/cxx_universal.h" + +// #include "optional_container.h" + +// NOTE Idea : Maybe also implement a custom trigger system to call a c++ callback when a trigger triggers ? +// (Could be implemented with a normal trigger that insert or update an internal table and then retreive +// the event in the C++ code, to call the C++ user callback, with update hooks: https://www.sqlite.org/c3ref/update_hook.html) +// It could be an interesting feature to bring to sqlite_orm that other libraries don't have ? + +namespace sqlite_orm { + namespace internal { + enum class trigger_timing { trigger_before, trigger_after, trigger_instead_of }; + enum class trigger_type { trigger_delete, trigger_insert, trigger_update }; + + /** + * This class is an intermediate SQLite trigger, to be used with + * `make_trigger` to create a full trigger. + * T is the base of the trigger (contains its type, timing and associated table) + * S is the list of trigger statements + */ + template + struct partial_trigger_t { + using statements_type = std::tuple; + + /** + * Base of the trigger (contains its type, timing and associated table) + */ + T base; + /** + * Statements of the triggers (to be executed when the trigger fires) + */ + statements_type statements; + + partial_trigger_t(T trigger_base, S... statements) : + base{std::move(trigger_base)}, statements{std::make_tuple(std::forward(statements)...)} {} + + partial_trigger_t& end() { + return *this; + } + }; + + struct base_trigger { + /** + * Name of the trigger + */ + std::string name; + }; + + /** + * This class represent a SQLite trigger + * T is the base of the trigger (contains its type, timing and associated table) + * S is the list of trigger statments + */ + template + struct trigger_t : base_trigger { + using object_type = void; + using elements_type = typename partial_trigger_t::statements_type; + + /** + * Base of the trigger (contains its type, timing and associated table) + */ + T base; + + /** + * Statements of the triggers (to be executed when the trigger fires) + */ + elements_type elements; + +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + trigger_t(std::string name, T trigger_base, elements_type statements) : + base_trigger{std::move(name)}, base(std::move(trigger_base)), elements(std::move(statements)) {} +#endif + }; + + /** + * Base of a trigger. Contains the trigger type/timming and the table type + * T is the table type + * W is `when` expression type + * Type is the trigger base type (type+timing) + */ + template + struct trigger_base_t { + using table_type = T; + using when_type = W; + using trigger_type_base = Type; + + /** + * Contains the trigger type and timing + */ + trigger_type_base type_base; + /** + * Value used to determine if we execute the trigger on each row or on each statement + * (SQLite doesn't support the FOR EACH STATEMENT syntax yet: https://sqlite.org/lang_createtrigger.html#description + * so this value is more of a placeholder for a later update) + */ + bool do_for_each_row = false; + /** + * When expression (if any) + * If a WHEN expression is specified, the trigger will only execute + * if the expression evaluates to true when the trigger is fired + */ + optional_container container_when; + + trigger_base_t(trigger_type_base type_base_) : type_base(std::move(type_base_)) {} + + trigger_base_t& for_each_row() { + this->do_for_each_row = true; + return *this; + } + + template + trigger_base_t when(WW expression) { + trigger_base_t res(this->type_base); + res.container_when.field = std::move(expression); + return res; + } + + template + partial_trigger_t, S...> begin(S... statements) { + return {*this, std::forward(statements)...}; + } + }; + + /** + * Contains the trigger type and timing + */ + struct trigger_type_base_t { + /** + * Value indicating if the trigger is run BEFORE, AFTER or INSTEAD OF + * the statement that fired it. + */ + trigger_timing timing; + /** + * The type of the statement that would cause the trigger to fire. + * Can be DELETE, INSERT, or UPDATE. + */ + trigger_type type; + + trigger_type_base_t(trigger_timing timing, trigger_type type) : timing(timing), type(type) {} + + template + trigger_base_t on() { + return {*this}; + } + }; + + /** + * Special case for UPDATE OF (columns) + * Contains the trigger type and timing + */ + template + struct trigger_update_type_t : trigger_type_base_t { + using columns_type = std::tuple; + + /** + * Contains the columns the trigger is watching. Will only + * trigger if one of theses columns is updated. + */ + columns_type columns; + + trigger_update_type_t(trigger_timing timing, trigger_type type, Cs... columns) : + trigger_type_base_t(timing, type), columns(std::make_tuple(std::forward(columns)...)) {} + + template + trigger_base_t> on() { + return {*this}; + } + }; + + struct trigger_timing_t { + trigger_timing timing; + + trigger_type_base_t delete_() { + return {timing, trigger_type::trigger_delete}; + } + + trigger_type_base_t insert() { + return {timing, trigger_type::trigger_insert}; + } + + trigger_type_base_t update() { + return {timing, trigger_type::trigger_update}; + } + + template + trigger_update_type_t update_of(Cs... columns) { + return {timing, trigger_type::trigger_update, std::forward(columns)...}; + } + }; + + struct raise_t { + enum class type_t { + ignore, + rollback, + abort, + fail, + }; + + type_t type = type_t::ignore; + std::string message; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + raise_t(type_t type, std::string message) : type{type}, message{std::move(message)} {} +#endif + }; + + template + struct new_t { + using expression_type = T; + + expression_type expression; + }; + + template + struct old_t { + using expression_type = T; + + expression_type expression; + }; + } // NAMESPACE internal + + /** + * NEW.expression function used within TRIGGER expressions + */ + template + internal::new_t new_(T expression) { + return {std::move(expression)}; + } + + /** + * OLD.expression function used within TRIGGER expressions + */ + template + internal::old_t old(T expression) { + return {std::move(expression)}; + } + + /** + * RAISE(IGNORE) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_ignore() { + return {internal::raise_t::type_t::ignore, {}}; + } + + /** + * RAISE(ROLLBACK, %message%) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_rollback(std::string message) { + return {internal::raise_t::type_t::rollback, std::move(message)}; + } + + /** + * RAISE(ABORT, %message%) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_abort(std::string message) { + return {internal::raise_t::type_t::abort, std::move(message)}; + } + + /** + * RAISE(FAIL, %message%) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_fail(std::string message) { + return {internal::raise_t::type_t::fail, std::move(message)}; + } + + template + internal::trigger_t make_trigger(std::string name, const internal::partial_trigger_t& part) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::move(part.base), std::move(part.statements)}); + } + + inline internal::trigger_timing_t before() { + return {internal::trigger_timing::trigger_before}; + } + + inline internal::trigger_timing_t after() { + return {internal::trigger_timing::trigger_after}; + } + + inline internal::trigger_timing_t instead_of() { + return {internal::trigger_timing::trigger_instead_of}; + } +} +#pragma once + +#include +#include // std::unique_ptr +#include // std::integral_constant + +namespace sqlite_orm { + + /** + * Guard class which finalizes `sqlite3_stmt` in dtor + */ + using statement_finalizer = + std::unique_ptr>; +} +#pragma once +#include + +namespace sqlite_orm { + + /** + * Helper classes used by statement_binder and row_extractor. + */ + struct int_or_smaller_tag {}; + struct bigint_tag {}; + struct real_tag {}; + + template + using arithmetic_tag_t = + std::conditional_t::value, + // Integer class + std::conditional_t, + // Floating-point class + real_tag>; +} +#pragma once + +#include +#include +#include + +// #include "functional/cxx_universal.h" + +// #include "xdestroy_handling.h" + +#include // std::integral_constant +#if defined(SQLITE_ORM_CONCEPTS_SUPPORTED) && SQLITE_ORM_HAS_INCLUDE() +#include +#endif + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + + using xdestroy_fn_t = void (*)(void*); + using null_xdestroy_t = std::integral_constant; + SQLITE_ORM_INLINE_VAR constexpr null_xdestroy_t null_xdestroy_f{}; +} + +namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_CONCEPTS_SUPPORTED + /** + * Constrains a deleter to be state-less. + */ + template + concept stateless_deleter = std::is_empty_v && std::is_default_constructible_v; + + /** + * Constrains a deleter to be an integral function constant. + */ + template + concept integral_fp_c = requires { + typename D::value_type; + D::value; + requires std::is_function_v>; + }; + + /** + * Constrains a deleter to be or to yield a function pointer. + */ + template + concept yields_fp = requires(D d) { + // yielding function pointer by using the plus trick + { +d }; + requires std::is_function_v>; + }; +#endif + +#if __cpp_lib_concepts >= 201907L + /** + * Yield a deleter's function pointer. + */ + template + struct yield_fp_of { + using type = decltype(+std::declval()); + }; +#else + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_stateless_deleter_v = + std::is_empty::value && std::is_default_constructible::value; + + template + struct is_integral_fp_c : std::false_type {}; + template + struct is_integral_fp_c< + D, + polyfill::void_t>::value>>> + : std::true_type {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_integral_fp_c_v = is_integral_fp_c::value; + + template + struct can_yield_fp : std::false_type {}; + template + struct can_yield_fp< + D, + polyfill::void_t< + decltype(+std::declval()), + std::enable_if_t())>>::value>>> + : std::true_type {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool can_yield_fp_v = can_yield_fp::value; + + template> + struct yield_fp_of { + using type = void; + }; + template + struct yield_fp_of { + using type = decltype(+std::declval()); + }; +#endif + template + using yielded_fn_t = typename yield_fp_of::type; + +#if __cpp_lib_concepts >= 201907L + template + concept is_unusable_for_xdestroy = (!stateless_deleter && + (yields_fp && !std::convertible_to, xdestroy_fn_t>)); + + /** + * This concept tests whether a deleter yields a function pointer, which is convertible to an xdestroy function pointer. + * Note: We are using 'is convertible' rather than 'is same' because of any exception specification. + */ + template + concept yields_xdestroy = yields_fp && std::convertible_to, xdestroy_fn_t>; + + template + concept needs_xdestroy_proxy = (stateless_deleter && + (!yields_fp || !std::convertible_to, xdestroy_fn_t>)); + + /** + * xDestroy function that constructs and invokes the stateless deleter. + * + * Requires that the deleter can be called with the q-qualified pointer argument; + * it doesn't check so explicitly, but a compiler error will occur. + */ + template + requires(!integral_fp_c) + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder> + auto o = (P*)p; + // ignoring return code + (void)D{}(o); + } + + /** + * xDestroy function that invokes the integral function pointer constant. + * + * Performs a const-cast of the argument pointer in order to allow for C API functions + * that take a non-const parameter, but user code passes a pointer to a const object. + */ + template + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder>, + auto o = (std::remove_cv_t

*)(P*)p; + // ignoring return code + (void)D{}(o); + } +#else + template + SQLITE_ORM_INLINE_VAR constexpr bool is_unusable_for_xdestroy_v = + !is_stateless_deleter_v && + (can_yield_fp_v && !std::is_convertible, xdestroy_fn_t>::value); + + template + SQLITE_ORM_INLINE_VAR constexpr bool can_yield_xdestroy_v = + can_yield_fp_v && std::is_convertible, xdestroy_fn_t>::value; + + template + SQLITE_ORM_INLINE_VAR constexpr bool needs_xdestroy_proxy_v = + is_stateless_deleter_v && + (!can_yield_fp_v || !std::is_convertible, xdestroy_fn_t>::value); + + template, bool> = true> + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder> + auto o = (P*)p; + // ignoring return code + (void)D{}(o); + } + + template, bool> = true> + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder>, + auto o = (std::remove_cv_t

*)(P*)p; + // ignoring return code + (void)D{}(o); + } +#endif + } +} + +namespace sqlite_orm { + +#if __cpp_lib_concepts >= 201907L + /** + * Prohibits using a yielded function pointer, which is not of type xdestroy_fn_t. + * + * Explicitly declared for better error messages. + */ + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept + requires(internal::is_unusable_for_xdestroy) + { + static_assert(polyfill::always_false_v, + "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); + return nullptr; + } + + /** + * Obtains a proxy 'xDestroy' function pointer [of type void(*)(void*)] + * for a deleter in a type-safe way. + * + * The deleter can be one of: + * - integral function constant + * - state-less (empty) deleter + * - non-capturing lambda + * + * Type-safety is garanteed by checking whether the deleter or yielded function pointer + * is invocable with the non-q-qualified pointer value. + */ + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept + requires(internal::needs_xdestroy_proxy) + { + return internal::xdestroy_proxy; + } + + /** + * Directly obtains a 'xDestroy' function pointer [of type void(*)(void*)] + * from a deleter in a type-safe way. + * + * The deleter can be one of: + * - function pointer of type xdestroy_fn_t + * - structure holding a function pointer + * - integral function constant + * - non-capturing lambda + * ... and yield a function pointer of type xdestroy_fn_t. + * + * Type-safety is garanteed by checking whether the deleter or yielded function pointer + * is invocable with the non-q-qualified pointer value. + */ + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P*) noexcept + requires(internal::yields_xdestroy) + { + return d; + } +#else + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) { + static_assert(polyfill::always_false_v, + "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); + return nullptr; + } + + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept { + return internal::xdestroy_proxy; + } + + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P*) noexcept { + return d; + } +#endif +} + +namespace sqlite_orm { + + /** + * Wraps a pointer and tags it with a pointer type, + * used for accepting function parameters, + * facilitating the 'pointer-passing interface'. + * + * Template parameters: + * - P: The value type, possibly const-qualified. + * - T: An integral constant string denoting the pointer type, e.g. `carray_pvt_name`. + * + */ + template + struct pointer_arg { + + static_assert(std::is_convertible::value, + "`std::integral_constant<>` must be convertible to `const char*`"); + + using tag = T; + P* p_; + + P* ptr() const noexcept { + return p_; + } + + operator P*() const noexcept { + return p_; + } + }; + + /** + * Pointer value with associated deleter function, + * used for returning or binding pointer values + * as part of facilitating the 'pointer-passing interface'. + * + * Template parameters: + * - D: The deleter for the pointer value; + * can be one of: + * - function pointer + * - integral function pointer constant + * - state-less (empty) deleter + * - non-capturing lambda + * - structure implicitly yielding a function pointer + * + * @note Use one of the factory functions to create a pointer binding, + * e.g. bindable_carray_pointer or statically_bindable_carray_pointer(). + * + * @example + * ``` + * int64 rememberedId; + * storage.select(func(&Object::id, statically_bindable_carray_pointer(&rememberedId))); + * ``` + */ + template + class pointer_binding { + + P* p_; + SQLITE_ORM_NOUNIQUEADDRESS + D d_; + + protected: + // Constructing pointer bindings must go through bindable_pointer() + template + friend auto bindable_pointer(P2*, D2) noexcept -> pointer_binding; + template + friend B bindable_pointer(typename B::qualified_type*, typename B::deleter_type) noexcept; + + // Construct from pointer and deleter. + // Transfers ownership of the passed in object. + pointer_binding(P* p, D d = {}) noexcept : p_{p}, d_{std::move(d)} {} + + public: + using qualified_type = P; + using tag = T; + using deleter_type = D; + + pointer_binding(const pointer_binding&) = delete; + pointer_binding& operator=(const pointer_binding&) = delete; + pointer_binding& operator=(pointer_binding&&) = delete; + + pointer_binding(pointer_binding&& other) noexcept : + p_{std::exchange(other.p_, nullptr)}, d_{std::move(other.d_)} {} + + ~pointer_binding() { + if(p_) { + if(auto xDestroy = get_xdestroy()) { + // note: C-casting `P* -> void*` like statement_binder> + xDestroy((void*)p_); + } + } + } + + P* ptr() const noexcept { + return p_; + } + + P* take_ptr() noexcept { + return std::exchange(p_, nullptr); + } + + xdestroy_fn_t get_xdestroy() const noexcept { + return obtain_xdestroy_for(d_, p_); + } + }; + + /** + * Template alias for a static pointer value binding. + * 'Static' means that ownership won't be transferred to sqlite, + * sqlite doesn't delete it, and sqlite assumes the object + * pointed to is valid throughout the lifetime of a statement. + */ + template + using static_pointer_binding = pointer_binding; +} + +namespace sqlite_orm { + + /** + * Wrap a pointer, its type and its deleter function for binding it to a statement. + * + * Unless the deleter yields a nullptr 'xDestroy' function the ownership of the pointed-to-object + * is transferred to the pointer binding, which will delete it through + * the deleter when the statement finishes. + */ + template + auto bindable_pointer(P* p, D d) noexcept -> pointer_binding { + return {p, std::move(d)}; + } + + template + auto bindable_pointer(std::unique_ptr p) noexcept -> pointer_binding { + return bindable_pointer(p.release(), p.get_deleter()); + } + + template + B bindable_pointer(typename B::qualified_type* p, typename B::deleter_type d = {}) noexcept { + return B{p, std::move(d)}; + } + + /** + * Wrap a pointer and its type for binding it to a statement. + * + * Note: 'Static' means that ownership of the pointed-to-object won't be transferred + * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. + */ + template + auto statically_bindable_pointer(P* p) noexcept -> static_pointer_binding { + return bindable_pointer(p, null_xdestroy_f); + } + + template + B statically_bindable_pointer(typename B::qualified_type* p, + typename B::deleter_type* /*exposition*/ = nullptr) noexcept { + return bindable_pointer(p); + } + + /** + * Forward a pointer value from an argument. + */ + template + auto rebind_statically(const pointer_arg& pv) noexcept -> static_pointer_binding { + return statically_bindable_pointer(pv.ptr()); + } +} +#pragma once + +#include +#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type, std::make_index_sequence, std::index_sequence +#include // std::default_delete +#include // std::string, std::wstring +#include // std::vector +#include // ::strncpy, ::strlen +// #include "functional/cxx_string_view.h" + +#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // ::wcsncpy, ::wcslen +#endif + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/cxx_functional_polyfill.h" + +#include +#if __cpp_lib_invoke < 201411L +#include // std::enable_if, std::is_member_object_pointer, std::is_member_function_pointer +#endif +#include // std::forward + +#if __cpp_lib_invoke < 201411L +// #include "cxx_type_traits_polyfill.h" + +#endif +// #include "../member_traits/member_traits.h" + +namespace sqlite_orm { + namespace internal { + namespace polyfill { + // C++20 or later (unfortunately there's no feature test macro). + // Stupidly, clang says C++20, but `std::identity` was only implemented in libc++ 13 and libstd++-v3 10 + // (the latter is used on Linux). + // gcc got it right and reports C++20 only starting with v10. + // The check here doesn't care and checks the library versions in use. + // + // Another way of detection would be the constrained algorithms feature macro __cpp_lib_ranges +#if(__cplusplus >= 202002L) && \ + ((!_LIBCPP_VERSION || _LIBCPP_VERSION >= 13) && (!_GLIBCXX_RELEASE || _GLIBCXX_RELEASE >= 10)) + using std::identity; +#else + struct identity { + template + constexpr T&& operator()(T&& v) const noexcept { + return std::forward(v); + } + + using is_transparent = int; + }; +#endif + +#if __cpp_lib_invoke >= 201411L + using std::invoke; +#else + // pointer-to-data-member+object + template, + std::enable_if_t::value, bool> = true> + decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { + return std::forward(object).*callable; + } + + // pointer-to-member-function+object + template, + std::enable_if_t::value, bool> = true> + decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { + return (std::forward(object).*callable)(std::forward(args)...); + } + + // pointer-to-member+reference-wrapped object (expect `reference_wrapper::*`) + template>, + std::reference_wrapper>>, + bool> = true> + decltype(auto) invoke(Callable&& callable, std::reference_wrapper wrapper, Args&&... args) { + return invoke(std::forward(callable), wrapper.get(), std::forward(args)...); + } + + // functor + template + decltype(auto) invoke(Callable&& callable, Args&&... args) { + return std::forward(callable)(std::forward(args)...); + } +#endif + } + } + + namespace polyfill = internal::polyfill; +} + +// #include "is_std_ptr.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "error_code.h" + +// #include "arithmetic_tag.h" + +// #include "xdestroy_handling.h" + +// #include "pointer_value.h" + +namespace sqlite_orm { + + /** + * Helper class used for binding fields to sqlite3 statements. + */ + template + struct statement_binder; + + namespace internal { + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_bindable_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_bindable_v())>> = true + // note : msvc 14.0 needs the parentheses constructor, otherwise `is_bindable` isn't recognised. + // The strangest thing is that this is mutually exclusive with `is_printable_v`. + ; + + template + using is_bindable = polyfill::bool_constant>; + + } + + /** + * Specialization for 'pointer-passing interface'. + */ + template + struct statement_binder, void> { + using V = pointer_binding; + + // ownership of pointed-to-object is left untouched and remains at prepared statement's AST expression + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + // note: C-casting `P* -> void*`, internal::xdestroy_proxy() does the inverse + return sqlite3_bind_pointer(stmt, index, (void*)value.ptr(), T::value, null_xdestroy_f); + } + + // ownership of pointed-to-object is transferred to sqlite + void result(sqlite3_context* context, V& value) const { + // note: C-casting `P* -> void*`, + // row_extractor>::extract() and internal::xdestroy_proxy() do the inverse + sqlite3_result_pointer(context, (void*)value.take_ptr(), T::value, value.get_xdestroy()); + } + }; + + /** + * Specialization for arithmetic types. + */ + template + struct statement_binder::value>> { + + 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, int_or_smaller_tag) const { + return sqlite3_bind_int(stmt, index, static_cast(value)); + } + + void result(sqlite3_context* context, const V& value, int_or_smaller_tag) const { + sqlite3_result_int(context, static_cast(value)); + } + + int bind(sqlite3_stmt* stmt, int index, const V& value, bigint_tag) const { + return sqlite3_bind_int64(stmt, index, static_cast(value)); + } + + void result(sqlite3_context* context, const V& value, bigint_tag) const { + sqlite3_result_int64(context, static_cast(value)); + } + + int bind(sqlite3_stmt* stmt, int index, const V& value, real_tag) const { + return sqlite3_bind_double(stmt, index, static_cast(value)); + } + + void result(sqlite3_context* context, const V& value, real_tag) const { + sqlite3_result_double(context, static_cast(value)); + } + }; + + /** + * Specialization for std::string and C-string. + */ + template + struct statement_binder, + std::is_same +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + , + std::is_same +#endif + >>> { + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + auto stringData = this->string_data(value); + return sqlite3_bind_text(stmt, index, stringData.first, stringData.second, SQLITE_TRANSIENT); + } + + void result(sqlite3_context* context, const V& value) const { + auto stringData = this->string_data(value); + auto dataCopy = new char[stringData.second + 1]; + constexpr auto deleter = std::default_delete{}; + ::strncpy(dataCopy, stringData.first, stringData.second + 1); + sqlite3_result_text(context, dataCopy, stringData.second, obtain_xdestroy_for(deleter, dataCopy)); + } + + private: +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::pair string_data(const std::string_view& s) const { + return {s.data(), int(s.size())}; + } +#else + std::pair string_data(const std::string& s) const { + return {s.c_str(), int(s.size())}; + } + + std::pair string_data(const char* s) const { + return {s, int(::strlen(s))}; + } +#endif + }; + +#ifndef SQLITE_ORM_OMITS_CODECVT + template + struct statement_binder, + std::is_same +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + , + std::is_same +#endif + >>> { + + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + auto stringData = this->string_data(value); + std::wstring_convert> converter; + std::string utf8Str = converter.to_bytes(stringData.first, stringData.first + stringData.second); + return statement_binder().bind(stmt, index, utf8Str); + } + + void result(sqlite3_context* context, const V& value) const { + auto stringData = this->string_data(value); + sqlite3_result_text16(context, stringData.first, stringData.second, nullptr); + } + + private: +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::pair string_data(const std::wstring_view& s) const { + return {s.data(), int(s.size())}; + } +#else + std::pair string_data(const std::wstring& s) const { + return {s.c_str(), int(s.size())}; + } + + std::pair string_data(const wchar_t* s) const { + return {s, int(::wcslen(s))}; + } +#endif + }; +#endif + + /** + * Specialization for nullptr_t. + */ + template<> + struct statement_binder { + int bind(sqlite3_stmt* stmt, int index, const nullptr_t&) const { + return sqlite3_bind_null(stmt, index); + } + + void result(sqlite3_context* context, const 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&) 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 + + template + struct statement_binder< + V, + std::enable_if_t::value && internal::is_bindable_v>>> { + using unqualified_type = std::remove_cv_t; + + 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); + } + } + }; + + /** + * Specialization for binary data (std::vector). + */ + template<> + struct statement_binder, void> { + int bind(sqlite3_stmt* stmt, int index, const std::vector& value) const { + if(!value.empty()) { + 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.empty()) { + sqlite3_result_blob(context, (const void*)&value.front(), int(value.size()), nullptr); + } else { + sqlite3_result_blob(context, "", 0, nullptr); + } + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_binder && + internal::is_bindable_v>>> { + using unqualified_type = std::remove_cv_t; + + 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, std::nullopt); + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + namespace internal { + + struct conditional_binder { + sqlite3_stmt* stmt = nullptr; + int index = 1; + + explicit conditional_binder(sqlite3_stmt* stmt) : stmt{stmt} {} + + template = true> + void operator()(const T& t) { + int rc = statement_binder{}.bind(this->stmt, this->index++, t); + if(SQLITE_OK != rc) { + throw_translated_sqlite_error(this->stmt); + } + } + + template = true> + void operator()(const T&) const {} + }; + + struct field_value_binder : conditional_binder { + using conditional_binder::conditional_binder; + using conditional_binder::operator(); + + template = true> + void operator()(const T&) const = delete; + + template + void operator()(const T* value) { + if(!value) { + throw std::system_error{orm_error_code::value_is_null}; + } + (*this)(*value); + } + }; + + struct tuple_value_binder { + sqlite3_stmt* stmt = nullptr; + + explicit tuple_value_binder(sqlite3_stmt* stmt) : stmt{stmt} {} + + template + void operator()(const Tpl& tpl, Projection project) const { + (*this)(tpl, + std::make_index_sequence::value>{}, + std::forward(project)); + } + + private: +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { + (this->bind(polyfill::invoke(project, std::get(tpl)), Idx), ...); + } +#else + template + void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { + this->bind(polyfill::invoke(project, std::get(tpl)), I); + (*this)(tpl, std::index_sequence{}, std::forward(project)); + } + + template + void operator()(const Tpl&, std::index_sequence<>, Projection) const {} +#endif + + template + void bind(const T& t, size_t idx) const { + int rc = statement_binder{}.bind(this->stmt, int(idx + 1), t); + if(SQLITE_OK != rc) { + throw_translated_sqlite_error(this->stmt); + } + } + + template + void bind(const T* value, size_t idx) const { + if(!value) { + throw std::system_error{orm_error_code::value_is_null}; + } + (*this)(*value, idx); + } + }; + + template + using bindable_filter_t = filter_tuple_t; + } +} +#pragma once + +#include +#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if +#include // atof, atoi, atoll +#include // std::system_error +#include // std::string, std::wstring +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::wstring_convert, std::codecvt_utf8_utf16 +#endif // SQLITE_ORM_OMITS_CODECVT +#include // std::vector +#include // strlen +#include +#include // std::copy +#include // std::back_inserter +#include // std::tuple, std::tuple_size, std::tuple_element + +// #include "functional/cxx_universal.h" + +// #include "arithmetic_tag.h" + +// #include "pointer_value.h" + +// #include "journal_mode.h" + +#include // std::back_inserter +#include // std::string +#include // std::unique_ptr +#include // std::array +#include // std::transform +#include // std::toupper + +#if defined(_WINNT_) +// DELETE is a macro defined in the Windows SDK (winnt.h) +#pragma push_macro("DELETE") +#undef DELETE +#endif + +namespace sqlite_orm { + + /** + * Caps case because of: + * 1) delete keyword; + * 2) https://www.sqlite.org/pragma.html#pragma_journal_mode original spelling + */ + enum class journal_mode : signed char { + DELETE = 0, + // An alternate enumeration value when using the Windows SDK that defines DELETE as a macro. + DELETE_ = DELETE, + TRUNCATE = 1, + PERSIST = 2, + MEMORY = 3, + WAL = 4, + OFF = 5, + }; + + namespace internal { + + inline const std::string& to_string(journal_mode j) { + static std::string res[] = { + "DELETE", + "TRUNCATE", + "PERSIST", + "MEMORY", + "WAL", + "OFF", + }; + return res[static_cast(j)]; + } + + 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))); + }); + static std::array all = {{ + journal_mode::DELETE, + journal_mode::TRUNCATE, + journal_mode::PERSIST, + journal_mode::MEMORY, + journal_mode::WAL, + journal_mode::OFF, + }}; + for(auto j: all) { + if(to_string(j) == upper_str) { + return std::make_unique(j); + } + } + return {}; + } + } +} + +#if defined(_WINNT_) +#pragma pop_macro("DELETE") +#endif + +// #include "error_code.h" + +// #include "is_std_ptr.h" + +namespace sqlite_orm { + + /** + * Helper class used to cast values from argv to V class + * which depends from column type. + * + */ + template + struct row_extractor { + // used in sqlite3_exec (select) + V extract(const char* row_value) const = delete; + + // used in sqlite_column (iteration, get_all) + V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; + + // used in user defined functions + V extract(sqlite3_value* value) const = delete; + }; + + template + int extract_single_value(void* data, int argc, char** argv, char**) { + auto& res = *(R*)data; + if(argc) { + res = row_extractor{}.extract(argv[0]); + } + return 0; + } + + /** + * Specialization for the 'pointer-passing interface'. + * + * @note The 'pointer-passing' interface doesn't support (and in fact prohibits) + * extracting pointers from columns. + */ + template + struct row_extractor, void> { + using V = pointer_arg; + + V extract(sqlite3_value* value) const { + return {(P*)sqlite3_value_pointer(value, T::value)}; + } + }; + + /** + * Undefine using pointer_binding<> for querying values + */ + template + struct row_extractor, void>; + + /** + * Specialization for arithmetic types. + */ + template + struct row_extractor::value>> { + 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_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&) const { + return static_cast(atoi(row_value)); + } + + V extract(sqlite3_stmt* stmt, int columnIndex, const int_or_smaller_tag&) const { + return static_cast(sqlite3_column_int(stmt, columnIndex)); + } + + 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&) const { + return static_cast(sqlite3_column_int64(stmt, columnIndex)); + } + + 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&) 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)); + } + }; + + /** + * Specialization for std::string. + */ + template<> + struct row_extractor { + std::string extract(const char* row_value) const { + if(row_value) { + return row_value; + } else { + return {}; + } + } + + 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 {}; + } + } + }; +#ifndef SQLITE_ORM_OMITS_CODECVT + /** + * Specialization for std::wstring. + */ + template<> + struct row_extractor { + std::wstring extract(const char* row_value) const { + if(row_value) { + std::wstring_convert> converter; + return converter.from_bytes(row_value); + } else { + return {}; + } + } + + 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); + } else { + 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 + + template + struct row_extractor::value>> { + using unqualified_type = std::remove_cv_t; + + V extract(const char* row_value) const { + if(row_value) { + return is_std_ptr::make(row_extractor().extract(row_value)); + } else { + return {}; + } + } + + 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)); + } else { + 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 + template + struct row_extractor>> { + using unqualified_type = std::remove_cv_t; + + V extract(const char* row_value) const { + if(row_value) { + return std::make_optional(row_extractor().extract(row_value)); + } else { + return std::nullopt; + } + } + + V 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)); + } else { + return std::nullopt; + } + } + + V 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 + + template<> + struct row_extractor { + nullptr_t extract(const char* /*row_value*/) const { + return nullptr; + } + + nullptr_t extract(sqlite3_stmt*, int /*columnIndex*/) const { + return nullptr; + } + + nullptr_t extract(sqlite3_value*) const { + return nullptr; + } + }; + /** + * Specialization for std::vector. + */ + template<> + struct row_extractor> { + std::vector extract(const char* row_value) const { + return {row_value, row_value + (row_value ? ::strlen(row_value) : 0)}; + } + + 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 {bytes, 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 {bytes, bytes + len}; + } + }; + + template + struct row_extractor> { + + std::tuple extract(char** argv) const { + return this->extract(argv, std::make_index_sequence{}); + } + + std::tuple extract(sqlite3_stmt* stmt, int /*columnIndex*/) const { + return this->extract(stmt, std::make_index_sequence{}); + } + + protected: + template + std::tuple extract(sqlite3_stmt* stmt, std::index_sequence) const { + return std::tuple{row_extractor{}.extract(stmt, Idx)...}; + } + + template + std::tuple extract(char** argv, std::index_sequence) const { + return std::tuple{row_extractor{}.extract(argv[Idx])...}; + } + }; + + /** + * Specialization for journal_mode. + */ + template<> + struct row_extractor { + 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); + } else { + throw std::system_error{orm_error_code::incorrect_journal_mode_string}; + } + } else { + throw std::system_error{orm_error_code::incorrect_journal_mode_string}; + } + } + + 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::move + +// #include "error_code.h" + +namespace sqlite_orm { + + /** + * Escape the provided character in the given string by doubling it. + * @param str A copy of the original string + * @param char2Escape The character to escape + */ + inline std::string sql_escape(std::string str, char char2Escape) { + for(size_t pos = 0; (pos = str.find(char2Escape, pos)) != str.npos; pos += 2) { + str.replace(pos, 1, 2, char2Escape); + } + + return str; + } + + /** + * Quote the given string value using single quotes, + * escape containing single quotes by doubling them. + */ + inline std::string quote_string_literal(std::string v) { + constexpr char quoteChar = '\''; + return quoteChar + sql_escape(std::move(v), quoteChar) + quoteChar; + } + + /** + * Quote the given string value using single quotes, + * escape containing single quotes by doubling them. + */ + inline std::string quote_blob_literal(std::string v) { + constexpr char quoteChar = '\''; + return std::string{char('x'), quoteChar} + std::move(v) + quoteChar; + } + + /** + * Quote the given identifier using double quotes, + * escape containing double quotes by doubling them. + */ + inline std::string quote_identifier(std::string identifier) { + constexpr char quoteChar = '"'; + return quoteChar + sql_escape(std::move(identifier), quoteChar) + quoteChar; + } + + namespace internal { + // Wrapper to reduce boiler-plate code + inline sqlite3_stmt* reset_stmt(sqlite3_stmt* stmt) { + sqlite3_reset(stmt); + return stmt; + } + + // note: query is deliberately taken by value, such that it is thrown away early + inline sqlite3_stmt* prepare_stmt(sqlite3* db, std::string query) { + sqlite3_stmt* stmt; + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + return stmt; + } + + inline 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_translated_sqlite_error(db); + } + } + + inline void perform_exec(sqlite3* db, + const char* query, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) { + int rc = sqlite3_exec(db, query, callback, user_data, nullptr); + if(rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + + inline void perform_exec(sqlite3* db, + const std::string& query, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) { + return perform_exec(db, query.c_str(), callback, user_data); + } + + template + void perform_step(sqlite3_stmt* stmt) { + int rc = sqlite3_step(stmt); + if(rc != expected) { + throw_translated_sqlite_error(stmt); + } + } + + template + void perform_step(sqlite3_stmt* stmt, L&& lambda) { + switch(int rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + break; + default: { + throw_translated_sqlite_error(stmt); + } + } + } + + template + void perform_steps(sqlite3_stmt* stmt, L&& lambda) { + int rc; + do { + switch(rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + break; + default: { + throw_translated_sqlite_error(stmt); + } + } + } while(rc != SQLITE_DONE); + } + } +} +#pragma once + +#include + +namespace sqlite_orm { + + enum class sync_schema_result { + + /** + * created new table, table with the same tablename did not exist + */ + new_table_created, + + /** + * table schema is the same as storage, nothing to be done + */ + already_in_sync, + + /** + * removed excess columns in table (than storage) without dropping a table + */ + old_columns_removed, + + /** + * lacking columns in table (than storage) added without dropping a table + */ + new_columns_added, + + /** + * both old_columns_removed and new_columns_added + */ + new_columns_added_and_old_columns_removed, + + /** + * old table is dropped and new is recreated. Reasons : + * 1. delete excess columns in the table than storage if preseve = false + * 2. Lacking columns in the table cannot be added due to NULL and DEFAULT constraint + * 3. Reasons 1 and 2 both together + * 4. data_type mismatch between table and storage. + */ + dropped_and_recreated, + }; + + 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"; + case sync_schema_result::already_in_sync: + return os << "table and storage is already in sync."; + case sync_schema_result::old_columns_removed: + return os << "old excess columns removed"; + case sync_schema_result::new_columns_added: + return os << "new columns added"; + case sync_schema_result::new_columns_added_and_old_columns_removed: + return os << "old excess columns removed and new columns added"; + case sync_schema_result::dropped_and_recreated: + return os << "old table dropped and recreated"; + } + return os; + } +} +#pragma once + +#include // std::tuple, std::make_tuple, std::declval, std::tuple_element_t +#include // std::string +#include // std::forward + +// #include "functional/cxx_universal.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "indexed_column.h" + +#include // std::string +#include // std::move + +// #include "functional/cxx_universal.h" + +// #include "ast/where.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct indexed_column_t { + using column_type = C; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + indexed_column_t(column_type _column_or_expression) : + column_or_expression(std::move(_column_or_expression)) {} +#endif + + column_type column_or_expression; + std::string _collation_name; + int _order = 0; // -1 = desc, 1 = asc, 0 = not specified + + indexed_column_t collate(std::string name) { + auto res = std::move(*this); + res._collation_name = std::move(name); + return res; + } + + indexed_column_t asc() { + auto res = std::move(*this); + res._order = 1; + return res; + } + + indexed_column_t desc() { + auto res = std::move(*this); + res._order = -1; + return res; + } + }; + + template + indexed_column_t make_indexed_column(C col) { + return {std::move(col)}; + } + + template + where_t make_indexed_column(where_t wher) { + return std::move(wher); + } + + template + indexed_column_t make_indexed_column(indexed_column_t col) { + return std::move(col); + } + } + + /** + * Use this function to specify indexed column inside `make_index` function call. + * Example: make_index("index_name", indexed_column(&User::id).asc()) + */ + template + internal::indexed_column_t indexed_column(C column_or_expression) { + return {std::move(column_or_expression)}; + } + +} + +// #include "table_type_of.h" + +namespace sqlite_orm { + + namespace internal { + + struct index_base { + std::string name; + bool unique = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + index_base(std::string name, bool unique) : name{std::move(name)}, unique{unique} {} +#endif + }; + + template + struct index_t : index_base { + using elements_type = std::tuple; + using object_type = void; + using table_mapped_type = T; + +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + index_t(std::string name_, bool unique_, elements_type elements_) : + index_base{std::move(name_), unique_}, elements(std::move(elements_)) {} +#endif + + elements_type elements; + }; + } + + template + internal::index_t()))...> make_index(std::string name, + Cols... cols) { + using cols_tuple = std::tuple; + static_assert(internal::count_tuple::value <= 1, + "amount of where arguments can be 0 or 1"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), false, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); + } + + template + internal::index_t>>, + decltype(internal::make_indexed_column(std::declval()))...> + make_index(std::string name, Cols... cols) { + using cols_tuple = std::tuple; + static_assert(internal::count_tuple::value <= 1, + "amount of where arguments can be 0 or 1"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), false, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); + } + + template + internal::index_t>>, + decltype(internal::make_indexed_column(std::declval()))...> + make_unique_index(std::string name, Cols... cols) { + using cols_tuple = std::tuple; + static_assert(internal::count_tuple::value <= 1, + "amount of where arguments can be 0 or 1"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), true, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); + } +} +#pragma once + +#include // std::string + +namespace sqlite_orm { + + namespace internal { + + struct rowid_t { + operator std::string() const { + return "rowid"; + } + }; + + struct oid_t { + operator std::string() const { + return "oid"; + } + }; + + struct _rowid_t { + operator std::string() const { + return "_rowid_"; + } + }; + + template + struct table_rowid_t : public rowid_t { + using type = T; + }; + + template + struct table_oid_t : public oid_t { + using type = T; + }; + template + struct table__rowid_t : public _rowid_t { + using type = T; + }; + + } + + inline internal::rowid_t rowid() { + return {}; + } + + inline internal::oid_t oid() { + return {}; + } + + inline internal::_rowid_t _rowid_() { + return {}; + } + + template + internal::table_rowid_t rowid() { + return {}; + } + + template + internal::table_oid_t oid() { + return {}; + } + + template + internal::table__rowid_t _rowid_() { + return {}; + } +} +#pragma once + +#include // std::string +#include // std::remove_reference, std::is_same, std::decay +#include // std::vector +#include // std::tuple_size, std::tuple_element +#include // std::forward, std::move + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/cxx_functional_polyfill.h" + +// #include "functional/static_magic.h" + +#ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED +#include // std::false_type, std::true_type, std::integral_constant +#endif +#include // std::forward + +namespace sqlite_orm { + + // got from here + // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co + namespace internal { + + template + decltype(auto) empty_callable() { + static auto res = [](auto&&...) -> R { + return R(); + }; + return (res); + } + +#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + template + decltype(auto) static_if([[maybe_unused]] T&& trueFn, [[maybe_unused]] F&& falseFn) { + if constexpr(B) { + return std::forward(trueFn); + } else { + return std::forward(falseFn); + } + } + + template + decltype(auto) static_if([[maybe_unused]] T&& trueFn) { + if constexpr(B) { + return std::forward(trueFn); + } else { + return empty_callable(); + } + } + + template + void call_if_constexpr([[maybe_unused]] L&& lambda, [[maybe_unused]] Args&&... args) { + if constexpr(B) { + lambda(std::forward(args)...); + } + } +#else + template + decltype(auto) static_if(std::true_type, T&& trueFn, const F&) { + return std::forward(trueFn); + } + + template + decltype(auto) static_if(std::false_type, const T&, F&& falseFn) { + return std::forward(falseFn); + } + + template + decltype(auto) static_if(T&& trueFn, F&& falseFn) { + return static_if(std::integral_constant{}, std::forward(trueFn), std::forward(falseFn)); + } + + template + decltype(auto) static_if(T&& trueFn) { + return static_if(std::integral_constant{}, std::forward(trueFn), empty_callable()); + } + + template + void call_if_constexpr(L&& lambda, Args&&... args) { + static_if(std::forward(lambda))(std::forward(args)...); + } +#endif + } + +} + +// #include "functional/mpl.h" + +// #include "functional/index_sequence_util.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_iteration.h" + +#include // std::tuple, std::get, std::tuple_element, std::tuple_size +#include // std::index_sequence, std::make_index_sequence +#include // std::forward, std::move + +// #include "../functional/cxx_universal.h" + +// #include "../functional/cxx_type_traits_polyfill.h" + +// #include "../functional/cxx_functional_polyfill.h" + +// #include "../functional/index_sequence_util.h" + +namespace sqlite_orm { + 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(std::move(t))...); + } + + template + auto call(Function& f, FunctionPointer functionPointer, Tuple t) { + constexpr size_t size = std::tuple_size::value; + return call_impl(f, functionPointer, std::move(t), std::make_index_sequence{}); + } + + template + auto call(Function& f, Tuple t) { + return call(f, &Function::operator(), std::move(t)); + } + +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + template + void iterate_tuple(const Tpl& tpl, std::index_sequence, L&& lambda) { + if constexpr(reversed) { + // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= + int sink; + ((lambda(std::get(tpl)), sink) = ... = 0); + } else { + (lambda(std::get(tpl)), ...); + } + } +#else + template + void iterate_tuple(const Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} + + template + void iterate_tuple(const Tpl& tpl, std::index_sequence, L&& lambda) { +#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr(reversed) { +#else + if(reversed) { +#endif + iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); + lambda(std::get(tpl)); + } else { + lambda(std::get(tpl)); + iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); + } + } +#endif + template + void iterate_tuple(const Tpl& tpl, L&& lambda) { + iterate_tuple(tpl, + std::make_index_sequence::value>{}, + std::forward(lambda)); + } + +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + void iterate_tuple(std::index_sequence, L&& lambda) { + (lambda((std::tuple_element_t*)nullptr), ...); + } +#else + template + void iterate_tuple(std::index_sequence<>, L&& /*lambda*/) {} + + template + void iterate_tuple(std::index_sequence, L&& lambda) { + lambda((std::tuple_element_t*)nullptr); + iterate_tuple(std::index_sequence{}, std::forward(lambda)); + } +#endif + template + void iterate_tuple(L&& lambda) { + iterate_tuple(std::make_index_sequence::value>{}, std::forward(lambda)); + } + + template + R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { + return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; + } + + template + R create_from_tuple(Tpl&& tpl, Projection project = {}) { + return create_from_tuple( + std::forward(tpl), + std::make_index_sequence>::value>{}, + std::forward(project)); + } + + template class Base, class L> + struct lambda_as_template_base : L { +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + lambda_as_template_base(L&& lambda) : L{std::move(lambda)} {} +#endif + template + decltype(auto) operator()(const Base& object) { + return L::operator()(object); + } + }; + + /* + * This method wraps the specified callable in another function object, + * which in turn implicitly casts its single argument to the specified template base class, + * then passes the converted argument to the lambda. + * + * Note: This method is useful for reducing combinatorial instantiation of template lambdas, + * as long as this library supports compilers that do not implement + * explicit template parameters in generic lambdas [SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED]. + * Unfortunately it doesn't work with user-defined conversion operators in order to extract + * parts of a class. In other words, the destination type must be a direct template base class. + */ + template class Base, class L> + lambda_as_template_base call_as_template_base(L lambda) { + return {std::move(lambda)}; + } + } +} + +// #include "member_traits/member_traits.h" + +// #include "typed_comparator.h" + +// #include "type_traits.h" + +// #include "constraints.h" + +// #include "table_info.h" + +// #include "column.h" + +namespace sqlite_orm { + + namespace internal { + + struct basic_table { + + /** + * Table name. + */ + std::string name; + }; + + /** + * Table definition. + */ + template + struct table_t : basic_table { + using object_type = O; + using elements_type = std::tuple; + + static constexpr bool is_without_rowid_v = WithoutRowId; + using is_without_rowid = polyfill::bool_constant; + + elements_type elements; + +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + table_t(std::string name_, elements_type elements_) : + basic_table{std::move(name_)}, elements{std::move(elements_)} {} +#endif + + table_t without_rowid() const { + return {this->name, this->elements}; + } + + /** + * Returns foreign keys count in table definition + */ + constexpr int foreign_keys_count() const { +#if SQLITE_VERSION_NUMBER >= 3006019 + using fk_index_sequence = filter_tuple_sequence_t; + return int(fk_index_sequence::size()); +#else + return 0; +#endif + } + + /** + * Function used to get field value from object by mapped member pointer/setter/getter. + * + * For a setter the corresponding getter has to be searched, + * so the method returns a pointer to the field as returned by the found getter. + * Otherwise the method invokes the member pointer and returns its result. + */ + template = true> + decltype(auto) object_field_value(const object_type& object, M memberPointer) const { + return polyfill::invoke(memberPointer, object); + } + + template = true> + const member_field_type_t* object_field_value(const object_type& object, M memberPointer) const { + using field_type = member_field_type_t; + const field_type* res = nullptr; + iterate_tuple(this->elements, + col_index_sequence_with_field_type{}, + call_as_template_base([&res, &memberPointer, &object](const auto& column) { + if(compare_any(column.setter, memberPointer)) { + res = &polyfill::invoke(column.member_pointer, object); + } + })); + return res; + } + + const basic_generated_always::storage_type* + find_column_generated_storage_type(const std::string& name) const { + const basic_generated_always::storage_type* result = nullptr; +#if SQLITE_VERSION_NUMBER >= 3031000 + iterate_tuple(this->elements, + col_index_sequence_with{}, + [&result, &name](auto& column) { + if(column.name != name) { + return; + } + using generated_op_index_sequence = + filter_tuple_sequence_t, + is_generated_always>; + constexpr size_t opIndex = first_index_sequence_value(generated_op_index_sequence{}); + result = &get(column.constraints).storage; + }); +#else + (void)name; +#endif + return result; + } + + template + bool exists_in_composite_primary_key(const column_field& column) const { + bool res = false; + this->for_each_primary_key([&column, &res](auto& primaryKey) { + using colrefs_tuple = decltype(primaryKey.columns); + using same_type_index_sequence = + filter_tuple_sequence_t>::template fn, + member_field_type_t>; + iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { + if(compare_any(memberPointer, column.member_pointer) || + compare_any(memberPointer, column.setter)) { + res = true; + } + }); + }); + return res; + } + + /** + * Call passed lambda with all defined primary keys. + */ + template + void for_each_primary_key(L&& lambda) const { + using pk_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->elements, pk_index_sequence{}, lambda); + } + + std::vector composite_key_columns_names() const { + std::vector res; + this->for_each_primary_key([this, &res](auto& primaryKey) { + res = this->composite_key_columns_names(primaryKey); + }); + return res; + } + + std::vector primary_key_column_names() const { + using pkcol_index_sequence = col_index_sequence_with; + + if(pkcol_index_sequence::size() > 0) { + return create_from_tuple>(this->elements, + pkcol_index_sequence{}, + &column_identifier::name); + } else { + return this->composite_key_columns_names(); + } + } + + template + void for_each_primary_key_column(L&& lambda) const { + iterate_tuple(this->elements, + col_index_sequence_with{}, + call_as_template_base([&lambda](const auto& column) { + lambda(column.member_pointer); + })); + this->for_each_primary_key([&lambda](auto& primaryKey) { + iterate_tuple(primaryKey.columns, lambda); + }); + } + + template + std::vector composite_key_columns_names(const primary_key_t& primaryKey) const { + return create_from_tuple>(primaryKey.columns, + [this, empty = std::string{}](auto& memberPointer) { + if(const std::string* columnName = + this->find_column_name(memberPointer)) { + return *columnName; + } else { + return empty; + } + }); + } + + /** + * Searches column name by class member pointer passed as the first argument. + * @return column name or empty string if nothing found. + */ + template = true> + const std::string* find_column_name(M m) const { + const std::string* res = nullptr; + using field_type = member_field_type_t; + iterate_tuple(this->elements, + col_index_sequence_with_field_type{}, + [&res, m](auto& c) { + if(compare_any(c.member_pointer, m) || compare_any(c.setter, m)) { + res = &c.name; + } + }); + return res; + } + + /** + * Counts and returns amount of columns without GENERATED ALWAYS constraints. Skips table constraints. + */ + constexpr int non_generated_columns_count() const { +#if SQLITE_VERSION_NUMBER >= 3031000 + using non_generated_col_index_sequence = + col_index_sequence_excluding; + return int(non_generated_col_index_sequence::size()); +#else + return this->count_columns_amount(); +#endif + } + + /** + * Counts and returns amount of columns. Skips constraints. + */ + constexpr int count_columns_amount() const { + using col_index_sequence = filter_tuple_sequence_t; + return int(col_index_sequence::size()); + } + + /** + * Call passed lambda with all defined foreign keys. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` + */ + template + void for_each_foreign_key(L&& lambda) const { + using fk_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->elements, fk_index_sequence{}, lambda); + } + + template + void for_each_foreign_key_to(L&& lambda) const { + using fk_index_sequence = filter_tuple_sequence_t; + using filtered_index_sequence = filter_tuple_sequence_t::template fn, + target_type_t, + fk_index_sequence>; + iterate_tuple(this->elements, filtered_index_sequence{}, lambda); + } + + /** + * Call passed lambda with all defined columns. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` + */ + template + void for_each_column(L&& lambda) const { + using col_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->elements, col_index_sequence{}, lambda); + } + + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. + */ + template class OpTraitFn, class L> + void for_each_column_excluding(L&& lambda) const { + iterate_tuple(this->elements, col_index_sequence_excluding{}, lambda); + } + + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. + */ + template = true> + void for_each_column_excluding(L&& lambda) const { + this->for_each_column_excluding(lambda); + } + + std::vector get_table_info() const; + }; + + template + struct is_table : std::false_type {}; + + template + struct is_table> : std::true_type {}; + } + + /** + * Factory function for a table definition. + * + * The mapped object type is determined implicitly from the first column definition. + */ + template>::object_type> + internal::table_t make_table(std::string name, Cs... args) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::make_tuple(std::forward(args)...)}); + } + + /** + * Factory function for a table definition. + * + * The mapped object type is explicitly specified. + */ + template + internal::table_t make_table(std::string name, Cs... args) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::make_tuple(std::forward(args)...)}); + } +} +#pragma once + +#include // std::string + +// #include "functional/cxx_universal.h" +// ::nullptr_t +// #include "functional/static_magic.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "type_traits.h" + +// #include "select_constraints.h" + +// #include "storage_lookup.h" + +#include // std::true_type, std::false_type, std::remove_const, std::enable_if, std::is_base_of, std::is_void +#include +#include // std::index_sequence, std::make_index_sequence + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + +namespace sqlite_orm { + namespace internal { + + template + struct storage_t; + + template + using db_objects_tuple = std::tuple; + + template + struct is_storage : std::false_type {}; + + template + struct is_storage> : std::true_type {}; + template + struct is_storage> : std::true_type {}; + + template + struct is_db_objects : std::false_type {}; + + template + struct is_db_objects> : std::true_type {}; + template + struct is_db_objects> : std::true_type {}; + + /** + * std::true_type if given object is mapped, std::false_type otherwise. + * + * Note: unlike table_t<>, index_t<>::object_type and trigger_t<>::object_type is always void. + */ + template + struct object_type_matches : polyfill::conjunction>>, + std::is_same>> {}; + + /** + * std::true_type if given lookup type (object) is mapped, std::false_type otherwise. + */ + template + struct lookup_type_matches : polyfill::disjunction> {}; + } + + // pick/lookup metafunctions + namespace internal { + + /** + * Indirect enabler for DBO, accepting an index to disambiguate non-unique DBOs + */ + template + struct enable_found_table : std::enable_if::value, DBO> {}; + + /** + * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. + * + * Lookup - mapped data type + * Seq - index sequence matching the number of DBOs + * DBOs - db_objects_tuple type + */ + template + struct storage_pick_table; + + template + struct storage_pick_table, db_objects_tuple> + : enable_found_table... {}; + + /** + * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. + * + * Lookup - 'table' type, mapped data type + * DBOs - db_objects_tuple type, possibly const-qualified + */ + template + using storage_pick_table_t = typename storage_pick_table::value>, + std::remove_const_t>::type; + + /** + * Find a table definition (`table_t`) from a tuple of database objects; + * `std::nonesuch` if not found. + * + * DBOs - db_objects_tuple type + * Lookup - mapped data type + */ + template + struct storage_find_table : polyfill::detected_or {}; + + /** + * Find a table definition (`table_t`) from a tuple of database objects; + * `std::nonesuch` if not found. + * + * DBOs - db_objects_tuple type, possibly const-qualified + * Lookup - mapped data type + */ + template + using storage_find_table_t = typename storage_find_table>::type; + +#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template + struct is_mapped : std::false_type {}; + template + struct is_mapped>> : std::true_type {}; +#else + template> + struct is_mapped : std::true_type {}; + template + struct is_mapped : std::false_type {}; +#endif + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_mapped_v = is_mapped::value; + } +} + +// runtime lookup functions +namespace sqlite_orm { + namespace internal { + /** + * Pick the table definition for the specified lookup type from the given tuple of schema objects. + * + * Note: This function requires Lookup to be mapped, otherwise it is removed from the overload resolution set. + */ + template = true> + auto& pick_table(DBOs& dbObjects) { + using table_type = storage_pick_table_t; + return std::get(dbObjects); + } + + template = true> + auto lookup_table(const DBOs& dbObjects); + + template = true> + decltype(auto) lookup_table_name(const DBOs& dbObjects); + + } +} + +// interface functions +namespace sqlite_orm { + namespace internal { + + template + using tables_index_sequence = filter_tuple_sequence_t; + + template = true> + int foreign_keys_count(const DBOs& dbObjects) { + int res = 0; + iterate_tuple(dbObjects, tables_index_sequence{}, [&res](const auto& table) { + res += table.foreign_keys_count(); + }); + return res; + } + + template> + auto lookup_table(const DBOs& dbObjects) { + return static_if>( + [](const auto& dbObjects) { + return &pick_table(dbObjects); + }, + empty_callable())(dbObjects); + } + + template> + decltype(auto) lookup_table_name(const DBOs& dbObjects) { + return static_if>( + [](const auto& dbObjects) -> const std::string& { + return pick_table(dbObjects).name; + }, + empty_callable())(dbObjects); + } + + /** + * Find column name by its type and member pointer. + */ + template = true> + const std::string* find_column_name(const DBOs& dbObjects, F O::*field) { + return pick_table(dbObjects).find_column_name(field); + } + + /** + * Materialize column pointer: + * 1. by explicit object type and member pointer. + */ + template = true> + constexpr decltype(auto) materialize_column_pointer(const DBOs&, const column_pointer& cp) { + return cp.field; + } + + /** + * Find column name by: + * 1. by explicit object type and member pointer. + */ + template = true> + const std::string* find_column_name(const DBOs& dbObjects, const column_pointer& cp) { + auto field = materialize_column_pointer(dbObjects, cp); + return pick_table(dbObjects).find_column_name(field); + } + } +} +#pragma once + +#include // std::string + +// #include "constraints.h" + +// #include "serializer_context.h" + +// #include "storage_lookup.h" + +namespace sqlite_orm { + + namespace internal { + + template + std::string serialize(const T&, const serializer_context&); + + /** + * Serialize default value of a column's default valu + */ + template + std::string serialize_default_value(const default_t& dft) { + db_objects_tuple<> dbObjects; + serializer_context> context{dbObjects}; + return serialize(dft.value, context); + } + + } + +} +#pragma once + +#include +#include // std::unique_ptr/shared_ptr, std::make_unique/std::make_shared +#include // std::system_error +#include // std::string +#include // std::remove_reference, std::is_base_of, std::decay, std::false_type, std::true_type +#include // std::identity +#include // std::stringstream +#include // std::map +#include // std::vector +#include // std::tuple_size, std::tuple, std::make_tuple, std::tie +#include // std::forward, std::pair +#include // std::for_each, std::ranges::for_each +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_functional_polyfill.h" + +// #include "functional/static_magic.h" + +// #include "functional/mpl.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "type_traits.h" + +// #include "alias.h" + +// #include "row_extractor_builder.h" + +// #include "functional/cxx_universal.h" + +// #include "row_extractor.h" + +// #include "mapped_row_extractor.h" + +#include + +// #include "object_from_column_builder.h" + +#include +#include // std::is_member_object_pointer + +// #include "functional/static_magic.h" + +// #include "row_extractor.h" + +namespace sqlite_orm { + + namespace internal { + + struct object_from_column_builder_base { + sqlite3_stmt* stmt = nullptr; + int index = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + object_from_column_builder_base(sqlite3_stmt* stmt) : stmt{stmt} {} +#endif + }; + + /** + * This is a cute lambda replacement which is used in several places. + */ + template + struct object_from_column_builder : object_from_column_builder_base { + using object_type = O; + + object_type& object; + + object_from_column_builder(object_type& object_, sqlite3_stmt* stmt_) : + object_from_column_builder_base{stmt_}, object(object_) {} + + template + void operator()(const column_field& column) { + auto value = row_extractor>().extract(this->stmt, this->index++); + static_if::value>( + [&value, &object = this->object](const auto& column) { + object.*column.member_pointer = std::move(value); + }, + [&value, &object = this->object](const auto& column) { + (object.*column.setter)(std::move(value)); + })(column); + } + }; + } +} + +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 `make_row_extractor()`. + * 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_type = Table; + + V extract(sqlite3_stmt* stmt, int /*columnIndex*/) const { + V res; + object_from_column_builder builder{res, stmt}; + this->tableInfo.for_each_column(builder); + return res; + } + + const table_type& tableInfo; + }; + + } + +} + +namespace sqlite_orm { + + namespace internal { + + template + row_extractor make_row_extractor(nullptr_t) { + return {}; + } + + template + mapped_row_extractor make_row_extractor(const Table* table) { + return {*table}; + } + } + +} + +// #include "error_code.h" + +// #include "type_printer.h" + +// #include "constraints.h" + +// #include "field_printer.h" + +// #include "rowid.h" + +// #include "operators.h" + +// #include "select_constraints.h" + +// #include "core_functions.h" + +// #include "conditions.h" + +// #include "statement_binder.h" + +// #include "column_result.h" + +#include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of +#include // std::tuple +#include // std::reference_wrapper + +// #include "functional/cxx_universal.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_fy.h" + +#include + +namespace sqlite_orm { + + namespace internal { + + template + struct tuplify { + using type = std::tuple; + }; + template + struct tuplify> { + using type = std::tuple; + }; + + template + using tuplify_t = typename tuplify::type; + } +} + +// #include "tuple_helper/tuple_filter.h" + +// #include "type_traits.h" + +// #include "member_traits/member_traits.h" + +// #include "mapped_type_proxy.h" + +#include // std::remove_const + +// #include "type_traits.h" + +// #include "alias_traits.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * If T is a recordset alias then the typename mapped_type_proxy::type is the unqualified aliased type, + * otherwise unqualified T. + */ + template + struct mapped_type_proxy : std::remove_const {}; + + template + struct mapped_type_proxy> : std::remove_const> {}; + + template + using mapped_type_proxy_t = typename mapped_type_proxy::type; + } +} + +// #include "core_functions.h" + +// #include "select_constraints.h" + +// #include "operators.h" + +// #include "rowid.h" + +// #include "alias.h" + +// #include "storage_traits.h" + +#include // std::tuple + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "tuple_helper/tuple_transformer.h" + +#include // std::tuple + +// #include "../functional/mpl.h" + +namespace sqlite_orm { + namespace internal { + + template class Op> + struct tuple_transformer; + + template class Op> + struct tuple_transformer, Op> { + using type = std::tuple...>; + }; + + /* + * Transform specified tuple. + * + * `Op` is a metafunction operation. + */ + template class Op> + using transform_tuple_t = typename tuple_transformer::type; + } +} + +// #include "type_traits.h" + +// #include "storage_lookup.h" + +namespace sqlite_orm { + + namespace internal { + + namespace storage_traits { + + /** + * DBO - db object (table) + */ + template + struct storage_mapped_columns_impl + : tuple_transformer, is_column>, field_type_t> {}; + + template<> + struct storage_mapped_columns_impl { + using type = std::tuple<>; + }; + + /** + * DBOs - db_objects_tuple type + * Lookup - mapped or unmapped data type + */ + template + struct storage_mapped_columns : storage_mapped_columns_impl> {}; + } + } +} + +// #include "function.h" + +#include +#include +#include // std::string +#include // std::tuple +#include // std::function +#include // std::min +#include // std::move, std::forward + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + + struct arg_values; + + template + struct pointer_arg; + template + class pointer_binding; + + namespace internal { + + struct user_defined_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; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + user_defined_function_base(decltype(name) name_, + decltype(argumentsCount) argumentsCount_, + decltype(create) create_, + decltype(destroy) destroy_) : + name(std::move(name_)), + argumentsCount(argumentsCount_), create(std::move(create_)), destroy(destroy_) {} +#endif + }; + + struct user_defined_scalar_function_t : user_defined_function_base { + func_call run; + + user_defined_scalar_function_t(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(run) run_, + decltype(destroy) destroy_) : + user_defined_function_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, + run(std::move(run_)) {} + }; + + struct user_defined_aggregate_function_t : user_defined_function_base { + func_call step; + final_call finalCall; + + user_defined_aggregate_function_t(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(step) step_, + decltype(finalCall) finalCall_, + decltype(destroy) destroy_) : + user_defined_function_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, + step(std::move(step_)), finalCall(std::move(finalCall_)) {} + }; + + template + using scalar_call_function_t = decltype(&F::operator()); + + template + using aggregate_step_function_t = decltype(&F::step); + + template + using aggregate_fin_function_t = decltype(&F::fin); + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_function_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_function_v>> = + true; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_function_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_function_v< + F, + polyfill::void_t, + aggregate_fin_function_t, + std::enable_if_t>::value>, + std::enable_if_t>::value>>> = + true; + + template + struct member_function_arguments; + + template + struct member_function_arguments { + using member_function_type = R (O::*)(Args...) const; + using tuple_type = std::tuple...>; + using return_type = R; + }; + + template + struct member_function_arguments { + using member_function_type = R (O::*)(Args...); + using tuple_type = std::tuple...>; + using return_type = R; + }; + + template + struct callable_arguments_impl; + + template + struct callable_arguments_impl>> { + using args_tuple = typename member_function_arguments>::tuple_type; + using return_type = typename member_function_arguments>::return_type; + }; + + template + struct callable_arguments_impl>> { + 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 {}; + + template + struct function_call { + using function_type = F; + using args_tuple = std::tuple; + + args_tuple args; + }; + + template + struct unpacked_arg { + using type = T; + }; + template + struct unpacked_arg> { + using type = typename callable_arguments::return_type; + }; + template + using unpacked_arg_t = typename unpacked_arg::type; + + template + SQLITE_ORM_CONSTEVAL bool expected_pointer_value() { + static_assert(polyfill::always_false_v, "Expected a pointer value for I-th argument"); + return false; + } + + template + constexpr bool is_same_pvt_v = expected_pointer_value(); + + // Always allow binding nullptr to a pointer argument + template + constexpr bool is_same_pvt_v> = true; + +#if __cplusplus >= 201703L // C++17 or later + template + SQLITE_ORM_CONSTEVAL bool assert_same_pointer_type() { + constexpr bool valid = Binding == PointerArg; + static_assert(valid, "Pointer value types of I-th argument do not match"); + return valid; + } + + template + constexpr bool + is_same_pvt_v> = + assert_same_pointer_type(); +#else + template + SQLITE_ORM_CONSTEVAL bool assert_same_pointer_type() { + constexpr bool valid = Binding::value == PointerArg::value; + static_assert(valid, "Pointer value types of I-th argument do not match"); + return valid; + } + + template + constexpr bool + is_same_pvt_v> = + assert_same_pointer_type(); +#endif + + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::false_type) { + return true; + } + + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::true_type) { + return is_same_pvt_v; + } + + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_types(polyfill::index_constant) { + return true; + } + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_types(polyfill::index_constant) { + using func_arg_t = std::tuple_element_t; + using passed_arg_t = unpacked_arg_t>; + +#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED + constexpr bool valid = validate_pointer_value_type, + unpacked_arg_t>>( + polyfill::bool_constant < (polyfill::is_specialization_of_v) || + (polyfill::is_specialization_of_v) > {}); + + return validate_pointer_value_types(polyfill::index_constant{}) && valid; +#else + return validate_pointer_value_types(polyfill::index_constant{}) && + validate_pointer_value_type, + unpacked_arg_t>>( + polyfill::bool_constant < (polyfill::is_specialization_of_v) || + (polyfill::is_specialization_of_v) > {}); +#endif + } + } + + /** + * 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 && + internal::validate_pointer_value_types( + polyfill::index_constant(functionArgsCount, argsCount) - 1>{})) || + std::is_same>::value, + "Number of arguments does not match"); + return {std::make_tuple(std::forward(args)...)}; + } + +} + +namespace sqlite_orm { + + namespace internal { + + /** + * Obtains the result type of expressions that form the columns of a select statement. + * + * This is a proxy class used to define what type must have result type depending on select + * arguments (member pointer, aggregate functions, etc). Below you can see specializations + * for different types. E.g. specialization for internal::length_t has `type` int cause + * LENGTH returns INTEGER in sqlite. Every column_result_t must have `type` type that equals + * c++ SELECT return type for T + * DBOs - db_objects_tuple type + * T - C++ type + * SFINAE - sfinae argument + */ + template + struct column_result_t; + + template + using column_result_of_t = typename column_result_t::type; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct column_result_t, void> { + using type = std::optional>; + }; + + template + struct column_result_t, void> { + using type = std::optional; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t> : member_field_type {}; + + template + struct column_result_t, void> { + using type = R; + }; + + template + 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, Rest...>, void> { + using type = std::unique_ptr>; + }; + + template + struct column_result_t, S, X>, void> { + using type = std::unique_ptr>; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t { + using type = nullptr_t; + }; + + template + struct column_result_t { + using type = int; + }; + + template + struct column_result_t, void> : column_result_t {}; + + template + struct column_result_t, void> : column_result_t {}; + + template + struct column_result_t, void> { + using type = std::string; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t, void> { + using type = int; + }; + + template + struct column_result_t { + using type = int64; + }; + + template + struct column_result_t { + using type = int64; + }; + + template + struct column_result_t { + using type = int64; + }; + + template + struct column_result_t, void> { + using type = int64; + }; + + template + struct column_result_t, void> { + using type = int64; + }; + + template + struct column_result_t, void> { + using type = int64; + }; + + template + struct column_result_t, void> : column_result_t {}; + + template + struct column_result_t, void> : column_result_t {}; + + template + struct column_result_t, void> { + using type = tuple_cat_t>>...>; + }; + + template + struct column_result_t> : column_result_t {}; + + template + struct column_result_t> { + using left_result = column_result_of_t; + using right_result = column_result_of_t; + static_assert(std::is_same::value, + "Compound subselect queries must return same types"); + using type = left_result; + }; + + template + struct column_result_t> { + using type = typename T::result_type; + }; + + /** + * Result for the most simple queries like `SELECT 1` + */ + template + struct column_result_t> { + using type = T; + }; + + /** + * Result for the most simple queries like `SELECT 'ototo'` + */ + template + struct column_result_t { + using type = std::string; + }; + + template + struct column_result_t { + using type = std::string; + }; + + template + struct column_result_t, void> : column_result_t> {}; + + template + struct column_result_t, void> + : storage_traits::storage_mapped_columns> {}; + + template + struct column_result_t, void> { + using type = T; + }; + + template + struct column_result_t, void> { + using type = T; + }; + + template + struct column_result_t, void> { + using type = R; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> : column_result_t {}; + } +} + +// #include "mapped_type_proxy.h" + +// #include "sync_schema_result.h" + +// #include "table_info.h" + +// #include "storage_impl.h" + +// #include "journal_mode.h" + +// #include "view.h" + +#include +#include // std::string +#include // std::forward, std::move +#include // std::tuple, std::make_tuple + +// #include "row_extractor.h" + +// #include "error_code.h" + +// #include "iterator.h" + +#include +#include // std::shared_ptr, std::unique_ptr, std::make_shared +#include // std::decay +#include // std::move +#include // std::input_iterator_tag +#include // std::system_error +#include // std::bind + +// #include "functional/cxx_universal.h" + +// #include "statement_finalizer.h" + +// #include "error_code.h" + +// #include "object_from_column_builder.h" + +// #include "storage_lookup.h" + +// #include "util.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct iterator_t { + using view_type = V; + using value_type = typename view_type::mapped_type; + + protected: + /** + * shared_ptr is used over unique_ptr here + * so that the iterator can be copyable. + */ + std::shared_ptr stmt; + + // only null for the default constructed iterator + view_type* view = nullptr; + + /** + * shared_ptr is used over unique_ptr here + * so that the iterator can be copyable. + */ + std::shared_ptr current; + + void extract_value() { + auto& dbObjects = obtain_db_objects(this->view->storage); + this->current = std::make_shared(); + object_from_column_builder builder{*this->current, this->stmt.get()}; + pick_table(dbObjects).for_each_column(builder); + } + + void next() { + this->current.reset(); + if(sqlite3_stmt* stmt = this->stmt.get()) { + perform_step(stmt, std::bind(&iterator_t::extract_value, this)); + if(!this->current) { + this->stmt.reset(); + } + } + } + + public: + using difference_type = ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::input_iterator_tag; + + iterator_t(){}; + + iterator_t(statement_finalizer stmt_, view_type& view_) : stmt{std::move(stmt_)}, view{&view_} { + next(); + } + + const value_type& operator*() const { + if(!this->stmt || !this->current) { + throw std::system_error{orm_error_code::trying_to_dereference_null_iterator}; + } + return *this->current; + } + + const value_type* operator->() const { + return &(this->operator*()); + } + + iterator_t& operator++() { + next(); + return *this; + } + + void operator++(int) { + this->operator++(); + } + + bool operator==(const iterator_t& other) const { + return this->current == other.current; + } + + bool operator!=(const iterator_t& other) const { + return !(*this == other); + } + }; + } +} + +// #include "ast_iterator.h" + +#include // std::vector +#include // std::reference_wrapper + +// #include "tuple_helper/tuple_iteration.h" + +// #include "type_traits.h" + +// #include "conditions.h" + +// #include "alias.h" + +// #include "select_constraints.h" + +// #include "operators.h" + +// #include "core_functions.h" + +// #include "prepared_statement.h" + +#include +#include // std::unique_ptr +#include // std::iterator_traits +#include // std::string +#include // std::integral_constant, std::declval +#include // std::pair + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/cxx_functional_polyfill.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "connection_holder.h" + +#include +#include +#include // std::string + +// #include "error_code.h" + +namespace sqlite_orm { + + namespace internal { + + struct connection_holder { + + connection_holder(std::string filename_) : filename(std::move(filename_)) {} + + void retain() { + if(1 == ++this->_retain_count) { + auto rc = sqlite3_open(this->filename.c_str(), &this->db); + if(rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + } + + void release() { + if(0 == --this->_retain_count) { + auto rc = sqlite3_close(this->db); + if(rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + } + + sqlite3* get() const { + return this->db; + } + + int retain_count() const { + return this->_retain_count; + } + + const std::string filename; + + protected: + sqlite3* db = nullptr; + std::atomic_int _retain_count{}; + }; + + struct connection_ref { + connection_ref(connection_holder& holder_) : holder(holder_) { + this->holder.retain(); + } + + connection_ref(const connection_ref& other) : holder(other.holder) { + this->holder.retain(); + } + + connection_ref(connection_ref&& other) : holder(other.holder) { + this->holder.retain(); + } + + ~connection_ref() { + this->holder.release(); + } + + sqlite3* get() const { + return this->holder.get(); + } + + protected: + connection_holder& holder; + }; + } +} + +// #include "select_constraints.h" + +// #include "values.h" + +#include // std::vector +#include // std::tuple +#include // std::forward + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct values_t { + using args_tuple = std::tuple; + + args_tuple tuple; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_values_v = polyfill::is_specialization_of_v; + + template + using is_values = polyfill::bool_constant>; + + 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 {{std::move(vector)}}; + } + +} + +// #include "ast/upsert_clause.h" + +#if SQLITE_VERSION_NUMBER >= 3024000 +#include // std::tuple +#include // std::forward, std::move +#endif + +// #include "../functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { +#if SQLITE_VERSION_NUMBER >= 3024000 + 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::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 + using is_upsert_clause = polyfill::is_specialization_of; +#else + template + struct is_upsert_clause : polyfill::bool_constant {}; +#endif + } + +#if SQLITE_VERSION_NUMBER >= 3024000 + /** + * 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)...)}; + } +#endif +} + +// #include "ast/set.h" + +#include // std::tuple, std::tuple_size +#include // std::string +#include // std::vector +#include // std::stringstream +#include // std::false_type, std::true_type + +// #include "../table_name_collector.h" + +#include // std::set +#include // std::string +#include // std::pair, std::move + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + +// #include "mapped_type_proxy.h" + +// #include "select_constraints.h" + +// #include "alias.h" + +// #include "core_functions.h" + +// #include "storage_lookup.h" + +namespace sqlite_orm { + + namespace internal { + + struct table_name_collector_base { + using table_name_set = std::set>; + + table_name_set table_names; + }; + + template + struct table_name_collector : table_name_collector_base { + using db_objects_type = DBOs; + + const db_objects_type& db_objects; + + table_name_collector() = default; + + table_name_collector(const db_objects_type& dbObjects) : db_objects{dbObjects} {} + + template + void operator()(const T&) const {} + + template + void operator()(F O::*) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + + template + void operator()(const column_pointer&) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + + template + void operator()(const alias_column_t&) { + // note: instead of accessing the column, we are interested in the type the column is aliased into + auto tableName = lookup_table_name>(this->db_objects); + this->table_names.emplace(std::move(tableName), alias_extractor::as_alias()); + } + + template + void operator()(const count_asterisk_t&) { + auto tableName = lookup_table_name(this->db_objects); + if(!tableName.empty()) { + this->table_names.emplace(std::move(tableName), ""); + } + } + + template + void operator()(const asterisk_t&) { + auto tableName = lookup_table_name>(this->db_objects); + table_names.emplace(std::move(tableName), alias_extractor::as_alias()); + } + + template + void operator()(const object_t&) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + + template + void operator()(const table_rowid_t&) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + + template + void operator()(const table_oid_t&) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + + template + void operator()(const table__rowid_t&) { + this->table_names.emplace(lookup_table_name(this->db_objects), ""); + } + }; + + template = true> + table_name_collector make_table_name_collector(const DBOs& dbObjects) { + return {dbObjects}; + } + + } + +} + +namespace sqlite_orm { + + namespace internal { + + template + void iterate_ast(const T& t, L&& lambda); + + template + struct set_t { + using assigns_type = std::tuple; + + assigns_type assigns; + }; + + template + struct is_set : std::false_type {}; + + template + struct is_set> : std::true_type {}; + + struct dynamic_set_entry { + std::string serialized_value; + }; + + template + struct dynamic_set_t { + using context_t = C; + using entry_t = dynamic_set_entry; + using const_iterator = typename std::vector::const_iterator; + + dynamic_set_t(const context_t& context_) : context(context_), collector(this->context.db_objects) {} + + dynamic_set_t(const dynamic_set_t& other) = default; + dynamic_set_t(dynamic_set_t&& other) = default; + dynamic_set_t& operator=(const dynamic_set_t& other) = default; + dynamic_set_t& operator=(dynamic_set_t&& other) = default; + + template + void push_back(assign_t assign) { + auto newContext = this->context; + newContext.skip_table_name = true; + iterate_ast(assign, this->collector); + std::stringstream ss; + ss << serialize(assign.lhs, newContext) << ' ' << assign.serialize() << ' ' + << serialize(assign.rhs, context); + this->entries.push_back({ss.str()}); + } + + const_iterator begin() const { + return this->entries.begin(); + } + + const_iterator end() const { + return this->entries.end(); + } + + void clear() { + this->entries.clear(); + this->collector.table_names.clear(); + } + + std::vector entries; + context_t context; + table_name_collector collector; + }; + + template + struct is_set> : std::true_type {}; + + template + struct is_dynamic_set : std::false_type {}; + + template + struct is_dynamic_set> : std::true_type {}; + } + + /** + * SET keyword used in UPDATE ... SET queries. + * Args must have `assign_t` type. E.g. set(assign(&User::id, 5)) or set(c(&User::id) = 5) + */ + template + internal::set_t set(Args... args) { + using arg_tuple = std::tuple; + static_assert(std::tuple_size::value == + internal::count_tuple::value, + "set function accepts assign operators only"); + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * SET keyword used in UPDATE ... SET queries. It is dynamic version. It means use can add amount of arguments now known at compilation time but known at runtime. + */ + template + internal::dynamic_set_t> dynamic_set(const S& storage) { + internal::serializer_context_builder builder(storage); + return builder(); + } +} + +namespace sqlite_orm { + + namespace internal { + + struct prepared_statement_base { + sqlite3_stmt* stmt = nullptr; + connection_ref con; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + prepared_statement_base(sqlite3_stmt* stmt, connection_ref con) : stmt{stmt}, con{std::move(con)} {} +#endif + + ~prepared_statement_base() { + sqlite3_finalize(this->stmt); + } + + std::string sql() const { + // note: sqlite3 internally checks for null before calling + // sqlite3_normalized_sql() or sqlite3_expanded_sql(), so check here, too, even if superfluous + if(const char* sql = sqlite3_sql(this->stmt)) { + return sql; + } else { + return {}; + } + } + +#if SQLITE_VERSION_NUMBER >= 3014000 + std::string expanded_sql() const { + // note: must check return value due to SQLITE_OMIT_TRACE + using char_ptr = std::unique_ptr>; + if(char_ptr sql{sqlite3_expanded_sql(this->stmt)}) { + return sql.get(); + } else { + return {}; + } + } +#endif +#if SQLITE_VERSION_NUMBER >= 3026000 and defined(SQLITE_ENABLE_NORMALIZE) + std::string normalized_sql() const { + if(const char* sql = sqlite3_normalized_sql(this->stmt)) { + return sql; + } else { + return {}; + } + } +#endif + +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::string_view column_name(int index) const { + return sqlite3_column_name(stmt, index); + } +#endif + }; + + template + struct prepared_statement_t : prepared_statement_base { + using expression_type = T; + + expression_type expression; + + prepared_statement_t(T expression_, sqlite3_stmt* stmt_, connection_ref con_) : + prepared_statement_base{stmt_, std::move(con_)}, expression(std::move(expression_)) {} + + prepared_statement_t(prepared_statement_t&& prepared_stmt) : + prepared_statement_base{prepared_stmt.stmt, std::move(prepared_stmt.con)}, + expression(std::move(prepared_stmt.expression)) { + prepared_stmt.stmt = nullptr; + } + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_prepared_statement_v = + polyfill::is_specialization_of_v; + + template + using is_prepared_statement = polyfill::bool_constant>; + + /** + * T - type of object to obtain from a database + */ + template + struct get_all_t { + using type = T; + using return_type = R; + + using conditions_type = std::tuple; + + conditions_type conditions; + }; + + template + struct get_all_pointer_t { + using type = T; + using return_type = R; + + using conditions_type = std::tuple; + + conditions_type conditions; + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct get_all_optional_t { + using type = T; + using return_type = R; + + using conditions_type = std::tuple; + + conditions_type conditions; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct update_all_t { + using set_type = S; + using conditions_type = std::tuple; + + static_assert(is_set::value, "update_all_t must have set or dynamic set as the first argument"); + + set_type set; + conditions_type conditions; + }; + + template + struct remove_all_t { + using type = T; + using conditions_type = std::tuple; + + conditions_type conditions; + }; + + template + struct get_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; + + template + struct get_pointer_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct get_optional_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct update_t { + using type = T; + + type object; + }; + + template + struct remove_t { + using type = T; + using ids_type = std::tuple; + + ids_type ids; + }; + + template + struct insert_t { + using type = T; + + type object; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_insert_v = polyfill::is_specialization_of_v; + + template + using is_insert = polyfill::bool_constant>; + + template + struct insert_explicit { + using type = T; + using columns_type = columns_t; + + type obj; + columns_type columns; + }; + + template + struct replace_t { + using type = T; + + type object; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_replace_v = polyfill::is_specialization_of_v; + + template + using is_replace = polyfill::bool_constant>; + + template + struct insert_range_t { + using iterator_type = It; + using transformer_type = Projection; + using object_type = O; + + std::pair range; + transformer_type transformer; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_insert_range_v = polyfill::is_specialization_of_v; + + template + using is_insert_range = polyfill::bool_constant>; + + template + struct replace_range_t { + using iterator_type = It; + using transformer_type = Projection; + using object_type = O; + + std::pair range; + transformer_type transformer; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_replace_range_v = polyfill::is_specialization_of_v; + + template + using is_replace_range = polyfill::bool_constant>; + + template + struct insert_raw_t { + using args_tuple = std::tuple; + + args_tuple args; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_insert_raw_v = polyfill::is_specialization_of_v; + + template + using is_insert_raw = polyfill::bool_constant>; + + template + struct replace_raw_t { + using args_tuple = std::tuple; + + args_tuple args; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_replace_raw_v = polyfill::is_specialization_of_v; + + template + using is_replace_raw = polyfill::bool_constant>; + + struct default_values_t {}; + + template + using is_default_values = std::is_same; + + enum class conflict_action { + abort, + fail, + ignore, + replace, + rollback, + }; + + struct insert_constraint { + conflict_action action = conflict_action::abort; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + insert_constraint(conflict_action action) : action{action} {} +#endif + }; + + template + using is_insert_constraint = std::is_same; + } + + inline internal::insert_constraint or_rollback() { + return {internal::conflict_action::rollback}; + } + + inline internal::insert_constraint or_replace() { + return {internal::conflict_action::replace}; + } + + inline internal::insert_constraint or_ignore() { + return {internal::conflict_action::ignore}; + } + + inline internal::insert_constraint or_fail() { + return {internal::conflict_action::fail}; + } + + inline internal::insert_constraint or_abort() { + return {internal::conflict_action::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 {}; + } + + /** + * 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. + * The objects in the range are transformed using the specified projection, which defaults to identity projection. + * + * @example + * ``` + * std::vector users; + * users.push_back(User{1, "Leony"}); + * auto statement = storage.prepare(replace_range(users.begin(), users.end())); + * storage.execute(statement); + * ``` + * @example + * ``` + * std::vector> userPointers; + * userPointers.push_back(std::make_unique(1, "Eneli")); + * auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)); + * storage.execute(statement); + * ``` + */ + template + auto replace_range(It from, It to, Projection project = {}) { + using O = std::decay_t(), *std::declval()))>; + return internal::replace_range_t{{std::move(from), std::move(to)}, std::move(project)}; + } + + /* + * Create a replace range statement. + * Overload of `replace_range(It, It, Projection)` with explicit object type template parameter. + */ + template + internal::replace_range_t replace_range(It from, It to, Projection project = {}) { + return {{std::move(from), std::move(to)}, std::move(project)}; + } + + /** + * Create an insert range statement. + * The objects in the range are transformed using the specified projection, which defaults to identity projection. + * + * @example + * ``` + * std::vector users; + * users.push_back(User{1, "Leony"}); + * auto statement = storage.prepare(insert_range(users.begin(), users.end())); + * storage.execute(statement); + * ``` + * @example + * ``` + * std::vector> userPointers; + * userPointers.push_back(std::make_unique(1, "Eneli")); + * auto statement = storage.prepare(insert_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)); + * storage.execute(statement); + * ``` + */ + template + auto insert_range(It from, It to, Projection project = {}) { + using O = std::decay_t(), *std::declval()))>; + return internal::insert_range_t{{std::move(from), std::move(to)}, std::move(project)}; + } + + /* + * Create an insert range statement. + * Overload of `insert_range(It, It, Projection)` with explicit object type template parameter. + */ + template + internal::insert_range_t insert_range(It from, It to, Projection project = {}) { + return {{std::move(from), std::move(to)}, std::move(project)}; + } + + /** + * Create a replace statement. + * T is an object type mapped to a storage. + * Usage: storage.replace(myUserInstance); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.replace(std::ref(myUserInstance)); + */ + template + internal::replace_t replace(T obj) { + return {std::move(obj)}; + } + + /** + * Create an insert statement. + * T is an object type mapped to a storage. + * Usage: storage.insert(myUserInstance); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.insert(std::ref(myUserInstance)); + */ + template + internal::insert_t insert(T obj) { + return {std::move(obj)}; + } + + /** + * Create an explicit insert statement. + * T is an object type mapped to a storage. + * Cols is columns types aparameter pack. Must contain member pointers + * Usage: storage.insert(myUserInstance, columns(&User::id, &User::name)); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.insert(std::ref(myUserInstance), columns(&User::id, &User::name)); + */ + template + internal::insert_explicit insert(T obj, internal::columns_t cols) { + return {std::move(obj), std::move(cols)}; + } + + /** + * Create a remove statement + * T is an object type mapped to a storage. + * Usage: remove(5); + */ + template + internal::remove_t remove(Ids... ids) { + std::tuple idsTuple{std::forward(ids)...}; + return {std::move(idsTuple)}; + } + + /** + * Create an update statement. + * T is an object type mapped to a storage. + * Usage: storage.update(myUserInstance); + * Parameter obj is accepted by value. If you want to accept it by ref + * please use std::ref function: storage.update(std::ref(myUserInstance)); + */ + template + internal::update_t update(T obj) { + return {std::move(obj)}; + } + + /** + * Create a get statement. + * T is an object type mapped to a storage. + * Usage: get(5); + */ + template + internal::get_t get(Ids... ids) { + std::tuple idsTuple{std::forward(ids)...}; + return {std::move(idsTuple)}; + } + + /** + * Create a get pointer statement. + * T is an object type mapped to a storage. + * Usage: get_pointer(5); + */ + template + internal::get_pointer_t get_pointer(Ids... ids) { + std::tuple idsTuple{std::forward(ids)...}; + return {std::move(idsTuple)}; + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * Create a get optional statement. + * T is an object type mapped to a storage. + * Usage: get_optional(5); + */ + template + internal::get_optional_t get_optional(Ids... ids) { + std::tuple idsTuple{std::forward(ids)...}; + return {std::move(idsTuple)}; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + /** + * Create a remove all statement. + * T is an object type mapped to a storage. + * Usage: storage.remove_all(...); + */ + template + internal::remove_all_t remove_all(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {std::move(conditions)}; + } + + /** + * Create a get all statement. + * T is an object type mapped to a storage. + * Usage: storage.get_all(...); + */ + template + internal::get_all_t, Args...> get_all(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {std::move(conditions)}; + } + + /** + * Create a get all statement. + * T is an object type mapped to a storage. + * R is a container type. std::vector is default + * Usage: storage.get_all(...); + */ + template + internal::get_all_t get_all(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {std::move(conditions)}; + } + + /** + * Create an update all statement. + * Usage: storage.update_all(set(...), ...); + */ + template + internal::update_all_t update_all(S set, Wargs... wh) { + static_assert(internal::is_set::value, "first argument in update_all can be either set or dynamic_set"); + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(wh)...}; + return {std::move(set), std::move(conditions)}; + } + + /** + * Create a get all pointer statement. + * T is an object type mapped to a storage. + * Usage: storage.get_all_pointer(...); + */ + template + internal::get_all_pointer_t>, Args...> get_all_pointer(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {std::move(conditions)}; + } + /** + * Create a get all pointer statement. + * T is an object type mapped to a storage. + * R is a container return type. std::vector> is default + * Usage: storage.get_all_pointer(...); + */ + template + internal::get_all_pointer_t get_all_pointer(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {std::move(conditions)}; + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * Create a get all optional statement. + * T is an object type mapped to a storage. + * Usage: storage.get_all_optional(...); + */ + template + internal::get_all_optional_t>, Args...> get_all_optional(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {std::move(conditions)}; + } + + /** + * Create a get all optional statement. + * T is an object type mapped to a storage. + * R is a container return type. std::vector> is default + * Usage: storage.get_all_optional(...); + */ + template + internal::get_all_optional_t get_all_optional(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {std::move(conditions)}; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} + +// #include "values.h" + +// #include "function.h" + +// #include "ast/excluded.h" + +#include // std::move + +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 "ast/where.h" + +// #include "ast/into.h" + +// #include "ast/group_by.h" + +// #include "ast/exists.h" + +#include // std::move + +// #include "../tags.h" + +namespace sqlite_orm { + namespace internal { + + template + struct exists_t : condition_t, negatable_t { + using expression_type = T; + using self = exists_t; + + expression_type expression; + + exists_t(expression_type expression_) : expression(std::move(expression_)) {} + }; + } + + /** + * 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 expression) { + return {std::move(expression)}; + } +} + +// #include "ast/set.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * ast_iterator accepts any expression and a callable object + * which will be called for any node of provided expression. + * E.g. if we pass `where(is_equal(5, max(&User::id, 10))` then + * callable object will be called with 5, &User::id and 10. + * ast_iterator is used in finding literals to be bound to + * a statement, and to collect table names. + * + * Note that not all leaves of the expression tree are always visited: + * Column expressions can be more complex, but are passed as a whole to the callable. + * Examples are `column_pointer<>` and `alias_column_t<>`. + * + * To use `ast_iterator` call `iterate_ast(object, callable);` + * + * `T` is an ast element, e.g. where_t + */ + template + struct ast_iterator { + using node_type = T; + + /** + * L is a callable type. Mostly is a templated lambda + */ + template + void operator()(const T& t, L& lambda) const { + lambda(t); + } + }; + + /** + * Simplified API + */ + template + void iterate_ast(const T& t, L&& lambda) { + ast_iterator iterator; + iterator(t, lambda); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct ast_iterator, void> { + using node_type = as_optional_t; + + template + void operator()(const node_type& node, 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& expression, L& lambda) const { + iterate_ast(expression.get(), lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = group_by_t; + + template + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = excluded_t; + + template + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.expression, lambda); + } + }; + + template + struct ast_iterator> { + using node_type = T; + + template + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.actions, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = where_t; + + template + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.expression, lambda); + } + }; + + template + struct ast_iterator> { + using node_type = T; + + template + void operator()(const node_type& binaryCondition, L& lambda) const { + iterate_ast(binaryCondition.l, lambda); + iterate_ast(binaryCondition.r, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = binary_operator; + + template + void operator()(const node_type& binaryOperator, C& lambda) const { + iterate_ast(binaryOperator.lhs, lambda); + iterate_ast(binaryOperator.rhs, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = columns_t; + + template + void operator()(const node_type& cols, L& lambda) const { + iterate_ast(cols.columns, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = dynamic_in_t; + + template + void operator()(const node_type& in, C& lambda) const { + iterate_ast(in.left, lambda); + iterate_ast(in.argument, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = in_t; + + template + void operator()(const node_type& in, C& lambda) const { + iterate_ast(in.left, lambda); + iterate_ast(in.argument, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = std::vector; + + template + void operator()(const node_type& vec, L& lambda) const { + for(auto& i: vec) { + iterate_ast(i, lambda); + } + } + }; + + template<> + struct ast_iterator, void> { + using node_type = std::vector; + + template + void operator()(const node_type& vec, L& lambda) const { + lambda(vec); + } + }; + + template + struct ast_iterator> { + using node_type = T; + + template + void operator()(const node_type& c, L& lambda) const { + iterate_ast(c.left, lambda); + iterate_ast(c.right, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = into_t; + + template + void operator()(const node_type& /*node*/, L& /*lambda*/) const { + //.. + } + }; + + template + struct ast_iterator, void> { + using node_type = insert_raw_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = replace_raw_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = select_t; + + template + void operator()(const node_type& sel, L& lambda) const { + iterate_ast(sel.col, lambda); + iterate_ast(sel.conditions, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = get_all_t; + + template + void operator()(const node_type& get, L& lambda) const { + iterate_ast(get.conditions, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = get_all_pointer_t; + + template + void operator()(const node_type& get, L& lambda) const { + iterate_ast(get.conditions, lambda); + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct ast_iterator, void> { + using node_type = get_all_optional_t; + + template + void operator()(const node_type& get, L& lambda) const { + iterate_ast(get.conditions, lambda); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct ast_iterator, void> { + using node_type = update_all_t; + + template + void operator()(const node_type& u, L& lambda) const { + iterate_ast(u.set, lambda); + iterate_ast(u.conditions, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = remove_all_t; + + template + void operator()(const node_type& r, L& lambda) const { + iterate_ast(r.conditions, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = set_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.assigns, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = dynamic_set_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.entries, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = std::tuple; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_tuple(node, [&lambda](auto& v) { + iterate_ast(v, lambda); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = group_by_with_having; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + iterate_ast(node.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = having_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = cast_t; + + template + void operator()(const node_type& c, L& lambda) const { + iterate_ast(c.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = exists_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = like_t; + + template + void operator()(const node_type& lk, L& lambda) const { + iterate_ast(lk.arg, lambda); + iterate_ast(lk.pattern, lambda); + lk.arg3.apply([&lambda](auto& value) { + iterate_ast(value, lambda); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = glob_t; + + template + void operator()(const node_type& lk, L& lambda) const { + iterate_ast(lk.arg, lambda); + iterate_ast(lk.pattern, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = between_t; + + template + void operator()(const node_type& b, L& lambda) const { + iterate_ast(b.expr, lambda); + iterate_ast(b.b1, lambda); + iterate_ast(b.b2, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = named_collate; + + template + void operator()(const node_type& col, L& lambda) const { + iterate_ast(col.expr, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = negated_condition_t; + + template + void operator()(const node_type& neg, L& lambda) const { + iterate_ast(neg.c, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = is_null_t; + + template + void operator()(const node_type& i, L& lambda) const { + iterate_ast(i.t, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = is_not_null_t; + + template + void operator()(const node_type& i, L& lambda) const { + iterate_ast(i.t, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = function_call; + + template + void operator()(const node_type& f, L& lambda) const { + iterate_ast(f.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = built_in_function_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = built_in_aggregate_function_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = filtered_aggregate_function; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.function, lambda); + iterate_ast(node.where, lambda); + } + }; + + template + struct ast_iterator> { + using node_type = Join; + + template + void operator()(const node_type& join, L& lambda) const { + iterate_ast(join.constraint, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = on_t; + + template + void operator()(const node_type& on, L& lambda) const { + iterate_ast(on.arg, lambda); + } + }; + + // note: not strictly necessary as there's no binding support for USING; + // we provide it nevertheless, in line with on_t. + template + struct ast_iterator>> { + using node_type = T; + + template + void operator()(const node_type& o, L& lambda) const { + iterate_ast(o.column, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = simple_case_t; + + template + void operator()(const node_type& c, L& lambda) const { + c.case_expression.apply([&lambda](auto& c_) { + iterate_ast(c_, lambda); + }); + iterate_tuple(c.args, [&lambda](auto& pair) { + iterate_ast(pair.first, lambda); + iterate_ast(pair.second, lambda); + }); + c.else_expression.apply([&lambda](auto& el) { + iterate_ast(el, lambda); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = as_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = limit_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.lim, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = limit_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.lim, lambda); + a.off.apply([&lambda](auto& value) { + iterate_ast(value, lambda); + }); + } + }; + + template + struct ast_iterator, void> { + using node_type = limit_t; + + template + void operator()(const node_type& a, L& lambda) const { + a.off.apply([&lambda](auto& value) { + iterate_ast(value, lambda); + }); + iterate_ast(a.lim, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = distinct_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.value, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = all_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.value, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = bitwise_not_t; + + template + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.argument, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = values_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.tuple, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = dynamic_values_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.vector, lambda); + } + }; + + /** + * Column alias or literal: skipped + */ + template + struct ast_iterator, + polyfill::is_specialization_of, + is_column_alias>>> { + using node_type = T; + + template + void operator()(const node_type& /*node*/, L& /*lambda*/) const {} + }; + + template + struct ast_iterator, void> { + using node_type = order_by_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expression, lambda); + } + }; + + template + struct ast_iterator, void> { + using node_type = collate_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expr, lambda); + } + }; + + } +} + +// #include "prepared_statement.h" + +// #include "connection_holder.h" + +// #include "util.h" + +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; + connection_ref connection; + get_all_t, 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() const { + return this->storage.template count(); + } + + bool empty() const { + return !this->size(); + } + + iterator_t begin() { + using context_t = serializer_context; + context_t context{obtain_db_objects(this->storage)}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + + statement_finalizer stmt{prepare_stmt(this->connection.get(), serialize(this->args, context))}; + iterate_ast(this->args.conditions, conditional_binder{stmt.get()}); + return {std::move(stmt), *this}; + } + + iterator_t end() { + return {}; + } + }; + } +} + +// #include "ast_iterator.h" + +// #include "storage_base.h" + +#include +#include // std::function, std::bind +#include // std::string +#include // std::stringstream +#include // std::move +#include // std::system_error +#include // std::vector +#include // std::make_unique, std::unique_ptr +#include // std::map +#include // std::decay, std::is_same +#include // std::find_if + +// #include "functional/cxx_universal.h" + +// #include "functional/static_magic.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "pragma.h" + +#include +#include // std::string +#include // std::function +#include // std::shared_ptr +#include // std::vector +#include + +// #include "error_code.h" + +// #include "row_extractor.h" + +// #include "journal_mode.h" + +// #include "connection_holder.h" + +// #include "util.h" + +// #include "serializing_util.h" + +#include // std::index_sequence +#include +#include +#include +#include +#include // std::exchange, std::tuple_size + +// #include "functional/cxx_universal.h" +// ::size_t +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "error_code.h" + +// #include "serializer_context.h" + +// #include "serialize_result_type.h" + +// #include "util.h" + +namespace sqlite_orm { + namespace internal { + template + struct order_by_t; + + template + std::string serialize(const T&, const serializer_context&); + + template + std::string serialize_order_by(const T&, const Ctx&); + + inline void stream_sql_escaped(std::ostream& os, serialize_arg_type str, char char2Escape) { + for(size_t offset = 0, next; true; offset = next + 1) { + next = str.find(char2Escape, offset); + + if(next == str.npos) { + os.write(str.data() + offset, str.size() - offset); + break; + } + + os.write(str.data() + offset, next - offset + 1); + os.write(&char2Escape, 1); + } + } + + inline void stream_identifier(std::ostream& ss, + serialize_arg_type qualifier, + serialize_arg_type identifier, + serialize_arg_type alias) { + constexpr char quoteChar = '"'; + constexpr char qualified[] = {quoteChar, '.', '\0'}; + constexpr char aliased[] = {' ', quoteChar, '\0'}; + + // note: In practice, escaping double quotes in identifiers is arguably overkill, + // but since the SQLite grammar allows it, it's better to be safe than sorry. + + if(!qualifier.empty()) { + ss << quoteChar; + stream_sql_escaped(ss, qualifier, quoteChar); + ss << qualified; + } + { + ss << quoteChar; + stream_sql_escaped(ss, identifier, quoteChar); + ss << quoteChar; + } + if(!alias.empty()) { + ss << aliased; + stream_sql_escaped(ss, alias, quoteChar); + ss << quoteChar; + } + } + + inline void stream_identifier(std::ostream& ss, const std::string& identifier, const std::string& alias) { + return stream_identifier(ss, "", identifier, alias); + } + + inline void stream_identifier(std::ostream& ss, const std::string& identifier) { + return stream_identifier(ss, "", identifier, ""); + } + + template + void stream_identifier(std::ostream& ss, const Tpl& tpl, std::index_sequence) { + static_assert(sizeof...(Is) > 0 && sizeof...(Is) <= 3, ""); + return stream_identifier(ss, std::get(tpl)...); + } + + template>, bool> = true> + void stream_identifier(std::ostream& ss, const Tpl& tpl) { + return stream_identifier(ss, tpl, std::make_index_sequence::value>{}); + } + + enum class stream_as { + conditions_tuple, + actions_tuple, + expressions_tuple, + dynamic_expressions, + serialized, + identifier, + identifiers, + values_placeholders, + table_columns, + non_generated_columns, + field_values_excluding, + mapped_columns_expressions, + column_constraints, + }; + + template + struct streaming { + template + auto operator()(const Ts&... ts) const { + return std::forward_as_tuple(*this, ts...); + } + + template + constexpr std::index_sequence<1u + Idx...> offset_index(std::index_sequence) const { + return {}; + } + }; + constexpr streaming streaming_conditions_tuple{}; + constexpr streaming streaming_actions_tuple{}; + constexpr streaming streaming_expressions_tuple{}; + constexpr streaming streaming_dynamic_expressions{}; + constexpr streaming streaming_serialized{}; + constexpr streaming streaming_identifier{}; + constexpr streaming streaming_identifiers{}; + constexpr streaming streaming_values_placeholders{}; + constexpr streaming streaming_table_column_names{}; + constexpr streaming streaming_non_generated_column_names{}; + constexpr streaming streaming_field_values_excluding{}; + constexpr streaming streaming_mapped_columns_expressions{}; + constexpr streaming streaming_column_constraints{}; + + // serialize and stream a tuple of condition expressions; + // space + space-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, T, Ctx> tpl) { + const auto& conditions = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(conditions, [&ss, &context](auto& c) { + ss << " " << serialize(c, context); + }); + return ss; + } + + // serialize and stream a tuple of action expressions; + // space-separated + template + std::ostream& operator<<(std::ostream& ss, std::tuple&, T, Ctx> tpl) { + const auto& actions = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(actions, [&ss, &context, first = true](auto& action) mutable { + constexpr std::array sep = {" ", ""}; + ss << sep[std::exchange(first, false)] << serialize(action, context); + }); + return ss; + } + + // serialize and stream a tuple of expressions; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, T, Ctx> tpl) { + const auto& args = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(args, [&ss, &context, first = true](auto& arg) mutable { + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << serialize(arg, context); + }); + return ss; + } + + // serialize and stream multi_order_by arguments; + // comma-separated + template + std::ostream& operator<<( + std::ostream& ss, + std::tuple&, const std::tuple...>&, Ctx> tpl) { + const auto& args = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(args, [&ss, &context, first = true](auto& arg) mutable { + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << serialize_order_by(arg, context); + }); + return ss; + } + + // serialize and stream a vector of expressions; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, C, Ctx> tpl) { + const auto& args = get<1>(tpl); + auto& context = get<2>(tpl); + + constexpr std::array sep = {", ", ""}; + for(size_t i = 0, first = true; i < args.size(); ++i) { + ss << sep[std::exchange(first, false)] << serialize(args[i], context); + } + return ss; + } + + // stream a vector of already serialized strings; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, std::tuple&, C> tpl) { + const auto& strings = get<1>(tpl); + + constexpr std::array sep = {", ", ""}; + for(size_t i = 0, first = true; i < strings.size(); ++i) { + ss << sep[std::exchange(first, false)] << strings[i]; + } + return ss; + } + + // stream an identifier described by a variadic string pack, which is one of: + // 1. identifier + // 2. identifier, alias + // 3. qualifier, identifier, alias + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, Strings...> tpl) { + stream_identifier(ss, tpl, streaming_identifier.offset_index(std::index_sequence_for{})); + return ss; + } + + // stream a container of identifiers described by a string or a tuple, which is one of: + // 1. identifier + // 1. tuple(identifier) + // 2. tuple(identifier, alias), pair(identifier, alias) + // 3. tuple(qualifier, identifier, alias) + // + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, std::tuple&, C> tpl) { + const auto& identifiers = get<1>(tpl); + + constexpr std::array sep = {", ", ""}; + bool first = true; + for(auto& identifier: identifiers) { + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, identifier); + } + return ss; + } + + // stream placeholders as part of a values clause + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, Ts...> tpl) { + const size_t& columnsCount = get<1>(tpl); + const ptrdiff_t& valuesCount = get<2>(tpl); + + if(!valuesCount || !columnsCount) { + return ss; + } + + std::string result; + result.reserve((1 + (columnsCount * 1) + (columnsCount * 2 - 2) + 1) * valuesCount + (valuesCount * 2 - 2)); + + constexpr std::array sep = {", ", ""}; + for(ptrdiff_t i = 0, first = true; i < valuesCount; ++i) { + result += sep[std::exchange(first, false)]; + result += "("; + for(size_t i = 0, first = true; i < columnsCount; ++i) { + result += sep[std::exchange(first, false)]; + result += "?"; + } + result += ")"; + } + ss << result; + return ss; + } + + // stream a table's column identifiers, possibly qualified; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, Table, const bool&> tpl) { + const auto& table = get<1>(tpl); + const bool& qualified = get<2>(tpl); + + table.for_each_column([&ss, &tableName = qualified ? table.name : std::string{}, first = true]( + const column_identifier& column) mutable { + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, tableName, column.name, std::string{}); + }); + return ss; + } + + // stream a table's non-generated column identifiers, unqualified; + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, Table> tpl) { + const auto& table = get<1>(tpl); + + table.template for_each_column_excluding( + [&ss, first = true](const column_identifier& column) mutable { + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, column.name); + }); + return ss; + } + + // stream a table's non-generated column identifiers, unqualified; + // comma-separated + template + std::ostream& + operator<<(std::ostream& ss, + std::tuple&, PredFnCls, L, Ctx, Obj> tpl) { + using check_if_excluded = polyfill::remove_cvref_t>; + auto& excluded = get<2>(tpl); + auto& context = get<3>(tpl); + auto& object = get<4>(tpl); + using object_type = polyfill::remove_cvref_t; + auto& table = pick_table(context.db_objects); + + table.template for_each_column_excluding(call_as_template_base( + [&ss, &excluded, &context, &object, first = true](auto& column) mutable { + if(excluded(column)) { + return; + } + + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] + << serialize(polyfill::invoke(column.member_pointer, object), context); + })); + return ss; + } + + // stream a tuple of mapped columns (which are member pointers or column pointers); + // comma-separated + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, T, Ctx> tpl) { + const auto& columns = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(columns, [&ss, &context, first = true](auto& colRef) mutable { + const std::string* columnName = find_column_name(context.db_objects, colRef); + if(!columnName) { + throw std::system_error{orm_error_code::column_not_found}; + } + + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, *columnName); + }); + return ss; + } + + template + std::ostream& operator<<(std::ostream& ss, + std::tuple&, + const column_constraints&, + const bool&, + Ctx> tpl) { + const auto& column = get<1>(tpl); + const bool& isNotNull = get<2>(tpl); + auto& context = get<3>(tpl); + + using constraints_type = constraints_type_t>; + constexpr size_t constraintsCount = std::tuple_size::value; + if(constraintsCount) { + std::vector constraintsStrings; + constraintsStrings.reserve(constraintsCount); + int primaryKeyIndex = -1; + int autoincrementIndex = -1; + int tupleIndex = 0; + iterate_tuple(column.constraints, + [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context]( + auto& constraint) { + using constraint_type = std::decay_t; + constraintsStrings.push_back(serialize(constraint, context)); + if(is_primary_key_v) { + primaryKeyIndex = tupleIndex; + } else if(is_autoincrement_v) { + autoincrementIndex = tupleIndex; + } + ++tupleIndex; + }); + if(primaryKeyIndex != -1 && autoincrementIndex != -1 && autoincrementIndex < primaryKeyIndex) { + iter_swap(constraintsStrings.begin() + primaryKeyIndex, + constraintsStrings.begin() + autoincrementIndex); + } + for(auto& str: constraintsStrings) { + ss << str << ' '; + } + } + if(isNotNull) { + ss << "NOT NULL "; + } + + return ss; + } + } +} + +namespace sqlite_orm { + + namespace internal { + struct storage_base; + + template + int getPragmaCallback(void* data, int argc, char** argv, char** x) { + return extract_single_value(data, argc, argv, x); + } + + 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(std::move(rowString)); + } + return 0; + } + + 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); + } + + int busy_timeout() { + return this->get_pragma("busy_timeout"); + } + + sqlite_orm::journal_mode journal_mode() { + return this->get_pragma("journal_mode"); + } + + 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); + } + + /** + * https://www.sqlite.org/pragma.html#pragma_application_id + */ + int application_id() { + return this->get_pragma("application_id"); + } + + /** + * https://www.sqlite.org/pragma.html#pragma_application_id + */ + void application_id(int value) { + this->set_pragma("application_id", value); + } + + int synchronous() { + return this->get_pragma("synchronous"); + } + + void synchronous(int value) { + this->_synchronous = -1; + this->set_pragma("synchronous", value); + this->_synchronous = value; + } + + int user_version() { + return this->get_pragma("user_version"); + } + + void user_version(int value) { + this->set_pragma("user_version", value); + } + + int auto_vacuum() { + return this->get_pragma("auto_vacuum"); + } + + void auto_vacuum(int value) { + this->set_pragma("auto_vacuum", value); + } + + std::vector integrity_check() { + return this->get_pragma>("integrity_check"); + } + + template + std::vector integrity_check(T table_name) { + std::ostringstream ss; + ss << "integrity_check(" << table_name << ")" << std::flush; + return this->get_pragma>(ss.str()); + } + + std::vector integrity_check(int n) { + std::ostringstream ss; + ss << "integrity_check(" << n << ")" << std::flush; + return this->get_pragma>(ss.str()); + } + + // will include generated columns in response as opposed to table_info + std::vector table_xinfo(const std::string& tableName) const { + auto connection = this->get_connection(); + + std::vector result; + std::ostringstream ss; + ss << "PRAGMA " + "table_xinfo(" + << streaming_identifier(tableName) << ")" << std::flush; + perform_exec( + connection.get(), + ss.str(), + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(std::vector*)data; + if(argc) { + auto index = 0; + auto cid = std::atoi(argv[index++]); + std::string name = argv[index++]; + std::string type = argv[index++]; + bool notnull = !!std::atoi(argv[index++]); + std::string dflt_value = argv[index] ? argv[index] : ""; + ++index; + auto pk = std::atoi(argv[index++]); + auto hidden = std::atoi(argv[index++]); + res.emplace_back(cid, + std::move(name), + std::move(type), + notnull, + std::move(dflt_value), + pk, + hidden); + } + return 0; + }, + &result); + return result; + } + + std::vector table_info(const std::string& tableName) const { + auto connection = this->get_connection(); + + std::ostringstream ss; + ss << "PRAGMA " + "table_info(" + << streaming_identifier(tableName) << ")" << std::flush; + std::vector result; + perform_exec( + connection.get(), + ss.str(), + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(std::vector*)data; + if(argc) { + auto index = 0; + auto cid = std::atoi(argv[index++]); + std::string name = argv[index++]; + std::string type = argv[index++]; + bool notnull = !!std::atoi(argv[index++]); + std::string dflt_value = argv[index] ? argv[index] : ""; + ++index; + auto pk = std::atoi(argv[index++]); + res.emplace_back(cid, std::move(name), std::move(type), notnull, std::move(dflt_value), pk); + } + return 0; + }, + &result); + return result; + } + + 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(); + T result; + perform_exec(connection.get(), "PRAGMA " + name, getPragmaCallback, &result); + return result; + } + + /** + * 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 << std::flush; + 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 << " = " << to_string(value) << std::flush; + perform_void_exec(db, ss.str()); + } + }; + } +} + +// #include "limit_accessor.h" + +#include +#include // std::map +#include // std::function +#include // std::shared_ptr + +// #include "connection_holder.h" + +namespace sqlite_orm { + + namespace internal { + + struct limit_accessor { + using get_connection_t = std::function; + + limit_accessor(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} + + int length() { + return this->get(SQLITE_LIMIT_LENGTH); + } + + void length(int newValue) { + this->set(SQLITE_LIMIT_LENGTH, newValue); + } + + int sql_length() { + return this->get(SQLITE_LIMIT_SQL_LENGTH); + } + + void sql_length(int newValue) { + this->set(SQLITE_LIMIT_SQL_LENGTH, newValue); + } + + int column() { + return this->get(SQLITE_LIMIT_COLUMN); + } + + void column(int newValue) { + this->set(SQLITE_LIMIT_COLUMN, newValue); + } + + int expr_depth() { + return this->get(SQLITE_LIMIT_EXPR_DEPTH); + } + + void expr_depth(int newValue) { + this->set(SQLITE_LIMIT_EXPR_DEPTH, newValue); + } + + int compound_select() { + return this->get(SQLITE_LIMIT_COMPOUND_SELECT); + } + + void compound_select(int newValue) { + this->set(SQLITE_LIMIT_COMPOUND_SELECT, newValue); + } + + int vdbe_op() { + return this->get(SQLITE_LIMIT_VDBE_OP); + } + + void vdbe_op(int newValue) { + this->set(SQLITE_LIMIT_VDBE_OP, newValue); + } + + int function_arg() { + return this->get(SQLITE_LIMIT_FUNCTION_ARG); + } + + void function_arg(int newValue) { + this->set(SQLITE_LIMIT_FUNCTION_ARG, newValue); + } + + int attached() { + return this->get(SQLITE_LIMIT_ATTACHED); + } + + void attached(int newValue) { + this->set(SQLITE_LIMIT_ATTACHED, newValue); + } + + int like_pattern_length() { + return this->get(SQLITE_LIMIT_LIKE_PATTERN_LENGTH); + } + + void like_pattern_length(int newValue) { + this->set(SQLITE_LIMIT_LIKE_PATTERN_LENGTH, newValue); + } + + int variable_number() { + return this->get(SQLITE_LIMIT_VARIABLE_NUMBER); + } + + void variable_number(int newValue) { + this->set(SQLITE_LIMIT_VARIABLE_NUMBER, newValue); + } + + int trigger_depth() { + return this->get(SQLITE_LIMIT_TRIGGER_DEPTH); + } + + void trigger_depth(int newValue) { + this->set(SQLITE_LIMIT_TRIGGER_DEPTH, newValue); + } + +#if SQLITE_VERSION_NUMBER >= 3008007 + int worker_threads() { + return this->get(SQLITE_LIMIT_WORKER_THREADS); + } + + void worker_threads(int newValue) { + this->set(SQLITE_LIMIT_WORKER_THREADS, newValue); + } +#endif + + protected: + get_connection_t get_connection; + + friend struct storage_base; + + /** + * Stores limit set between connections. + */ + std::map limits; + + int get(int id) { + auto connection = this->get_connection(); + return sqlite3_limit(connection.get(), id, -1); + } + + void set(int id, int newValue) { + this->limits[id] = newValue; + auto connection = this->get_connection(); + sqlite3_limit(connection.get(), id, newValue); + } + }; + } +} + +// #include "transaction_guard.h" + +#include // std::function +#include // std::move + +// #include "connection_holder.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * Class used as a guard for a transaction. Calls `ROLLBACK` in destructor. + * Has explicit `commit()` and `rollback()` functions. After explicit function is fired + * guard won't do anything in d-tor. Also you can set `commit_on_destroy` to true to + * make it call `COMMIT` on destroy. + * + * Note: The guard's destructor is explicitly marked as potentially throwing, + * so exceptions that occur during commit or rollback are propagated to the caller. + */ + struct transaction_guard_t { + /** + * This is a public lever to tell a guard what it must do in its destructor + * if `gotta_fire` is true + */ + bool commit_on_destroy = false; + + transaction_guard_t(connection_ref connection_, + std::function commit_func_, + std::function rollback_func_) : + connection(std::move(connection_)), + commit_func(std::move(commit_func_)), rollback_func(std::move(rollback_func_)) {} + + transaction_guard_t(transaction_guard_t&& other) : + commit_on_destroy(other.commit_on_destroy), connection(std::move(other.connection)), + commit_func(std::move(other.commit_func)), rollback_func(std::move(other.rollback_func)), + gotta_fire(other.gotta_fire) { + other.gotta_fire = false; + } + + ~transaction_guard_t() noexcept(false) { + if(this->gotta_fire) { + if(this->commit_on_destroy) { + this->commit_func(); + } else { + this->rollback_func(); + } + } + } + + transaction_guard_t& operator=(transaction_guard_t&&) = delete; + + /** + * Call `COMMIT` explicitly. After this call + * guard will not call `COMMIT` or `ROLLBACK` + * in its destructor. + */ + void commit() { + this->gotta_fire = false; + this->commit_func(); + } + + /** + * Call `ROLLBACK` explicitly. After this call + * guard will not call `COMMIT` or `ROLLBACK` + * in its destructor. + */ + void rollback() { + this->gotta_fire = false; + this->rollback_func(); + } + + protected: + connection_ref connection; + std::function commit_func; + std::function rollback_func; + bool gotta_fire = true; + }; + } +} + +// #include "row_extractor.h" + +// #include "connection_holder.h" + +// #include "backup.h" + +#include +#include // std::system_error +#include // std::string +#include +#include // std::move, std::exchange + +// #include "error_code.h" + +// #include "connection_holder.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * A backup class. Don't construct it as is, call storage.make_backup_from or storage.make_backup_to instead. + * An instance of this class represents a wrapper around sqlite3_backup pointer. Use this class + * to have maximum control on a backup operation. In case you need a single backup in one line you + * can skip creating a backup_t instance and just call storage.backup_from or storage.backup_to function. + */ + struct backup_t { + backup_t(connection_ref to_, + const std::string& zDestName, + connection_ref from_, + const std::string& zSourceName, + std::unique_ptr holder_) : + handle(sqlite3_backup_init(to_.get(), zDestName.c_str(), from_.get(), zSourceName.c_str())), + holder(std::move(holder_)), to(to_), from(from_) { + if(!this->handle) { + throw std::system_error{orm_error_code::failed_to_init_a_backup}; + } + } + + backup_t(backup_t&& other) : + handle(std::exchange(other.handle, nullptr)), holder(std::move(other.holder)), to(other.to), + from(other.from) {} + + ~backup_t() { + if(this->handle) { + (void)sqlite3_backup_finish(this->handle); + } + } + + /** + * Calls sqlite3_backup_step with pages argument + */ + int step(int pages) { + return sqlite3_backup_step(this->handle, pages); + } + + /** + * Returns sqlite3_backup_remaining result + */ + int remaining() const { + return sqlite3_backup_remaining(this->handle); + } + + /** + * Returns sqlite3_backup_pagecount result + */ + int pagecount() const { + return sqlite3_backup_pagecount(this->handle); + } + + protected: + sqlite3_backup* handle = nullptr; + std::unique_ptr holder; + connection_ref to; + connection_ref from; + }; + } +} + +// #include "function.h" + +// #include "values_to_tuple.h" + +#include +#include // std::index_sequence, std::make_index_sequence +#include // std::tuple, std::tuple_size, std::get + +// #include "functional/cxx_universal.h" + +// #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{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) { + sqlite3_value* value = this->values[index]; + return {value}; + } else { + throw std::system_error{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 { + + struct values_to_tuple { + template + void operator()(sqlite3_value** values, Tpl& tuple, int /*argsCount*/) const { + (*this)(values, tuple, std::make_index_sequence::value>{}); + } + + void operator()(sqlite3_value** values, std::tuple& tuple, int argsCount) const { + std::get<0>(tuple) = arg_values(argsCount, values); + } + + private: +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + void operator()(sqlite3_value** values, Tpl& tuple, std::index_sequence) const { + (this->extract(values[Idx], std::get(tuple)), ...); + } +#else + template + void operator()(sqlite3_value** values, Tpl& tuple, std::index_sequence) const { + this->extract(values[I], std::get(tuple)); + (*this)(values, tuple, std::index_sequence{}); + } + template + void operator()(sqlite3_value** /*values*/, Tpl&, std::index_sequence) const {} +#endif + template + void extract(sqlite3_value* value, T& t) const { + t = row_extractor{}.extract(value); + } + }; + } +} + +// #include "arg_values.h" + +// #include "util.h" + +// #include "serializing_util.h" + +namespace sqlite_orm { + + namespace internal { + + struct storage_base { + using collating_function = std::function; + + std::function on_open; + pragma_t pragma; + limit_accessor limit; + + transaction_guard_t transaction_guard() { + this->begin_transaction(); + return {this->get_connection(), + std::bind(&storage_base::commit, this), + std::bind(&storage_base::rollback, this)}; + } + + void drop_index(const std::string& indexName) { + std::stringstream ss; + ss << "DROP INDEX " << quote_identifier(indexName) << std::flush; + perform_void_exec(this->get_connection().get(), ss.str()); + } + + void drop_trigger(const std::string& triggerName) { + std::stringstream ss; + ss << "DROP TRIGGER " << quote_identifier(triggerName) << std::flush; + perform_void_exec(this->get_connection().get(), ss.str()); + } + + void vacuum() { + perform_void_exec(this->get_connection().get(), "VACUUM"); + } + + /** + * Drops table with given name. + */ + void drop_table(const std::string& tableName) { + this->drop_table_internal(this->get_connection().get(), tableName); + } + + /** + * Rename table named `from` to `to`. + */ + void rename_table(const std::string& from, const std::string& to) { + this->rename_table(this->get_connection().get(), from, to); + } + + protected: + void rename_table(sqlite3* db, const std::string& oldName, const std::string& newName) const { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(oldName) << " RENAME TO " << streaming_identifier(newName) + << std::flush; + perform_void_exec(db, ss.str()); + } + + /** + * Checks whether table exists in db. Doesn't check storage itself - works only with actual database. + * 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) { + auto con = this->get_connection(); + return this->table_exists(con.get(), tableName); + } + + bool table_exists(sqlite3* db, const std::string& tableName) const { + bool result = false; + std::stringstream ss; + ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = " << streaming_identifier("table") + << " AND name = " << quote_string_literal(tableName) << std::flush; + perform_exec( + db, + ss.str(), + [](void* data, int argc, char** argv, char** /*azColName*/) -> int { + auto& res = *(bool*)data; + if(argc) { + res = !!std::atoi(argv[0]); + } + return 0; + }, + &result); + return result; + } + + void add_generated_cols(std::vector& columnsToAdd, + const std::vector& storageTableInfo) { + // iterate through storage columns + for(const table_xinfo& storageColumnInfo: storageTableInfo) { + if(storageColumnInfo.hidden) { + columnsToAdd.push_back(&storageColumnInfo); + } + } + } + + public: + /** + * sqlite3_changes function. + */ + int changes() { + auto con = this->get_connection(); + return sqlite3_changes(con.get()); + } + + /** + * sqlite3_total_changes function. + */ + int total_changes() { + auto con = this->get_connection(); + return sqlite3_total_changes(con.get()); + } + + int64 last_insert_rowid() { + auto con = this->get_connection(); + return sqlite3_last_insert_rowid(con.get()); + } + + int busy_timeout(int ms) { + auto con = this->get_connection(); + return sqlite3_busy_timeout(con.get(), ms); + } + + /** + * Returns libsqlite3 version, not sqlite_orm + */ + std::string libversion() { + return sqlite3_libversion(); + } + + bool transaction(const std::function& f) { + auto guard = this->transaction_guard(); + return guard.commit_on_destroy = f(); + } + + std::string current_timestamp() { + auto con = this->get_connection(); + return this->current_timestamp(con.get()); + } + +#if SQLITE_VERSION_NUMBER >= 3007010 + /** + * \fn db_release_memory + * \brief Releases freeable memory of database. It is function can/should be called periodically by + * application, if application has less memory usage constraint. \note sqlite3_db_release_memory added + * in 3.7.10 https://sqlite.org/changes.html + */ + int db_release_memory() { + auto con = this->get_connection(); + return sqlite3_db_release_memory(con.get()); + } +#endif + + /** + * Returns existing permanent table names in database. Doesn't check storage itself - works only with + * actual database. + * @return Returns list of tables in database. + */ + std::vector table_names() { + auto con = this->get_connection(); + std::vector tableNames; + using data_t = std::vector; + perform_exec( + con.get(), + "SELECT name FROM sqlite_master WHERE type='table'", + [](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_.emplace_back(argv[i]); + } + } + return 0; + }, + &tableNames); + return tableNames; + } + + void open_forever() { + this->isOpenedForever = true; + this->connection->retain(); + if(1 == this->connection->retain_count()) { + this->on_open_internal(this->connection->get()); + } + } + + /** + * 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"; + * } + * }; + * ``` + * + * Note: Currently, a function's name must not contain white-space characters, because it doesn't get quoted. + */ + template + void create_scalar_function() { + static_assert(is_scalar_function_v, "F can't be an aggregate function"); + + std::stringstream ss; + ss << F::name() << std::flush; + auto name = ss.str(); + using args_tuple = typename callable_arguments::args_tuple; + using return_type = typename callable_arguments::return_type; + constexpr auto argsCount = std::is_same>::value + ? -1 + : int(std::tuple_size::value); + this->scalarFunctions.emplace_back(new user_defined_scalar_function_t{ + std::move(name), + argsCount, + []() -> int* { + return (int*)(new F()); + }, + /* call = */ + [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { + auto& function = *static_cast(functionVoidPointer); + args_tuple argsTuple; + values_to_tuple{}(values, argsTuple, argsCount); + auto result = call(function, std::move(argsTuple)); + statement_binder().result(context, result); + }, + delete_function_callback, + }); + + if(this->connection->retain_count() > 0) { + sqlite3* 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"; + * } + * }; + * ``` + * + * Note: Currently, a function's name must not contain white-space characters, because it doesn't get quoted. + */ + template + void create_aggregate_function() { + static_assert(is_aggregate_function_v, "F can't be a scalar function"); + + std::stringstream ss; + ss << F::name() << std::flush; + auto name = ss.str(); + using args_tuple = typename callable_arguments::args_tuple; + using return_type = typename callable_arguments::return_type; + constexpr auto argsCount = std::is_same>::value + ? -1 + : int(std::tuple_size::value); + this->aggregateFunctions.emplace_back(new user_defined_aggregate_function_t{ + std::move(name), + argsCount, + /* create = */ + []() -> int* { + return (int*)(new F()); + }, + /* step = */ + [](sqlite3_context*, void* functionVoidPointer, int argsCount, sqlite3_value** values) { + auto& function = *static_cast(functionVoidPointer); + args_tuple argsTuple; + values_to_tuple{}(values, argsTuple, argsCount); + call(function, &F::step, std::move(argsTuple)); + }, + /* finalCall = */ + [](sqlite3_context* context, void* functionVoidPointer) { + auto& function = *static_cast(functionVoidPointer); + auto result = function.fin(); + statement_binder().result(context, result); + }, + delete_function_callback, + }); + + if(this->connection->retain_count() > 0) { + sqlite3* 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_v, "F cannot be an aggregate function"); + std::stringstream ss; + ss << F::name() << std::flush; + this->delete_function_impl(ss.str(), 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_v, "F cannot be a scalar function"); + std::stringstream ss; + ss << F::name() << std::flush; + this->delete_function_impl(ss.str(), 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() << std::flush; + this->create_collation(ss.str(), std::move(func)); + } + + void create_collation(const std::string& name, collating_function f) { + collating_function* function = nullptr; + const auto functionExists = bool(f); + if(functionExists) { + function = &(collatingFunctions[name] = std::move(f)); + } else { + collatingFunctions.erase(name); + } + + // create collations if db is open + if(this->connection->retain_count() > 0) { + sqlite3* db = this->connection->get(); + auto resultCode = sqlite3_create_collation(db, + name.c_str(), + SQLITE_UTF8, + function, + functionExists ? collate_callback : nullptr); + if(resultCode != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + } + + template + void delete_collation() { + std::stringstream ss; + ss << C::name() << std::flush; + this->create_collation(ss.str(), {}); + } + + void begin_transaction() { + this->begin_transaction_internal("BEGIN TRANSACTION"); + } + + void begin_deferred_transaction() { + this->begin_transaction_internal("BEGIN DEFERRED TRANSACTION"); + } + + void begin_immediate_transaction() { + this->begin_transaction_internal("BEGIN IMMEDIATE TRANSACTION"); + } + + void begin_exclusive_transaction() { + this->begin_transaction_internal("BEGIN EXCLUSIVE TRANSACTION"); + } + + void commit() { + sqlite3* db = this->connection->get(); + perform_void_exec(db, "COMMIT"); + this->connection->release(); + if(this->connection->retain_count() < 0) { + throw std::system_error{orm_error_code::no_active_transaction}; + } + } + + void rollback() { + sqlite3* db = this->connection->get(); + perform_void_exec(db, "ROLLBACK"); + this->connection->release(); + if(this->connection->retain_count() < 0) { + throw std::system_error{orm_error_code::no_active_transaction}; + } + } + + void backup_to(const std::string& filename) { + auto backup = this->make_backup_to(filename); + backup.step(-1); + } + + void backup_to(storage_base& other) { + auto backup = this->make_backup_to(other); + backup.step(-1); + } + + void backup_from(const std::string& filename) { + auto backup = this->make_backup_from(filename); + backup.step(-1); + } + + 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) { + auto holder = std::make_unique(filename); + connection_ref conRef{*holder}; + return {conRef, "main", this->get_connection(), "main", std::move(holder)}; + } + + 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) { + auto holder = std::make_unique(filename); + connection_ref conRef{*holder}; + return {this->get_connection(), "main", conRef, "main", std::move(holder)}; + } + + backup_t make_backup_from(storage_base& other) { + return {this->get_connection(), "main", other.get_connection(), "main", {}}; + } + + const std::string& filename() const { + return this->connection->filename; + } + + /** + * Checks whether connection to database is opened right now. + * Returns always `true` for in memory databases. + */ + bool is_opened() const { + return this->connection->retain_count() > 0; + } + + /* + * returning false when there is a transaction in place + * otherwise true; function is not const because it has to call get_connection() + */ + bool get_autocommit() { + auto con = this->get_connection(); + return sqlite3_get_autocommit(con.get()); + } + + int busy_handler(std::function handler) { + _busy_handler = std::move(handler); + if(this->is_opened()) { + if(_busy_handler) { + return sqlite3_busy_handler(this->connection->get(), busy_handler_callback, this); + } else { + return sqlite3_busy_handler(this->connection->get(), nullptr, nullptr); + } + } else { + return SQLITE_OK; + } + } + + protected: + storage_base(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:"), + connection(std::make_unique(std::move(filename))), + cachedForeignKeysCount(foreignKeysCount) { + if(this->inMemory) { + this->connection->retain(); + this->on_open_internal(this->connection->get()); + } + } + + 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)), + cachedForeignKeysCount(other.cachedForeignKeysCount) { + if(this->inMemory) { + this->connection->retain(); + this->on_open_internal(this->connection->get()); + } + } + + ~storage_base() { + if(this->isOpenedForever) { + this->connection->release(); + } + if(this->inMemory) { + this->connection->release(); + } + } + + void begin_transaction_internal(const std::string& query) { + this->connection->retain(); + if(1 == this->connection->retain_count()) { + this->on_open_internal(this->connection->get()); + } + sqlite3* db = this->connection->get(); + perform_void_exec(db, query); + } + + connection_ref get_connection() { + connection_ref res{*this->connection}; + if(1 == this->connection->retain_count()) { + this->on_open_internal(this->connection->get()); + } + return res; + } + +#if SQLITE_VERSION_NUMBER >= 3006019 + + void foreign_keys(sqlite3* db, bool value) { + std::stringstream ss; + ss << "PRAGMA foreign_keys = " << value << std::flush; + perform_void_exec(db, ss.str()); + } + + bool foreign_keys(sqlite3* db) { + bool result = false; + perform_exec(db, "PRAGMA foreign_keys", extract_single_value, &result); + return result; + } + +#endif + void on_open_internal(sqlite3* db) { + +#if SQLITE_VERSION_NUMBER >= 3006019 + if(this->cachedForeignKeysCount) { + this->foreign_keys(db, true); + } +#endif + if(this->pragma._synchronous != -1) { + this->pragma.synchronous(this->pragma._synchronous); + } + + if(this->pragma._journal_mode != -1) { + this->pragma.set_pragma("journal_mode", static_cast(this->pragma._journal_mode), db); + } + + 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_translated_sqlite_error(db); + } + } + + for(auto& p: this->limit.limits) { + sqlite3_limit(db, p.first, p.second); + } + + if(_busy_handler) { + 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 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) { + sqlite3* 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_translated_sqlite_error(db); + } + } + } else { + throw std::system_error{orm_error_code::function_not_found}; + } + } + + void try_to_create_function(sqlite3* db, user_defined_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_translated_sqlite_error(db); + } + } + + void try_to_create_function(sqlite3* db, user_defined_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_translated_sqlite_error(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{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; + perform_exec(db, "SELECT CURRENT_TIMESTAMP", extract_single_value, &result); + return result; + } + + void drop_table_internal(sqlite3* db, const std::string& tableName) { + std::stringstream ss; + ss << "DROP TABLE " << streaming_identifier(tableName) << std::flush; + perform_void_exec(db, ss.str()); + } + + 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); + if(storage._busy_handler) { + return storage._busy_handler(triesCount); + } else { + return 0; + } + } + + bool calculate_remove_add_columns(std::vector& columnsToAdd, + std::vector& storageTableInfo, + std::vector& dbTableInfo) const { + bool notEqual = false; + + // iterate through storage columns + for(size_t storageColumnInfoIndex = 0; storageColumnInfoIndex < storageTableInfo.size(); + ++storageColumnInfoIndex) { + + // get storage's column info + 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) { + return ti.name == columnName; + }); + if(dbColumnInfoIt != dbTableInfo.end()) { + auto& dbColumnInfo = *dbColumnInfoIt; + auto columnsAreEqual = + dbColumnInfo.name == storageColumnInfo.name && + dbColumnInfo.notnull == storageColumnInfo.notnull && + (!dbColumnInfo.dflt_value.empty()) == (!storageColumnInfo.dflt_value.empty()) && + dbColumnInfo.pk == storageColumnInfo.pk && + (dbColumnInfo.hidden == 0) == (storageColumnInfo.hidden == 0); + if(!columnsAreEqual) { + notEqual = true; + break; + } + dbTableInfo.erase(dbColumnInfoIt); + storageTableInfo.erase(storageTableInfo.begin() + + static_cast(storageColumnInfoIndex)); + --storageColumnInfoIndex; + } else { + columnsToAdd.push_back(&storageColumnInfo); + } + } + return notEqual; + } + + 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; + }; + } +} + +// #include "prepared_statement.h" + +// #include "expression_object_type.h" + +#include // std::decay +#include // std::reference_wrapper + +// #include "prepared_statement.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct expression_object_type; + + template + struct expression_object_type> : std::decay {}; + + template + struct expression_object_type>> : std::decay {}; + + template + struct expression_object_type> : std::decay {}; + + template + struct expression_object_type>> : std::decay {}; + + 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 = T; + }; + + template + struct expression_object_type, Ids...>> { + using type = T; + }; + + template + struct expression_object_type> : std::decay {}; + + template + struct expression_object_type>> : std::decay {}; + + template + struct expression_object_type> { + 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> : std::decay {}; + + template + struct expression_object_type, Cols...>> : std::decay {}; + + template + struct get_ref_t { + + template + auto& operator()(O& t) const { + return t; + } + }; + + template + struct get_ref_t> { + + template + auto& operator()(O& t) const { + return t.get(); + } + }; + + template + auto& get_ref(T& t) { + using arg_type = std::decay_t; + get_ref_t g; + return g(t); + } + + template + struct get_object_t; + + template + struct get_object_t : get_object_t {}; + + template + auto& get_object(T& t) { + using expression_type = std::decay_t; + get_object_t obj; + return obj(t); + } + + template + struct get_object_t> { + using expression_type = replace_t; + + template + auto& operator()(O& e) const { + return get_ref(e.object); + } + }; + + template + struct get_object_t> { + using expression_type = insert_t; + + template + auto& operator()(O& e) const { + return get_ref(e.object); + } + }; + + template + struct get_object_t> { + using expression_type = update_t; + + template + auto& operator()(O& e) const { + return get_ref(e.object); + } + }; + } +} + +// #include "statement_serializer.h" + +#include // std::stringstream +#include // std::string +#include // std::enable_if, std::remove_pointer +#include // std::vector +#include // std::iter_swap +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::codecvt_utf8_utf16 +#endif // SQLITE_ORM_OMITS_CODECVT +#include +#include +// #include "functional/cxx_string_view.h" + +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_functional_polyfill.h" + +// #include "functional/mpl.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "ast/upsert_clause.h" + +// #include "ast/excluded.h" + +// #include "ast/group_by.h" + +// #include "ast/into.h" + +// #include "core_functions.h" + +// #include "constraints.h" + +// #include "conditions.h" + +// #include "column.h" + +// #include "indexed_column.h" + +// #include "function.h" + +// #include "prepared_statement.h" + +// #include "rowid.h" + +// #include "pointer_value.h" + +// #include "type_printer.h" + +// #include "field_printer.h" + +// #include "literal.h" + +// #include "table_name_collector.h" + +// #include "column_names_getter.h" + +#include // std::is_base_of +#include // std::string +#include // std::vector +#include // std::reference_wrapper +#include +#include // std::move + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "error_code.h" + +// #include "mapped_type_proxy.h" + +// #include "alias_traits.h" + +// #include "select_constraints.h" + +// #include "storage_lookup.h" +// pick_table +// #include "serializer_context.h" + +// #include "util.h" + +namespace sqlite_orm { + + namespace internal { + + template + std::string serialize(const T&, const serializer_context&); + + template + std::vector& collect_table_column_names(std::vector& collectedExpressions, + bool definedOrder, + const Ctx& context) { + if(definedOrder) { + auto& table = pick_table>(context.db_objects); + collectedExpressions.reserve(collectedExpressions.size() + table.count_columns_amount()); + table.for_each_column([qualified = !context.skip_table_name, + &tableName = table.name, + &collectedExpressions](const column_identifier& column) { + if(is_alias_v) { + collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + "." + + quote_identifier(column.name)); + } else if(qualified) { + collectedExpressions.push_back(quote_identifier(tableName) + "." + + quote_identifier(column.name)); + } else { + collectedExpressions.push_back(quote_identifier(column.name)); + } + }); + } else { + collectedExpressions.reserve(collectedExpressions.size() + 1); + if(is_alias_v) { + collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + ".*"); + } else if(!context.skip_table_name) { + const basic_table& table = pick_table>(context.db_objects); + collectedExpressions.push_back(quote_identifier(table.name) + ".*"); + } else { + collectedExpressions.emplace_back("*"); + } + } + + return collectedExpressions; + } + + /** @short Column expression collector. + */ + struct column_names_getter { + /** + * The default implementation simply serializes the passed argument. + */ + template + std::vector& operator()(const E& t, const Ctx& context) { + auto columnExpression = serialize(t, context); + if(columnExpression.empty()) { + throw std::system_error{orm_error_code::column_not_found}; + } + collectedExpressions.reserve(collectedExpressions.size() + 1); + collectedExpressions.push_back(std::move(columnExpression)); + return collectedExpressions; + } + + template + std::vector& operator()(const std::reference_wrapper& expression, const Ctx& context) { + return (*this)(expression.get(), context); + } + + template + std::vector& operator()(const asterisk_t& expression, const Ctx& context) { + return collect_table_column_names(collectedExpressions, expression.defined_order, context); + } + + template + std::vector& operator()(const object_t& expression, const Ctx& context) { + return collect_table_column_names(collectedExpressions, expression.defined_order, context); + } + + template + std::vector& operator()(const columns_t& cols, const Ctx& context) { + collectedExpressions.reserve(collectedExpressions.size() + cols.count); + iterate_tuple(cols.columns, [this, &context](auto& colExpr) { + (*this)(colExpr, context); + }); + // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order + if(mpl::instantiate, + typename columns_t::columns_type>::value && + collectedExpressions.capacity() > collectedExpressions.size()) { + collectedExpressions.shrink_to_fit(); + } + return collectedExpressions; + } + + std::vector collectedExpressions; + }; + + template + std::vector get_column_names(const T& t, const Ctx& context) { + column_names_getter serializer; + return serializer(t, context); + } + } +} + +// #include "order_by_serializer.h" + +#include // std::string +#include // std::stringstream + +namespace sqlite_orm { + + namespace internal { + + template + struct order_by_serializer; + + template + std::string serialize_order_by(const T& t, const Ctx& context) { + order_by_serializer serializer; + return serializer(t, context); + } + + template + struct order_by_serializer, void> { + using statement_type = order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + + ss << serialize(orderBy.expression, newContext); + if(!orderBy._collate_argument.empty()) { + ss << " COLLATE " << orderBy._collate_argument; + } + switch(orderBy.asc_desc) { + case 1: + ss << " ASC"; + break; + case -1: + ss << " DESC"; + break; + } + return ss.str(); + } + }; + + template + struct order_by_serializer, void> { + using statement_type = dynamic_order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx&) const { + std::stringstream ss; + ss << static_cast(orderBy) << " "; + int index = 0; + for(const dynamic_order_by_entry_t& entry: orderBy) { + if(index > 0) { + ss << ", "; + } + + ss << entry.name; + if(!entry._collate_argument.empty()) { + ss << " COLLATE " << entry._collate_argument; + } + switch(entry.asc_desc) { + case 1: + ss << " ASC"; + break; + case -1: + ss << " DESC"; + break; + } + ++index; + }; + return ss.str(); + } + }; + + } +} + +// #include "serializing_util.h" + +// #include "statement_binder.h" + +// #include "values.h" + +// #include "triggers.h" + +// #include "table_type_of.h" + +// #include "index.h" + +// #include "util.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct statement_serializer; + + template + std::string serialize(const T& t, const serializer_context& context) { + statement_serializer serializer; + return serializer(t, context); + } + + /** + * Serializer for bindable types. + */ + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const T& statement, const Ctx& context) const { + if(context.replace_bindable_with_question) { + return "?"; + } else { + return this->do_serialize(statement); + } + } + + private: + template && !std::is_base_of::value +#ifndef SQLITE_ORM_OMITS_CODECVT + && !std::is_base_of::value +#endif + , + bool> = true> + std::string do_serialize(const X& c) const { + static_assert(std::is_same::value, ""); + + // implementation detail: utilizing field_printer + return field_printer{}(c); + } + + std::string do_serialize(const std::string& c) const { + // implementation detail: utilizing field_printer + return quote_string_literal(field_printer{}(c)); + } + + std::string do_serialize(const char* c) const { + return quote_string_literal(c); + } +#ifndef SQLITE_ORM_OMITS_CODECVT + std::string do_serialize(const std::wstring& c) const { + // implementation detail: utilizing field_printer + return quote_string_literal(field_printer{}(c)); + } + + std::string do_serialize(const wchar_t* c) const { + std::wstring_convert> converter; + return quote_string_literal(converter.to_bytes(c)); + } +#endif +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::string do_serialize(const std::string_view& c) const { + return quote_string_literal(std::string(c)); + } +#ifndef SQLITE_ORM_OMITS_CODECVT + std::string do_serialize(const std::wstring_view& c) const { + std::wstring_convert> converter; + return quote_string_literal(converter.to_bytes(c.data(), c.data() + c.size())); + } +#endif +#endif + /** + * Specialization for binary data (std::vector). + */ + std::string do_serialize(const std::vector& t) const { + return quote_blob_literal(field_printer>{}(t)); + } + + template + std::string do_serialize(const pointer_binding&) const { + // always serialize null (security reasons) + return field_printer{}(nullptr); + } + }; + + /** + * Serializer for literal values. + */ + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const T& literal, const Ctx& context) const { + static_assert(is_bindable_v>, "A literal value must be also bindable"); + + Ctx literalCtx = context; + literalCtx.replace_bindable_with_question = false; + statement_serializer> serializer{}; + return serializer(literal.value, literalCtx); + } + }; + + template + struct statement_serializer, void> { + using statement_type = filtered_aggregate_function; + + template + std::string operator()(const statement_type& statement, const Ctx& context) { + std::stringstream ss; + ss << serialize(statement.function, context); + ss << " FILTER (WHERE " << serialize(statement.where, context) << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = excluded_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "excluded."; + if(auto* columnName = find_column_name(context.db_objects, statement.expression)) { + ss << streaming_identifier(*columnName); + } else { + throw std::system_error{orm_error_code::column_not_found}; + } + return ss.str(); + } + }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializer, void> { + using statement_type = as_optional_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + return serialize(statement.value, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializer, void> { + using statement_type = std::reference_wrapper; + + template + std::string operator()(const statement_type& s, const Ctx& context) const { + return serialize(s.get(), context); + } + }; + + template + struct statement_serializer, void> { + using statement_type = alias_holder; + + template + std::string operator()(const statement_type&, const Ctx&) { + std::stringstream ss; + ss << streaming_identifier(T::get()); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = column_alias; + + template + std::string operator()(const statement_type&, const Ctx&) { + std::stringstream ss; + ss << streaming_identifier(statement_type::get()); + return ss.str(); + } + }; + + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "ON CONFLICT"; + iterate_tuple(statement.target_args, [&ss, &context](auto& value) { + using value_type = std::decay_t; + 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 { + auto updateContext = context; + updateContext.use_parentheses = false; + ss << " UPDATE " << streaming_actions_tuple(statement.actions, updateContext); + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = built_in_function_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << statement.serialize() << "(" << streaming_expressions_tuple(statement.args, context) << ")"; + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> + : statement_serializer, void> {}; + + template + struct statement_serializer, void> { + using statement_type = function_call; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << F::name() << "(" << streaming_expressions_tuple(statement.args, context) << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = as_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.expression, context) + " AS " << streaming_identifier(alias_extractor::extract()); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = alias_column_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + if(!context.skip_table_name) { + ss << streaming_identifier(alias_extractor::extract()) << "."; + } + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(c.column, newContext); + return ss.str(); + } + }; + + template + struct statement_serializer< + E, + std::enable_if_t, is_column_pointer>>> { + using statement_type = E; + + template + std::string operator()(const statement_type& e, const Ctx& context) const { + std::stringstream ss; + if(auto* columnName = find_column_name(context.db_objects, e)) { + ss << streaming_identifier( + !context.skip_table_name ? lookup_table_name>(context.db_objects) : "", + *columnName, + ""); + } else { + throw std::system_error{orm_error_code::column_not_found}; + } + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = rowid_t; + + template + std::string operator()(const statement_type& s, const Ctx&) const { + return static_cast(s); + } + }; + + template<> + struct statement_serializer { + using statement_type = oid_t; + + template + std::string operator()(const statement_type& s, const Ctx&) const { + return static_cast(s); + } + }; + + template<> + struct statement_serializer<_rowid_t, void> { + using statement_type = _rowid_t; + + template + std::string operator()(const statement_type& s, const Ctx&) const { + return static_cast(s); + } + }; + + template + struct statement_serializer, void> { + using statement_type = table_rowid_t; + + template + std::string operator()(const statement_type& s, const Ctx& context) const { + std::stringstream ss; + if(!context.skip_table_name) { + ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; + } + ss << static_cast(s); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = table_oid_t; + + template + std::string operator()(const statement_type& s, const Ctx& context) const { + std::stringstream ss; + if(!context.skip_table_name) { + ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; + } + ss << static_cast(s); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = table__rowid_t; + + template + std::string operator()(const statement_type& s, const Ctx& context) const { + std::stringstream ss; + if(!context.skip_table_name) { + ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; + } + ss << static_cast(s); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = binary_operator; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + auto lhs = serialize(statement.lhs, context); + auto rhs = serialize(statement.rhs, context); + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << lhs << " " << statement.serialize() << " " << rhs; + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = count_asterisk_t; + + template + std::string operator()(const statement_type&, const Ctx& context) const { + return serialize(count_asterisk_without_type{}, context); + } + }; + + template<> + struct statement_serializer { + using statement_type = count_asterisk_without_type; + + template + std::string operator()(const statement_type& c, const Ctx&) const { + std::stringstream ss; + auto functionName = c.serialize(); + ss << functionName << "(*)"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = distinct_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + auto expr = serialize(c.value, context); + ss << static_cast(c) << "(" << expr << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = all_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + auto expr = serialize(c.value, context); + ss << static_cast(c) << "(" << expr << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = cast_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(c) << " ("; + ss << serialize(c.expression, context) << " AS " << type_printer().print() << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.left, context) << " "; + ss << static_cast(c) << " "; + ss << serialize(c.right, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = simple_case_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << "CASE "; + c.case_expression.apply([&ss, context](auto& c_) { + ss << serialize(c_, context) << " "; + }); + 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) { + ss << "ELSE " << serialize(el, context) << " "; + }); + ss << "END"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = is_null_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.t, context) << " " << static_cast(c); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = is_not_null_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.t, context) << " " << static_cast(c); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = bitwise_not_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << statement.serialize() << " "; + auto cString = serialize(statement.argument, context); + ss << " (" << cString << " )"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = negated_condition_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(c) << " "; + auto cString = serialize(c.c, context); + ss << " (" << cString << " )"; + return ss.str(); + } + }; + + template + struct statement_serializer> { + using statement_type = T; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + auto leftString = serialize(c.l, context); + auto rightString = serialize(c.r, context); + std::stringstream ss; + if(context.use_parentheses) { + ss << "("; + } + ss << leftString << " " << static_cast(c) << " " << rightString; + if(context.use_parentheses) { + ss << ")"; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = named_collate; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + auto newContext = context; + newContext.use_parentheses = false; + auto res = serialize(c.expr, newContext); + return res + " " + static_cast(c); + } + }; + + template + struct statement_serializer, void> { + using statement_type = collate_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + auto newContext = context; + newContext.use_parentheses = false; + auto res = serialize(c.expr, newContext); + return res + " " + static_cast(c); + } + }; + + template + struct statement_serializer, void> { + using statement_type = dynamic_in_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto leftString = serialize(statement.left, context); + ss << leftString << " "; + if(!statement.negative) { + ss << "IN"; + } else { + ss << "NOT IN"; + } + ss << " "; + if(is_compound_operator_v) { + ss << '('; + } + auto newContext = context; + newContext.use_parentheses = true; + ss << serialize(statement.argument, newContext); + if(is_compound_operator_v) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer>, void> { + using statement_type = dynamic_in_t>; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto leftString = serialize(statement.left, context); + ss << leftString << " "; + if(!statement.negative) { + ss << "IN"; + } else { + ss << "NOT IN"; + } + ss << " (" << streaming_dynamic_expressions(statement.argument, context) << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = in_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto leftString = serialize(statement.left, context); + ss << leftString << " "; + if(!statement.negative) { + ss << "IN"; + } else { + ss << "NOT IN"; + } + ss << " "; + using args_type = std::tuple; + constexpr bool theOnlySelect = + std::tuple_size::value == 1 && is_select_v>; + if(!theOnlySelect) { + ss << "("; + } + ss << streaming_expressions_tuple(statement.argument, context); + if(!theOnlySelect) { + ss << ")"; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = like_t; + + template + std::string operator()(const statement_type& c, const Ctx& 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) { + ss << " ESCAPE " << serialize(value, context); + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = glob_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.arg, context) << " "; + ss << static_cast(c) << " "; + ss << serialize(c.pattern, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = between_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + auto expr = serialize(c.expr, context); + ss << expr << " " << static_cast(c) << " "; + ss << serialize(c.b1, context); + ss << " AND "; + ss << serialize(c.b2, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = exists_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.use_parentheses = true; + ss << "EXISTS " << serialize(statement.expression, newContext); + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = autoincrement_t; + + template + std::string operator()(const statement_type&, const Ctx&) const { + return "AUTOINCREMENT"; + } + }; + + template<> + struct statement_serializer { + using statement_type = conflict_clause_t; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + switch(statement) { + case conflict_clause_t::rollback: + return "ROLLBACK"; + case conflict_clause_t::abort: + return "ABORT"; + case conflict_clause_t::fail: + return "FAIL"; + case conflict_clause_t::ignore: + return "IGNORE"; + case conflict_clause_t::replace: + return "REPLACE"; + } + return {}; + } + }; + + template + struct statement_serializer, void> { + using statement_type = primary_key_with_autoincrement; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + return serialize(statement.primary_key, context) + " AUTOINCREMENT"; + } + }; + + template + struct statement_serializer, void> { + using statement_type = primary_key_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "PRIMARY KEY"; + switch(statement.options.asc_option) { + case statement_type::order_by::ascending: + ss << " ASC"; + break; + case statement_type::order_by::descending: + ss << " DESC"; + break; + default: + break; + } + if(statement.options.conflict_clause_is_on) { + ss << " ON CONFLICT " << serialize(statement.options.conflict_clause, context); + } + using columns_tuple = typename statement_type::columns_tuple; + const size_t columnsCount = std::tuple_size::value; + if(columnsCount) { + ss << "(" << streaming_mapped_columns_expressions(statement.columns, context) << ")"; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = unique_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(c); + using columns_tuple = typename statement_type::columns_tuple; + const size_t columnsCount = std::tuple_size::value; + if(columnsCount) { + ss << "(" << streaming_mapped_columns_expressions(c.columns, context) << ")"; + } + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = collate_constraint_t; + + template + std::string operator()(const statement_type& c, const Ctx&) const { + return static_cast(c); + } + }; + + template + struct statement_serializer, void> { + using statement_type = default_t; + + template + std::string operator()(const statement_type& c, const Ctx& context) const { + return static_cast(c) + " (" + serialize(c.value, context) + ")"; + } + }; + + template + struct statement_serializer, std::tuple>, void> { + using statement_type = foreign_key_t, std::tuple>; + + template + std::string operator()(const statement_type& fk, const Ctx& context) const { + std::stringstream ss; + ss << "FOREIGN KEY(" << streaming_mapped_columns_expressions(fk.columns, context) << ") REFERENCES "; + { + using references_type_t = typename std::decay_t::references_type; + using first_reference_t = std::tuple_element_t<0, references_type_t>; + using first_reference_mapped_type = table_type_of_t; + auto refTableName = lookup_table_name(context.db_objects); + ss << streaming_identifier(refTableName); + } + ss << "(" << streaming_mapped_columns_expressions(fk.references, context) << ")"; + if(fk.on_update) { + ss << ' ' << static_cast(fk.on_update) << " " << fk.on_update._action; + } + if(fk.on_delete) { + ss << ' ' << static_cast(fk.on_delete) << " " << fk.on_delete._action; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = check_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "CHECK (" << serialize(statement.expression, context) << ")"; + return ss.str(); + } + }; +#if SQLITE_VERSION_NUMBER >= 3031000 + template + struct statement_serializer, void> { + using statement_type = generated_always_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if(statement.full) { + ss << "GENERATED ALWAYS "; + } + ss << "AS ("; + ss << serialize(statement.expression, context) << ")"; + switch(statement.storage) { + case basic_generated_always::storage_type::not_specified: + //.. + break; + case basic_generated_always::storage_type::virtual_: + ss << " VIRTUAL"; + break; + case basic_generated_always::storage_type::stored: + ss << " STORED"; + break; + } + return ss.str(); + } + }; +#endif + template + struct statement_serializer, void> { + using statement_type = column_t; + + template + std::string operator()(const statement_type& column, const Ctx& context) const { + using column_type = statement_type; + + std::stringstream ss; + ss << streaming_identifier(column.name) << " " << type_printer>().print() + << " " + << streaming_column_constraints( + call_as_template_base(polyfill::identity{})(column), + column.is_not_null(), + context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = remove_all_t; + + template + std::string operator()(const statement_type& rem, const Ctx& context) const { + auto& table = pick_table(context.db_objects); + + std::stringstream ss; + ss << "DELETE FROM " << streaming_identifier(table.name) + << streaming_conditions_tuple(rem.conditions, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = replace_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using expression_type = std::decay_t; + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + std::stringstream ss; + ss << "REPLACE INTO " << streaming_identifier(table.name) << " (" + << streaming_non_generated_column_names(table) << ")" + << " VALUES (" + << streaming_field_values_excluding(check_if{}, + empty_callable(), // don't exclude + context, + get_ref(statement.object)) + << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = insert_explicit; + + template + std::string operator()(const statement_type& ins, const Ctx& context) const { + constexpr size_t colsCount = std::tuple_size>::value; + static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); + using expression_type = std::decay_t; + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(table.name) << " "; + ss << "(" << streaming_mapped_columns_expressions(ins.columns.columns, context) << ") " + << "VALUES ("; + iterate_tuple(ins.columns.columns, + [&ss, &context, &object = get_ref(ins.obj), first = true](auto& memberPointer) mutable { + using member_pointer_type = std::decay_t; + static_assert(!is_setter_v, + "Unable to use setter within insert explicit"); + + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] + << serialize(polyfill::invoke(memberPointer, object), context); + }); + ss << ")"; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = update_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using expression_type = std::decay_t; + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + + std::stringstream ss; + ss << "UPDATE " << streaming_identifier(table.name) << " SET "; + table.template for_each_column_excluding>( + [&table, &ss, &context, &object = get_ref(statement.object), first = true](auto& column) mutable { + if(table.exists_in_composite_primary_key(column)) { + return; + } + + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << streaming_identifier(column.name) << " = " + << serialize(polyfill::invoke(column.member_pointer, object), context); + }); + ss << " WHERE "; + table.for_each_column( + [&table, &context, &ss, &object = get_ref(statement.object), first = true](auto& column) mutable { + if(!column.template is() && !table.exists_in_composite_primary_key(column)) { + return; + } + + constexpr std::array sep = {" AND ", ""}; + ss << sep[std::exchange(first, false)] << streaming_identifier(column.name) << " = " + << serialize(polyfill::invoke(column.member_pointer, object), context); + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = dynamic_set_t; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + std::stringstream ss; + ss << "SET "; + int index = 0; + for(const dynamic_set_entry& entry: statement) { + if(index > 0) { + ss << ", "; + } + ss << entry.serialized_value; + ++index; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = set_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "SET "; + auto leftContext = context; + leftContext.skip_table_name = true; + iterate_tuple(statement.assigns, [&ss, &context, &leftContext, first = true](auto& value) mutable { + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << serialize(value.lhs, leftContext) << ' ' + << value.serialize() << ' ' << serialize(value.rhs, context); + }); + return ss.str(); + } + }; + + template + std::set> collect_table_names(const set_t& set, const Ctx& ctx) { + auto collector = make_table_name_collector(ctx.db_objects); + iterate_ast(set, collector); + return std::move(collector.table_names); + } + + template + const std::set>& collect_table_names(const dynamic_set_t& set, + const Ctx&) { + return set.collector.table_names; + } + + template = true> + std::set> collect_table_names(const T& sel, const Ctx& ctx) { + auto collector = make_table_name_collector(ctx.db_objects); + iterate_ast(sel.col, collector); + iterate_ast(sel.conditions, collector); + return std::move(collector.table_names); + } + + template = true> + std::set> collect_table_names(const T& table, const Ctx&) { + return {{table.name, ""}}; + } + + template + struct statement_serializer, void> { + using statement_type = update_all_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + const auto& tableNames = collect_table_names(statement.set, context); + if(tableNames.empty()) { + throw std::system_error{orm_error_code::no_tables_specified}; + } + const std::string& tableName = tableNames.begin()->first; + + std::stringstream ss; + ss << "UPDATE " << streaming_identifier(tableName) << ' ' << serialize(statement.set, context) + << streaming_conditions_tuple(statement.conditions, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = insert_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + using is_without_rowid = typename std::decay_t::is_without_rowid; + + std::vector> columnNames; + table.template for_each_column_excluding< + mpl::conjunction>, + mpl::disjunction_fn>>( + [&table, &columnNames](auto& column) { + if(table.exists_in_composite_primary_key(column)) { + return; + } + + columnNames.push_back(cref(column.name)); + }); + const size_t columnNamesCount = columnNames.size(); + + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(table.name) << " "; + if(columnNamesCount) { + ss << "(" << streaming_identifiers(columnNames) << ")"; + } else { + ss << "DEFAULT"; + } + ss << " VALUES"; + if(columnNamesCount) { + ss << " (" + << streaming_field_values_excluding( + mpl::conjunction>, + mpl::disjunction_fn>{}, + [&table](auto& column) { + return table.exists_in_composite_primary_key(column); + }, + context, + get_ref(statement.object)) + << ")"; + } + + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = into_t; + + template + std::string operator()(const statement_type&, const Ctx& context) const { + auto& table = pick_table(context.db_objects); + + std::stringstream ss; + ss << "INTO " << streaming_identifier(table.name); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = columns_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + // note: pass `statement` itself + ss << streaming_serialized(get_column_names(statement, context)); + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer, is_replace_raw>>> { + using statement_type = T; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if(is_insert_raw_v) { + ss << "INSERT"; + } else { + ss << "REPLACE"; + } + iterate_tuple(statement.args, [&context, &ss](auto& value) { + using value_type = std::decay_t; + ss << ' '; + if(is_columns_v) { + auto newContext = context; + newContext.skip_table_name = true; + newContext.use_parentheses = true; + ss << serialize(value, newContext); + } else if(is_values_v || is_select_v) { + auto newContext = context; + newContext.use_parentheses = false; + ss << serialize(value, newContext); + } else { + ss << serialize(value, context); + } + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = remove_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + auto& table = pick_table(context.db_objects); + std::stringstream ss; + ss << "DELETE FROM " << streaming_identifier(table.name) << " " + << "WHERE "; + std::vector idsStrings; + idsStrings.reserve(std::tuple_size::value); + iterate_tuple(statement.ids, [&idsStrings, &context](auto& idValue) { + idsStrings.push_back(serialize(idValue, context)); + }); + table.for_each_primary_key_column([&table, &ss, &idsStrings, index = 0](auto& memberPointer) mutable { + auto* columnName = table.find_column_name(memberPointer); + if(!columnName) { + throw std::system_error{orm_error_code::column_not_found}; + } + + constexpr std::array sep = {" AND ", ""}; + ss << sep[index == 0] << streaming_identifier(*columnName) << " = " << idsStrings[index]; + ++index; + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = replace_range_t; + + template + std::string operator()(const statement_type& rep, const Ctx& context) const { + using expression_type = std::decay_t; + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + + std::stringstream ss; + ss << "REPLACE INTO " << streaming_identifier(table.name) << " (" + << streaming_non_generated_column_names(table) << ")"; + const auto valuesCount = std::distance(rep.range.first, rep.range.second); + const auto columnsCount = table.non_generated_columns_count(); + ss << " VALUES " << streaming_values_placeholders(columnsCount, valuesCount); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = insert_range_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + using object_type = typename expression_object_type::type; + auto& table = pick_table(context.db_objects); + using is_without_rowid = typename std::decay_t::is_without_rowid; + + std::vector> columnNames; + table.template for_each_column_excluding< + mpl::conjunction>, + mpl::disjunction_fn>>( + [&table, &columnNames](auto& column) { + if(table.exists_in_composite_primary_key(column)) { + return; + } + + columnNames.push_back(cref(column.name)); + }); + const size_t valuesCount = std::distance(statement.range.first, statement.range.second); + const size_t columnNamesCount = columnNames.size(); + + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(table.name) << " "; + if(columnNamesCount) { + ss << "(" << streaming_identifiers(columnNames) << ")"; + } else { + ss << "DEFAULT"; + } + ss << " VALUES "; + if(columnNamesCount) { + ss << streaming_values_placeholders(columnNamesCount, valuesCount); + } else if(valuesCount != 1) { + throw std::system_error{orm_error_code::cannot_use_default_value}; + } + return ss.str(); + } + }; + + template + std::string serialize_get_all_impl(const T& get, const Ctx& context) { + using primary_type = type_t; + + auto& table = pick_table(context.db_objects); + auto tableNames = collect_table_names(table, context); + + std::stringstream ss; + ss << "SELECT " << streaming_table_column_names(table, true) << " FROM " + << streaming_identifiers(tableNames) << streaming_conditions_tuple(get.conditions, context); + return ss.str(); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializer, void> { + using statement_type = get_all_optional_t; + + template + std::string operator()(const statement_type& get, const Ctx& context) const { + return serialize_get_all_impl(get, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct statement_serializer, void> { + using statement_type = get_all_pointer_t; + + template + std::string operator()(const statement_type& get, const Ctx& context) const { + return serialize_get_all_impl(get, context); + } + }; + + template + struct statement_serializer, void> { + using statement_type = get_all_t; + + template + std::string operator()(const statement_type& get, const Ctx& context) const { + return serialize_get_all_impl(get, context); + } + }; + + template + std::string serialize_get_impl(const T&, const Ctx& context) { + using primary_type = type_t; + auto& table = pick_table(context.db_objects); + std::stringstream ss; + ss << "SELECT " << streaming_table_column_names(table, false) << " FROM " + << streaming_identifier(table.name) << " WHERE "; + + auto primaryKeyColumnNames = table.primary_key_column_names(); + if(primaryKeyColumnNames.empty()) { + throw std::system_error{orm_error_code::table_has_no_primary_key_column}; + } + + for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { + if(i > 0) { + ss << " AND "; + } + ss << streaming_identifier(primaryKeyColumnNames[i]) << " = ?"; + } + return ss.str(); + } + + template + struct statement_serializer, void> { + using statement_type = get_t; + + template + std::string operator()(const statement_type& get, const Ctx& context) const { + return serialize_get_impl(get, context); + } + }; + + template + struct statement_serializer, void> { + using statement_type = get_pointer_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + return serialize_get_impl(statement, context); + } + }; + + template<> + struct statement_serializer { + using statement_type = conflict_action; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + switch(statement) { + case conflict_action::replace: + return "REPLACE"; + case conflict_action::abort: + return "ABORT"; + case conflict_action::fail: + return "FAIL"; + case conflict_action::ignore: + return "IGNORE"; + case conflict_action::rollback: + return "ROLLBACK"; + } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = insert_constraint; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + return "OR " + serialize(statement.action, context); + } + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializer, void> { + using statement_type = get_optional_t; + + template + std::string operator()(const statement_type& get, const Ctx& context) const { + return serialize_get_impl(get, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializer, void> { + using statement_type = select_t; + + template + std::string operator()(const statement_type& sel, Ctx context) const { + context.skip_table_name = false; + + std::stringstream ss; + if(!is_compound_operator_v) { + if(!sel.highest_level && context.use_parentheses) { + ss << "("; + } + ss << "SELECT "; + } + if(get_distinct(sel.col)) { + ss << static_cast(distinct(0)) << " "; + } + ss << streaming_serialized(get_column_names(sel.col, context)); + using conditions_tuple = typename statement_type::conditions_type; + constexpr bool hasExplicitFrom = tuple_has::value; + if(!hasExplicitFrom) { + auto tableNames = collect_table_names(sel, context); + using joins_index_sequence = filter_tuple_sequence_t; + // deduplicate table names of constrained join statements + iterate_tuple(sel.conditions, joins_index_sequence{}, [&tableNames, &context](auto& join) { + using original_join_type = typename std::decay_t::type; + using cross_join_type = mapped_type_proxy_t; + std::pair tableNameWithAlias{ + lookup_table_name(context.db_objects), + alias_extractor::as_alias()}; + tableNames.erase(tableNameWithAlias); + }); + if(!tableNames.empty() && !is_compound_operator_v) { + ss << " FROM " << streaming_identifiers(tableNames); + } + } + ss << streaming_conditions_tuple(sel.conditions, context); + if(!is_compound_operator_v) { + if(!sel.highest_level && context.use_parentheses) { + ss << ")"; + } + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = indexed_column_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << serialize(statement.column_or_expression, context); + if(!statement._collation_name.empty()) { + ss << " COLLATE " << statement._collation_name; + } + if(statement._order) { + switch(statement._order) { + case -1: + ss << " DESC"; + break; + case 1: + ss << " ASC"; + break; + default: + throw std::system_error{orm_error_code::incorrect_order}; + } + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = index_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "CREATE "; + if(statement.unique) { + ss << "UNIQUE "; + } + using indexed_type = typename std::decay_t::table_mapped_type; + ss << "INDEX IF NOT EXISTS " << streaming_identifier(statement.name) << " ON " + << streaming_identifier(lookup_table_name(context.db_objects)); + std::vector columnNames; + std::string whereString; + iterate_tuple(statement.elements, [&columnNames, &context, &whereString](auto& value) { + using value_type = std::decay_t; + if(!is_where_v) { + auto newContext = context; + newContext.use_parentheses = false; + auto whereString = serialize(value, newContext); + columnNames.push_back(std::move(whereString)); + } else { + auto columnName = serialize(value, context); + whereString = std::move(columnName); + } + }); + ss << " (" << streaming_serialized(columnNames) << ")"; + if(!whereString.empty()) { + ss << ' ' << whereString; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = from_t; + + template + std::string operator()(const statement_type&, const Ctx& context) const { + using tuple = std::tuple; + + std::stringstream ss; + ss << "FROM "; + iterate_tuple([&context, &ss, first = true](auto* item) mutable { + using from_type = std::remove_pointer_t; + + constexpr std::array sep = {", ", ""}; + ss << sep[std::exchange(first, false)] + << streaming_identifier(lookup_table_name>(context.db_objects), + alias_extractor::as_alias()); + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = old_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "OLD."; + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(statement.expression, newContext); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = new_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "NEW."; + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(statement.expression, newContext); + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = raise_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + switch(statement.type) { + case raise_t::type_t::ignore: + return "RAISE(IGNORE)"; + + case raise_t::type_t::rollback: + return "RAISE(ROLLBACK, " + serialize(statement.message, context) + ")"; + + case raise_t::type_t::abort: + return "RAISE(ABORT, " + serialize(statement.message, context) + ")"; + + case raise_t::type_t::fail: + return "RAISE(FAIL, " + serialize(statement.message, context) + ")"; + } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = trigger_timing; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + switch(statement) { + case trigger_timing::trigger_before: + return "BEFORE"; + case trigger_timing::trigger_after: + return "AFTER"; + case trigger_timing::trigger_instead_of: + return "INSTEAD OF"; + } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = trigger_type; + + template + std::string operator()(const statement_type& statement, const Ctx&) const { + switch(statement) { + case trigger_type::trigger_delete: + return "DELETE"; + case trigger_type::trigger_insert: + return "INSERT"; + case trigger_type::trigger_update: + return "UPDATE"; + } + return {}; + } + }; + + template<> + struct statement_serializer { + using statement_type = trigger_type_base_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << serialize(statement.timing, context) << " " << serialize(statement.type, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = trigger_update_type_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << serialize(statement.timing, context) << " UPDATE OF " + << streaming_mapped_columns_expressions(statement.columns, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = trigger_base_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << serialize(statement.type_base, context); + ss << " ON " << streaming_identifier(lookup_table_name(context.db_objects)); + if(statement.do_for_each_row) { + ss << " FOR EACH ROW"; + } + statement.container_when.apply([&ss, &context](auto& value) { + ss << " WHEN " << serialize(value, context); + }); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = trigger_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "CREATE "; + + ss << "TRIGGER IF NOT EXISTS " << streaming_identifier(statement.name) << " " + << serialize(statement.base, context); + ss << " BEGIN "; + iterate_tuple(statement.elements, [&ss, &context](auto& element) { + using element_type = std::decay_t; + if(is_select_v) { + auto newContext = context; + newContext.use_parentheses = false; + ss << serialize(element, newContext); + } else { + ss << serialize(element, context); + } + ss << ";"; + }); + ss << " END"; + + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = where_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << statement.serialize() << " "; + auto whereString = serialize(statement.expression, context); + ss << '(' << whereString << ')'; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(orderBy) << " "; + ss << serialize_order_by(orderBy, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = dynamic_order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { + return serialize_order_by(orderBy, context); + } + }; + + template + struct statement_serializer, void> { + using statement_type = multi_order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(orderBy) << " " << streaming_expressions_tuple(orderBy.args, context); + return ss.str(); + } + }; + + template + struct statement_serializer< + Join, + std::enable_if_t, + polyfill::is_specialization_of>>> { + using statement_type = Join; + + template + std::string operator()(const statement_type& join, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(join) << " " + << streaming_identifier(lookup_table_name>(context.db_objects)); + return ss.str(); + } + }; + + template + struct statement_serializer> { + using statement_type = Join; + + template + std::string operator()(const statement_type& join, const Ctx& context) const { + std::stringstream ss; + ss << static_cast(join) << " " + << streaming_identifier(lookup_table_name>>(context.db_objects), + alias_extractor>::as_alias()) + << " " << serialize(join.constraint, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = on_t; + + template + std::string operator()(const statement_type& on, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << static_cast(on) << " " << serialize(on.arg, newContext) << " "; + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = group_by_with_having; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << "GROUP BY " << streaming_expressions_tuple(statement.args, newContext) << " HAVING " + << serialize(statement.expression, context); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = group_by_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << "GROUP BY " << streaming_expressions_tuple(statement.args, newContext); + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = having_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << "HAVING " << serialize(statement.expression, newContext); + return ss.str(); + } + }; + + /** + * HO - has offset + * OI - offset is implicit + */ + template + struct statement_serializer, void> { + using statement_type = limit_t; + + template + std::string operator()(const statement_type& limt, const Ctx& 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) { + ss << serialize(value, newContext); + }); + ss << ", "; + ss << serialize(limt.lim, newContext); + } else { + ss << serialize(limt.lim, newContext) << " OFFSET "; + limt.off.apply([&newContext, &ss](auto& value) { + ss << serialize(value, newContext); + }); + } + } else { + ss << serialize(limt.lim, newContext); + } + return ss.str(); + } + }; + + template<> + struct statement_serializer { + using statement_type = default_values_t; + + template + std::string operator()(const statement_type&, const Ctx&) const { + return "DEFAULT VALUES"; + } + }; + + template + struct statement_serializer, void> { + using statement_type = using_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + auto newContext = context; + newContext.skip_table_name = true; + return static_cast(statement) + " (" + serialize(statement.column, newContext) + ")"; + } + }; + + template + struct statement_serializer, void> { + using statement_type = std::tuple; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << streaming_expressions_tuple(statement, context); + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = values_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << "VALUES "; + { + Ctx tupleContext = context; + tupleContext.use_parentheses = true; + ss << streaming_expressions_tuple(statement.tuple, tupleContext); + } + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializer, void> { + using statement_type = dynamic_values_t; + + template + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << "VALUES " << streaming_dynamic_expressions(statement.vector, context); + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + } +} + +// #include "triggers.h" + +// #include "object_from_column_builder.h" + +// #include "table.h" + +// #include "column.h" + +// #include "index.h" + +// #include "util.h" + +// #include "serializing_util.h" + +namespace sqlite_orm { + + namespace internal { + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_preparable_v = false; + + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_preparable_v().prepare(std::declval()))>> = true; + + /** + * Storage class itself. Create an instanse to use it as an interfacto to sqlite db by calling `make_storage` + * function. + */ + template + struct storage_t : storage_base { + using self = storage_t; + using db_objects_type = db_objects_tuple; + + /** + * @param filename database filename. + * @param dbObjects db_objects_tuple + */ + storage_t(std::string filename, db_objects_type dbObjects) : + storage_base{std::move(filename), foreign_keys_count(dbObjects)}, db_objects{std::move(dbObjects)} {} + + private: + db_objects_type db_objects; + + /** + * Obtain a storage_t's const db_objects_tuple. + * + * @note Historically, `serializer_context_builder` was declared friend, along with + * a few other library stock objects, in order to limit access to the db_objects_tuple. + * However, one could gain access to a storage_t's db_objects_tuple through + * `serializer_context_builder`, hence leading the whole friend declaration mambo-jumbo + * ad absurdum. + * Providing a free function is way better and cleaner. + * + * Hence, friend was replaced by `obtain_db_objects()` and `pick_const_impl()`. + */ + friend const db_objects_type& obtain_db_objects(const self& storage) noexcept { + return storage.db_objects; + } + + template + void create_table(sqlite3* db, const std::string& tableName, const Table& table) { + using table_type = std::decay_t; + using context_t = serializer_context; + + std::stringstream ss; + context_t context{this->db_objects}; + ss << "CREATE TABLE " << streaming_identifier(tableName) << " ( " + << streaming_expressions_tuple(table.elements, context) << ")"; + if(table_type::is_without_rowid_v) { + ss << " WITHOUT ROWID"; + } + ss.flush(); + perform_void_exec(db, ss.str()); + } + + /** + * Copies sourceTableName to another table with name: destinationTableName + * Performs INSERT INTO %destinationTableName% () SELECT %table.column_names% FROM %sourceTableName% + */ + template + void copy_table(sqlite3* db, + const std::string& sourceTableName, + const std::string& destinationTableName, + const Table& table, + const std::vector& columnsToIgnore) const; + +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + void drop_column(sqlite3* db, const std::string& tableName, const std::string& columnName) { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " DROP COLUMN " + << streaming_identifier(columnName) << std::flush; + perform_void_exec(db, ss.str()); + } +#endif + + template + void drop_create_with_loss(sqlite3* db, const Table& table) { + // eliminated all transaction handling + this->drop_table_internal(db, table.name); + this->create_table(db, table.name, table); + } + + template + void backup_table(sqlite3* db, const Table& table, 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.. + auto backupTableName = table.name + "_backup"; + if(this->table_exists(db, backupTableName)) { + int suffix = 1; + do { + std::stringstream ss; + ss << suffix << std::flush; + auto anotherBackupTableName = backupTableName + ss.str(); + if(!this->table_exists(db, anotherBackupTableName)) { + backupTableName = std::move(anotherBackupTableName); + break; + } + ++suffix; + } while(true); + } + this->create_table(db, backupTableName, table); + + this->copy_table(db, table.name, backupTableName, table, columnsToIgnore); + + this->drop_table_internal(db, table.name); + + this->rename_table(db, backupTableName, table.name); + } + + template + void assert_mapped_type() const { + using mapped_types_tuple = std::tuple; + static_assert(mpl::invoke_t, mapped_types_tuple>::value, + "type is not mapped to a storage"); + } + + template, + std::enable_if_t = true> + void assert_insertable_type() const {} + + template, + std::enable_if_t = true> + void assert_insertable_type() const { + using elements_type = elements_type_t; + using pkcol_index_sequence = col_index_sequence_with; + static_assert( + count_filtered_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_filtered_tuple::template fn, + pkcol_index_sequence>::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."); + } + + template + auto& get_table() const { + return pick_table(this->db_objects); + } + + template + auto& get_table() { + return pick_table(this->db_objects); + } + + public: + template + view_t iterate(Args&&... args) { + this->assert_mapped_type(); + + auto con = this->get_connection(); + return {*this, std::move(con), std::forward(args)...}; + } + + /** + * Delete from routine. + * O is an object's type. Must be specified explicitly. + * @param args optional conditions: `where`, `join` etc + * @example: storage.remove_all(); - DELETE FROM users + * @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) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::remove_all(std::forward(args)...)); + this->execute(statement); + } + + /** + * Delete routine. + * O is an object's type. Must be specified explicitly. + * @param ids ids of object to be removed. + */ + template + void remove(Ids... ids) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::remove(std::forward(ids)...)); + this->execute(statement); + } + + /** + * Update routine. Sets all non primary key fields where primary key is equal. + * O is an object type. May be not specified explicitly cause it can be deduced by + * compiler from first parameter. + * @param o object to be updated. + */ + template + void update(const O& o) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::update(std::ref(o))); + this->execute(statement); + } + + template + void update_all(S set, Wargs... wh) { + static_assert(internal::is_set::value, + "first argument in update_all can be either set or dynamic_set"); + auto statement = this->prepare(sqlite_orm::update_all(std::move(set), std::forward(wh)...)); + this->execute(statement); + } + + protected: + template + std::string group_concat_internal(F O::*m, std::unique_ptr y, Args&&... args) { + this->assert_mapped_type(); + std::vector rows; + if(y) { + rows = this->select(sqlite_orm::group_concat(m, std::move(*y)), std::forward(args)...); + } else { + rows = this->select(sqlite_orm::group_concat(m), std::forward(args)...); + } + if(!rows.empty()) { + return std::move(rows.front()); + } else { + return {}; + } + } + + public: + /** + * SELECT * routine. + * O is an object type to be extracted. Must be specified explicitly. + * @return All objects of type O stored in database at the moment in `std::vector`. + * @note If you need to return the result in a different container type then use a different `get_all` function overload `get_all>` + * @example: storage.get_all() - SELECT * FROM users + * @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) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_all(std::forward(args)...)); + return this->execute(statement); + } + + /** + * SELECT * routine. + * O is an object type to be extracted. Must be specified explicitly. + * R is an explicit return type. This type must have `push_back(O &&)` function. + * @return All objects of type O stored in database at the moment in `R`. + * @example: storage.get_all>(); - SELECT * FROM users + * @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) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_all(std::forward(args)...)); + return this->execute(statement); + } + + /** + * SELECT * routine. + * O is an object type to be extracted. Must be specified explicitly. + * @return All objects of type O as `std::unique_ptr` inside a `std::vector` stored in database at the moment. + * @note If you need to return the result in a different container type then use a different `get_all_pointer` function overload `get_all_pointer>` + * @example: storage.get_all_pointer(); - SELECT * FROM users + * @example: storage.get_all_pointer(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 + */ + template + 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); + } + + /** + * SELECT * routine. + * O is an object type to be extracted. Must be specified explicitly. + * R is a container type. std::vector> is default + * @return All objects of type O as std::unique_ptr stored in database at the moment. + * @example: storage.get_all_pointer>(); - SELECT * FROM users + * @example: storage.get_all_pointer>(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 + */ + template + 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); + } + + /** + * Select * by id routine. + * throws std::system_error{orm_error_code::not_found} if object not found with given + * id. throws std::system_error with orm_error_category in case of db error. O is an object type to be + * extracted. Must be specified explicitly. + * @return Object of type O where id is equal parameter passed or throws + * `std::system_error{orm_error_code::not_found}` if there is no object with such id. + */ + template + O get(Ids... ids) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get(std::forward(ids)...)); + return this->execute(statement); + } + + /** + * The same as `get` function but doesn't throw an exception if noting found but returns std::unique_ptr + * with null value. throws std::system_error in case of db error. + */ + template + std::unique_ptr get_pointer(Ids... ids) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_pointer(std::forward(ids)...)); + return this->execute(statement); + } + + /** + * A previous version of get_pointer() that returns a shared_ptr + * instead of a unique_ptr. New code should prefer get_pointer() + * unless the data needs to be shared. + * + * @note + * Most scenarios don't need shared ownership of data, so we should prefer + * unique_ptr when possible. It's more efficient, doesn't require atomic + * ops for a reference count (which can cause major slowdowns on + * weakly-ordered platforms like ARM), and can be easily promoted to a + * shared_ptr, exactly like we're doing here. + * (Conversely, you _can't_ go from shared back to unique.) + */ + template + std::shared_ptr get_no_throw(Ids... ids) { + return std::shared_ptr(this->get_pointer(std::forward(ids)...)); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * The same as `get` function but doesn't throw an exception if noting found but + * returns an empty std::optional. throws std::system_error in case of db error. + */ + template + std::optional get_optional(Ids... ids) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_optional(std::forward(ids)...)); + return this->execute(statement); + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + /** + * SELECT COUNT(*) https://www.sqlite.org/lang_aggfunc.html#count + * @return Number of O object in table. + */ + template> + int count(Args&&... args) { + this->assert_mapped_type(); + auto rows = this->select(sqlite_orm::count(), std::forward(args)...); + if(!rows.empty()) { + return rows.front(); + } else { + return 0; + } + } + + /** + * 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) { + this->assert_mapped_type(); + auto rows = this->select(sqlite_orm::count(m), std::forward(args)...); + if(!rows.empty()) { + return rows.front(); + } else { + return 0; + } + } + + /** + * 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 database. + */ + template + 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()) { + return rows.front(); + } else { + return 0; + } + } + + template + std::string group_concat(F O::*m) { + return this->group_concat_internal(m, {}); + } + + /** + * GROUP_CONCAT(X) query. https://www.sqlite.org/lang_aggfunc.html#groupconcat + * @param m is a class member pointer (the same you passed into make_column). + * @return group_concat query result. + */ + template, + std::enable_if_t::value >= 1, bool> = true> + std::string group_concat(F O::*m, Args&&... args) { + return this->group_concat_internal(m, {}, std::forward(args)...); + } + + /** + * GROUP_CONCAT(X, Y) query. https://www.sqlite.org/lang_aggfunc.html#groupconcat + * @param m is a class member pointer (the same you passed into make_column). + * @return group_concat query result. + */ + template + std::string group_concat(F O::*m, std::string y, Args&&... args) { + return this->group_concat_internal(m, + std::make_unique(std::move(y)), + std::forward(args)...); + } + + template + std::string group_concat(F O::*m, const char* y, Args&&... args) { + std::unique_ptr str; + if(y) { + str = std::make_unique(y); + } else { + str = std::make_unique(); + } + return this->group_concat_internal(m, std::move(str), std::forward(args)...); + } + + /** + * MAX(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return std::unique_ptr with max value or null if sqlite engine returned null. + */ + template> + 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()) { + return std::move(rows.front()); + } else { + return {}; + } + } + + /** + * MIN(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return std::unique_ptr with min value or null if sqlite engine returned null. + */ + template> + 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()) { + return std::move(rows.front()); + } else { + return {}; + } + } + + /** + * SUM(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return std::unique_ptr with sum value or null if sqlite engine returned null. + */ + template> + 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)...); + if(!rows.empty()) { + if(rows.front()) { + return std::make_unique(std::move(*rows.front())); + } else { + return {}; + } + } else { + return {}; + } + } + + /** + * TOTAL(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return total value (the same as SUM but not nullable. More details here + * https://www.sqlite.org/lang_aggfunc.html) + */ + template + 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()) { + return std::move(rows.front()); + } else { + return {}; + } + } + + /** + * Select a single column into std::vector or multiple columns into std::vector>. + * For a single column use `auto rows = storage.select(&User::id, where(...)); + * For multicolumns use `auto rows = storage.select(columns(&User::id, &User::name), where(...)); + */ + template> + std::vector select(T m, Args... args) { + static_assert(!is_compound_operator_v || sizeof...(Args) == 0, + "Cannot use args with a compound operator"); + auto statement = this->prepare(sqlite_orm::select(std::move(m), std::forward(args)...)); + return this->execute(statement); + } + + template = true> + std::string dump(const T& preparedStatement, bool parametrized = true) const { + return this->dump(preparedStatement.expression, parametrized); + } + + template, + std::enable_if_t && !is_mapped_v, bool> = true> + std::string dump(E&& expression, bool parametrized = false) const { + static_assert(is_preparable_v, "Expression must be a high-level statement"); + + decltype(auto) e2 = static_if>( + [](auto expression) -> auto{ + expression.highest_level = true; + return expression; + }, + [](const auto& expression) -> decltype(auto) { + return (expression); + })(std::forward(expression)); + using context_t = serializer_context; + context_t context{this->db_objects}; + context.replace_bindable_with_question = parametrized; + // just like prepare_impl() + context.skip_table_name = false; + return serialize(e2, context); + } + + /** + * Returns a string representation of object of a class mapped to the storage. + * Type of string has json-like style. + */ + template = true> + std::string dump(const O& object) const { + auto& table = this->get_table(); + std::stringstream ss; + ss << "{ "; + table.for_each_column([&ss, &object, first = true](auto& column) mutable { + using column_type = std::decay_t; + using field_type = typename column_type::field_type; + constexpr std::array sep = {", ", ""}; + + ss << sep[std::exchange(first, false)] << column.name << " : '" + << field_printer{}(polyfill::invoke(column.member_pointer, object)) << "'"; + }); + ss << " }"; + return ss.str(); + } + + /** + * This is REPLACE (INSERT OR REPLACE) function. + * Also if you need to insert value with knows id you should + * also you this function instead of insert cause inserts ignores + * id and creates own one. + */ + template + void replace(const O& o) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::replace(std::ref(o))); + this->execute(statement); + } + + template + void replace_range(It from, It to, Projection project = {}) { + using O = std::decay_t(), *std::declval()))>; + this->assert_mapped_type(); + if(from == to) { + return; + } + + auto statement = + this->prepare(sqlite_orm::replace_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); + } + + template + void replace_range(It from, It to, Projection project = {}) { + this->assert_mapped_type(); + if(from == to) { + return; + } + + auto statement = + this->prepare(sqlite_orm::replace_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); + } + + template + int insert(const O& o, columns_t cols) { + static_assert(cols.count > 0, "Use insert or replace with 1 argument instead"); + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::insert(std::ref(o), std::move(cols))); + return int(this->execute(statement)); + } + + /** + * Insert routine. Inserts object with all non primary key fields in passed object. Id of passed + * object doesn't matter. + * @return id of just created object. + */ + template + int insert(const O& o) { + this->assert_mapped_type(); + this->assert_insertable_type(); + 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, Projection project = {}) { + using O = std::decay_t(), *std::declval()))>; + this->assert_mapped_type(); + this->assert_insertable_type(); + if(from == to) { + return; + } + auto statement = + this->prepare(sqlite_orm::insert_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); + } + + template + void insert_range(It from, It to, Projection project = {}) { + this->assert_mapped_type(); + this->assert_insertable_type(); + if(from == to) { + return; + } + auto statement = + this->prepare(sqlite_orm::insert_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); + } + + /** + * Change table name inside storage's schema info. This function does not + * affect database + */ + template + void rename_table(std::string name) { + this->assert_mapped_type(); + auto& table = this->get_table(); + table.name = std::move(name); + } + + using storage_base::rename_table; + + /** + * Get table's name stored in storage's schema info. This function does not call + * any SQLite queries + */ + template + const std::string& tablename() const { + this->assert_mapped_type(); + auto& table = this->get_table(); + return table.name; + } + + template + [[deprecated("Use the more accurately named function `find_column_name()`")]] const std::string* + column_name(F O::*memberPointer) const { + return internal::find_column_name(this->db_objects, memberPointer); + } + + template + const std::string* find_column_name(F O::*memberPointer) const { + return internal::find_column_name(this->db_objects, memberPointer); + } + + protected: + template + sync_schema_result schema_status(const index_t&, sqlite3*, bool, bool*) { + return sync_schema_result::already_in_sync; + } + + template + sync_schema_result schema_status(const table_t& table, + sqlite3* db, + bool preserve, + bool* attempt_to_preserve) { + if(attempt_to_preserve) { + *attempt_to_preserve = true; + } + + auto dbTableInfo = this->pragma.table_xinfo(table.name); + auto res = sync_schema_result::already_in_sync; + + // first let's see if table with such name exists.. + auto gottaCreateTable = !this->table_exists(db, table.name); + if(!gottaCreateTable) { + + // get table info provided in `make_table` call.. + auto storageTableInfo = table.get_table_info(); + + // this vector will contain pointers to columns that gotta be added.. + std::vector columnsToAdd; + + if(calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { + gottaCreateTable = true; + } + + if(!gottaCreateTable) { // if all storage columns are equal to actual db columns but there are + // excess columns at the db.. + if(!dbTableInfo.empty()) { + // extra table columns than storage columns + if(!preserve) { +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + res = sync_schema_result::old_columns_removed; +#else + gottaCreateTable = true; +#endif + } else { + res = sync_schema_result::old_columns_removed; + } + } + } + if(gottaCreateTable) { + res = sync_schema_result::dropped_and_recreated; + } else { + if(!columnsToAdd.empty()) { + // extra storage columns than table columns + for(const table_xinfo* colInfo: columnsToAdd) { + const basic_generated_always::storage_type* generatedStorageType = + table.find_column_generated_storage_type(colInfo->name); + if(generatedStorageType) { + if(*generatedStorageType == basic_generated_always::storage_type::stored) { + gottaCreateTable = true; + break; + } + // fallback cause VIRTUAL can be added + } else { + if(colInfo->notnull && colInfo->dflt_value.empty()) { + gottaCreateTable = true; + // no matter if preserve is true or false, there is no way to preserve data, so we wont try! + if(attempt_to_preserve) { + *attempt_to_preserve = false; + }; + break; + } + } + } + if(!gottaCreateTable) { + if(res == sync_schema_result::old_columns_removed) { + res = sync_schema_result::new_columns_added_and_old_columns_removed; + } else { + res = sync_schema_result::new_columns_added; + } + } else { + res = sync_schema_result::dropped_and_recreated; + } + } else { + if(res != sync_schema_result::old_columns_removed) { + res = sync_schema_result::already_in_sync; + } + } + } + } else { + res = sync_schema_result::new_table_created; + } + return res; + } + + template + sync_schema_result sync_table(const index_t& index, sqlite3* db, bool) { + auto res = sync_schema_result::already_in_sync; + using context_t = serializer_context; + context_t context{this->db_objects}; + auto query = serialize(index, context); + perform_void_exec(db, query); + return res; + } + + template + sync_schema_result sync_table(const trigger_t& trigger, sqlite3* db, bool) { + auto res = sync_schema_result::already_in_sync; // TODO Change accordingly + using context_t = serializer_context; + context_t context{this->db_objects}; + perform_void_exec(db, serialize(trigger, context)); + return res; + } + + template = true> + sync_schema_result sync_table(const Table& table, sqlite3* db, bool preserve); + + template + void add_column(sqlite3* db, const std::string& tableName, const C& column) const { + using context_t = serializer_context; + + context_t context{this->db_objects}; + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " ADD COLUMN " << serialize(column, context) + << std::flush; + perform_void_exec(db, ss.str()); + } + + template + prepared_statement_t prepare_impl(S statement) { + using context_t = serializer_context; + context_t context{this->db_objects}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + + auto con = this->get_connection(); + sqlite3_stmt* stmt = prepare_stmt(con.get(), serialize(statement, context)); + return prepared_statement_t{std::forward(statement), stmt, con}; + } + + public: + /** + * This is a cute function used to replace migration up/down functionality. + * It performs check storage schema with actual db schema and: + * * if there are excess tables exist in db they are ignored (not dropped) + * * every table from storage is compared with it's db analog and + * * if table doesn't exist it is being created + * * if table exists its colums are being compared with table_info from db and + * * if there are columns in db that do not exist in storage (excess) table will be dropped and + * recreated + * * if there are columns in storage that do not exist in db they will be added using `ALTER TABLE + * ... ADD COLUMN ...' command + * * if there is any column existing in both db and storage but differs by any of + * properties/constraints (pk, notnull, dflt_value) table will be dropped and recreated. Be aware that + * `sync_schema` doesn't guarantee that data will not be dropped. It guarantees only that it will make db + * schema the same as you specified in `make_storage` function call. A good point is that if you have no db + * file at all it will be created and all tables also will be created with exact tables and columns you + * specified in `make_storage`, `make_table` and `make_column` calls. The best practice is to call this + * function right after storage creation. + * @param preserve affects function's behaviour in case it is needed to remove a column. If it is `false` + * so table will be dropped if there is column to remove if SQLite version is < 3.35.0 and remove column if SQLite version >= 3.35.0, + * if `true` - table is being copied into another table, dropped and copied table is renamed with source table name. + * Warning: sync_schema doesn't check foreign keys cause it is unable to do so in sqlite3. If you know how to get foreign key info please + * submit an issue https://github.com/fnc12/sqlite_orm/issues + * @return std::map with std::string key equal table name and `sync_schema_result` as value. + * `sync_schema_result` is a enum value that stores table state after syncing a schema. `sync_schema_result` + * can be printed out on std::ostream with `operator<<`. + */ + std::map sync_schema(bool preserve = false) { + auto con = this->get_connection(); + std::map result; + iterate_tuple(this->db_objects, [this, db = con.get(), preserve, &result](auto& schemaObject) { + sync_schema_result status = this->sync_table(schemaObject, db, preserve); + result.emplace(schemaObject.name, status); + }); + return result; + } + + /** + * This function returns the same map that `sync_schema` returns but it + * doesn't perform `sync_schema` actually - just simulates it in case you want to know + * what will happen if you sync your schema. + */ + std::map sync_schema_simulate(bool preserve = false) { + auto con = this->get_connection(); + std::map result; + iterate_tuple(this->db_objects, [this, db = con.get(), preserve, &result](auto& schemaObject) { + sync_schema_result status = this->schema_status(schemaObject, db, preserve, nullptr); + result.emplace(schemaObject.name, status); + }); + return result; + } + + using storage_base::table_exists; // now that it is in storage_base make it into overload set + + template + prepared_statement_t> prepare(select_t sel) { + sel.highest_level = true; + return prepare_impl>(std::move(sel)); + } + + template + prepared_statement_t> prepare(get_all_t get_) { + return prepare_impl>(std::move(get_)); + } + + template + prepared_statement_t> prepare(get_all_pointer_t get_) { + 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_) { + return prepare_impl>(std::move(get_)); + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + prepared_statement_t> prepare(update_all_t upd) { + return prepare_impl>(std::move(upd)); + } + + template + prepared_statement_t> prepare(remove_all_t rem) { + return prepare_impl>(std::move(rem)); + } + + template + prepared_statement_t> prepare(get_t get_) { + return prepare_impl>(std::move(get_)); + } + + template + prepared_statement_t> prepare(get_pointer_t get_) { + return prepare_impl>(std::move(get_)); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + prepared_statement_t> prepare(get_optional_t get_) { + return prepare_impl>(std::move(get_)); + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + prepared_statement_t> prepare(update_t upd) { + return prepare_impl>(std::move(upd)); + } + + template + prepared_statement_t> prepare(remove_t statement) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + return this->prepare_impl>(std::move(statement)); + } + + template + prepared_statement_t> prepare(insert_t statement) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + this->assert_insertable_type(); + return this->prepare_impl>(std::move(statement)); + } + + template + prepared_statement_t> prepare(replace_t rep) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + return this->prepare_impl>(std::move(rep)); + } + + 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 this->prepare_impl>(std::move(statement)); + } + + template + prepared_statement_t> prepare(replace_range_t statement) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + return this->prepare_impl>(std::move(statement)); + } + + template + prepared_statement_t> prepare(insert_explicit ins) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + return this->prepare_impl>(std::move(ins)); + } + + template + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.args, conditional_binder{statement.stmt}); + perform_step(stmt); + } + + template + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.args, conditional_binder{stmt}); + perform_step(stmt); + } + + template + int64 execute(const prepared_statement_t>& statement) { + using statement_type = std::decay_t; + using expression_type = typename statement_type::expression_type; + using object_type = typename expression_object_type::type; + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + tuple_value_binder{stmt}( + statement.expression.columns.columns, + [&table = this->get_table(), &object = statement.expression.obj](auto& memberPointer) { + return table.object_field_value(object, memberPointer); + }); + perform_step(stmt); + return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); + } + + template, is_replace_range>, bool> = true> + void execute(const prepared_statement_t& statement) { + using statement_type = std::decay_t; + using expression_type = typename statement_type::expression_type; + using object_type = typename expression_object_type::type; + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + auto processObject = [&table = this->get_table(), + bindValue = field_value_binder{stmt}](auto& object) mutable { + table.template for_each_column_excluding( + call_as_template_base([&bindValue, &object](auto& column) { + bindValue(polyfill::invoke(column.member_pointer, object)); + })); + }; + + static_if>( + [&processObject](auto& expression) { +#if __cpp_lib_ranges >= 201911L + std::ranges::for_each(expression.range.first, + expression.range.second, + std::ref(processObject), + std::ref(expression.transformer)); +#else + auto& transformer = expression.transformer; + std::for_each(expression.range.first, + expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); +#endif + }, + [&processObject](auto& expression) { + const object_type& o = get_object(expression); + processObject(o); + })(statement.expression); + + perform_step(stmt); + } + + template, is_insert_range>, bool> = true> + int64 execute(const prepared_statement_t& statement) { + using statement_type = std::decay_t; + using expression_type = typename statement_type::expression_type; + using object_type = typename expression_object_type::type; + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + auto processObject = [&table = this->get_table(), + bindValue = field_value_binder{stmt}](auto& object) mutable { + using is_without_rowid = typename std::decay_t::is_without_rowid; + table.template for_each_column_excluding< + mpl::conjunction>, + mpl::disjunction_fn>>( + call_as_template_base([&table, &bindValue, &object](auto& column) { + if(!table.exists_in_composite_primary_key(column)) { + bindValue(polyfill::invoke(column.member_pointer, object)); + } + })); + }; + + static_if>( + [&processObject](auto& expression) { +#if __cpp_lib_ranges >= 201911L + std::ranges::for_each(expression.range.first, + expression.range.second, + std::ref(processObject), + std::ref(expression.transformer)); +#else + auto& transformer = expression.transformer; + std::for_each(expression.range.first, + expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); +#endif + }, + [&processObject](auto& expression) { + const object_type& o = get_object(expression); + processObject(o); + })(statement.expression); + + perform_step(stmt); + return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); + } + + template + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + perform_step(stmt); + } + + template + void execute(const prepared_statement_t>& statement) { + using statement_type = std::decay_t; + using expression_type = typename statement_type::expression_type; + using object_type = typename expression_object_type::type; + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + auto& table = this->get_table(); + + field_value_binder bindValue{stmt}; + auto& object = get_object(statement.expression); + table.template for_each_column_excluding>( + call_as_template_base([&table, &bindValue, &object](auto& column) { + if(!table.exists_in_composite_primary_key(column)) { + bindValue(polyfill::invoke(column.member_pointer, object)); + } + })); + table.for_each_column([&table, &bindValue, &object](auto& column) { + if(column.template is() || table.exists_in_composite_primary_key(column)) { + bindValue(polyfill::invoke(column.member_pointer, object)); + } + }); + perform_step(stmt); + } + + template + std::unique_ptr execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + + std::unique_ptr res; + perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + res = std::make_unique(); + object_from_column_builder builder{*res, stmt}; + table.for_each_column(builder); + }); + return res; + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + std::optional execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + + std::optional res; + perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + object_from_column_builder builder{res.emplace(), stmt}; + table.for_each_column(builder); + }); + return res; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + T execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + std::optional res; + perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + object_from_column_builder builder{res.emplace(), stmt}; + table.for_each_column(builder); + }); + if(!res.has_value()) { + throw std::system_error{orm_error_code::not_found}; + } + return std::move(res).value(); +#else + auto& table = this->get_table(); + auto stepRes = sqlite3_step(stmt); + switch(stepRes) { + case SQLITE_ROW: { + T res; + object_from_column_builder builder{res, stmt}; + table.for_each_column(builder); + return res; + } break; + case SQLITE_DONE: { + throw std::system_error{orm_error_code::not_found}; + } break; + default: { + throw_translated_sqlite_error(stmt); + } + } +#endif + } + + template + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.conditions, conditional_binder{stmt}); + perform_step(stmt); + } + + template + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + conditional_binder bindNode{stmt}; + iterate_ast(statement.expression.set, bindNode); + iterate_ast(statement.expression.conditions, bindNode); + perform_step(stmt); + } + + template> + std::vector execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + + std::vector res; + perform_steps(stmt, + [rowExtractor = make_row_extractor(lookup_table(this->db_objects)), + &res](sqlite3_stmt* stmt) { + res.push_back(rowExtractor.extract(stmt, 0)); + }); + return res; + } + + template + R execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + + R res; + perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + T obj; + object_from_column_builder builder{obj, stmt}; + table.for_each_column(builder); + res.push_back(std::move(obj)); + }); + return res; + } + + template + R execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + + R res; + perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + auto obj = std::make_unique(); + object_from_column_builder builder{*obj, stmt}; + table.for_each_column(builder); + res.push_back(std::move(obj)); + }); + return res; + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + R execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + + R res; + perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + auto obj = std::make_optional(); + object_from_column_builder builder{*obj, stmt}; + table.for_each_column(builder); + res.push_back(std::move(obj)); + }); + return res; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + }; // struct storage_t + } + + /* + * Factory function for a storage, from a database file and a bunch of database object definitions. + */ + template + internal::storage_t make_storage(std::string filename, DBO... dbObjects) { + return {std::move(filename), internal::db_objects_tuple{std::forward(dbObjects)...}}; + } + + /** + * sqlite3_threadsafe() interface. + */ + inline int threadsafe() { + return sqlite3_threadsafe(); + } +} +#pragma once + +#include // std::is_same, std::decay, std::remove_reference +#include // std::get + +// #include "functional/cxx_universal.h" +// ::size_t +// #include "functional/static_magic.h" + +// #include "prepared_statement.h" + +// #include "ast_iterator.h" + +// #include "node_tuple.h" + +#include // std::enable_if +#include // std::tuple +#include // std::pair +#include // std::reference_wrapper +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "conditions.h" + +// #include "operators.h" + +// #include "select_constraints.h" + +// #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" + +// #include "ast/into.h" + +// #include "ast/group_by.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct node_tuple { + using type = std::tuple; + }; + + template + using node_tuple_t = typename node_tuple::type; + + template<> + struct node_tuple { + using type = std::tuple<>; + }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct node_tuple, void> : node_tuple {}; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple> {}; + + template + struct node_tuple, void> { + using args_tuple = node_tuple_t>; + using expression_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple> : node_tuple {}; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + /** + * Column alias + */ + template + struct node_tuple, void> : node_tuple {}; + + /** + * Column alias + */ + template + struct node_tuple, void> : node_tuple {}; + + /** + * Literal + */ + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple> { + using node_type = T; + using left_type = typename node_type::left_type; + using right_type = typename node_type::right_type; + using left_node_tuple = node_tuple_t; + using right_node_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using node_type = binary_operator; + using left_type = typename node_type::left_type; + using right_type = typename node_type::right_type; + using left_node_tuple = node_tuple_t; + using right_node_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using left_tuple = node_tuple_t; + using right_tuple = tuple_cat_t...>; + using type = tuple_cat_t; + }; + + template + struct node_tuple> { + using node_type = T; + using left_type = typename node_type::left_type; + using right_type = typename node_type::right_type; + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using columns_tuple = node_tuple_t; + using args_tuple = tuple_cat_t...>; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct node_tuple, Wargs...>, void> { + using set_tuple = tuple_cat_t...>; + using conditions_tuple = tuple_cat_t...>; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> { + using arg_tuple = node_tuple_t; + using pattern_tuple = node_tuple_t; + using escape_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using arg_tuple = node_tuple_t; + using pattern_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using expression_tuple = node_tuple_t; + using lower_tuple = node_tuple_t; + using upper_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> { + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + // note: not strictly necessary as there's no binding support for USING; + // we provide it nevertheless, in line with on_t. + template + struct node_tuple, void> : node_tuple> {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> { + using case_tuple = node_tuple_t; + using args_tuple = tuple_cat_t...>; + using else_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> { + using left_tuple = node_tuple_t; + using right_tuple = node_tuple_t; + using type = tuple_cat_t; + }; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> { + using type = tuple_cat_t, node_tuple_t>; + }; + + template + struct node_tuple, void> { + using type = tuple_cat_t, node_tuple_t>; + }; + } +} + +// #include "expression_object_type.h" + +namespace sqlite_orm { + + template + auto& get(internal::prepared_statement_t>& statement) { + return std::get(statement.expression.range); + } + + template + const auto& get(const internal::prepared_statement_t>& statement) { + return std::get(statement.expression.range); + } + + template + auto& get(internal::prepared_statement_t>& statement) { + return std::get(statement.expression.range); + } + + template + const auto& get(const internal::prepared_statement_t>& statement) { + return std::get(statement.expression.range); + } + + template + auto& get(internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } + + template + const auto& get(const internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } + + template + auto& get(internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } + + template + const auto& get(const internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + auto& get(internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } + + template + const auto& get(const internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + auto& get(internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } + + template + const auto& get(const internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } + + template + 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.expression.object); + } + + template + 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.expression.object); + } + + template + 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.expression.obj); + } + + template + 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.expression.obj); + } + + template + 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.expression.object); + } + + template + 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.expression.object); + } + + template + 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.expression.object); + } + + template + 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.expression.object); + } + + template + const auto& get(const internal::prepared_statement_t& statement) { + using statement_type = std::decay_t; + using expression_type = typename statement_type::expression_type; + using node_tuple = internal::node_tuple_t; + using bind_tuple = internal::bindable_filter_t; + using result_type = std::tuple_element_t(N), bind_tuple>; + const result_type* result = nullptr; + internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { + using node_type = std::decay_t; + if(internal::is_bindable_v) { + ++index; + } + if(index == N) { + internal::call_if_constexpr::value>( + [](auto& r, auto& n) { + r = &n; + }, + result, + node); + } + }); + return internal::get_ref(*result); + } + + template + auto& get(internal::prepared_statement_t& statement) { + using statement_type = std::decay_t; + using expression_type = typename statement_type::expression_type; + using node_tuple = internal::node_tuple_t; + using bind_tuple = internal::bindable_filter_t; + using result_type = std::tuple_element_t(N), bind_tuple>; + result_type* result = nullptr; + + internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { + using node_type = std::decay_t; + if(internal::is_bindable_v) { + ++index; + } + if(index == N) { + internal::call_if_constexpr::value>( + [](auto& r, auto& n) { + r = const_cast>(&n); + }, + result, + node); + } + }); + return internal::get_ref(*result); + } +} +#pragma once + +/* + * Note: This feature needs constexpr variables with external linkage. + * which can be achieved before C++17's inline variables, but differs from compiler to compiler. + * Hence we make it only available for compilers supporting inline variables. + */ + +#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +#include // std::integral_constant +#include // std::move + +// #include "functional/cxx_universal.h" + +// #include "pointer_value.h" + +namespace sqlite_orm { + + inline constexpr const char carray_pvt_name[] = "carray"; + using carray_pvt = std::integral_constant; + + template + using carray_pointer_arg = pointer_arg; + template + using carray_pointer_binding = pointer_binding; + template + using static_carray_pointer_binding = static_pointer_binding; + + /** + * Wrap a pointer of type 'carray' and its deleter function for binding it to a statement. + * + * Unless the deleter yields a nullptr 'xDestroy' function the ownership of the pointed-to-object + * is transferred to the pointer binding, which will delete it through + * the deleter when the statement finishes. + */ + template + auto bindable_carray_pointer(P* p, D d) noexcept -> pointer_binding { + return bindable_pointer(p, std::move(d)); + } + + /** + * Wrap a pointer of type 'carray' for binding it to a statement. + * + * Note: 'Static' means that ownership of the pointed-to-object won't be transferred + * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. + */ + template + auto statically_bindable_carray_pointer(P* p) noexcept -> static_pointer_binding { + return statically_bindable_pointer(p); + } + + /** + * Generalized form of the 'remember' SQL function that is a pass-through for values + * (it returns its argument unchanged using move semantics) but also saves the + * value that is passed through into a bound variable. + */ + template + struct note_value_fn { + P operator()(P&& value, carray_pointer_arg

pv) const { + if(P* observer = pv) { + *observer = value; + } + return std::move(value); + } + + static constexpr const char* name() { + return "note_value"; + } + }; + + /** + * remember(V, $PTR) extension function https://sqlite.org/src/file/ext/misc/remember.c + */ + struct remember_fn : note_value_fn { + static constexpr const char* name() { + return "remember"; + } + }; +} +#endif +#pragma once + +#include // std::string + +// #include "column.h" + +// #include "table.h" + +namespace sqlite_orm { +#ifdef SQLITE_ENABLE_DBSTAT_VTAB + struct dbstat { + std::string name; + std::string path; + int pageno = 0; + std::string pagetype; + int ncell = 0; + int payload = 0; + int unused = 0; + int mx_payload = 0; + int pgoffset = 0; + int pgsize = 0; + }; + + inline auto make_dbstat_table() { + return make_table("dbstat", + make_column("name", &dbstat::name), + make_column("path", &dbstat::path), + make_column("pageno", &dbstat::pageno), + make_column("pagetype", &dbstat::pagetype), + make_column("ncell", &dbstat::ncell), + make_column("payload", &dbstat::payload), + make_column("unused", &dbstat::unused), + make_column("mx_payload", &dbstat::mx_payload), + make_column("pgoffset", &dbstat::pgoffset), + make_column("pgsize", &dbstat::pgsize)); + } +#endif // SQLITE_ENABLE_DBSTAT_VTAB +} +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * this file is also used to provide definitions of interface methods 'hitting the database'. + */ +#pragma once + +// #include "implementations/column_definitions.h" +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * this file is also used to provide definitions of interface methods 'hitting the database'. + */ + +#include // std::make_unique + +// #include "../functional/cxx_core_features.h" + +// #include "../functional/static_magic.h" + +// #include "../functional/index_sequence_util.h" + +// #include "../tuple_helper/tuple_filter.h" + +// #include "../tuple_helper/tuple_traits.h" + +// #include "../default_value_extractor.h" + +// #include "../column.h" + +namespace sqlite_orm { + namespace internal { + + template + std::unique_ptr column_constraints::default_value() const { + using default_op_index_sequence = + filter_tuple_sequence_t::template fn>; + + std::unique_ptr value; + call_if_constexpr( + [&value](auto& constraints, auto op_index_sequence) { + using default_op_index_sequence = decltype(op_index_sequence); + constexpr size_t opIndex = first_index_sequence_value(default_op_index_sequence{}); + value = std::make_unique(serialize_default_value(get(constraints))); + }, + this->constraints, + default_op_index_sequence{}); + return value; + } + + } +} + +// #include "implementations/table_definitions.h" +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * this file is also used to provide definitions of interface methods 'hitting the database'. + */ + +#include // std::decay_t +#include // std::move +#include // std::find_if, std::ranges::find + +// #include "../type_printer.h" + +// #include "../column.h" + +// #include "../table.h" + +namespace sqlite_orm { + namespace internal { + + template + std::vector table_t::get_table_info() const { + std::vector res; + res.reserve(size_t(filter_tuple_sequence_t::size())); + this->for_each_column([&res](auto& column) { + using field_type = field_type_t>; + std::string dft; + if(auto d = column.default_value()) { + dft = std::move(*d); + } + res.emplace_back(-1, + column.name, + type_printer().print(), + column.is_not_null(), + dft, + column.template is(), + column.is_generated()); + }); + auto compositeKeyColumnNames = this->composite_key_columns_names(); + for(size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { + auto& columnName = compositeKeyColumnNames[i]; +#if __cpp_lib_ranges >= 201911L + auto it = std::ranges::find(res, columnName, &table_xinfo::name); +#else + auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_xinfo& ti) { + return ti.name == columnName; + }); +#endif + if(it != res.end()) { + it->pk = static_cast(i + 1); + } + } + return res; + } + + } +} + +// #include "implementations/storage_definitions.h" +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * this file is also used to separate implementation details from the main header file, + * e.g. usage of the dbstat table. + */ + +#include // std::is_same +#include +#include // std::reference_wrapper, std::cref +#include // std::find_if, std::ranges::find + +// #include "../dbstat.h" + +// #include "../type_traits.h" + +// #include "../util.h" + +// #include "../serializing_util.h" + +// #include "../storage.h" + +namespace sqlite_orm { + namespace internal { + + template + template> + sync_schema_result storage_t::sync_table(const Table& table, sqlite3* db, bool preserve) { +#ifdef SQLITE_ENABLE_DBSTAT_VTAB + if(std::is_same, dbstat>::value) { + return sync_schema_result::already_in_sync; + } +#endif // SQLITE_ENABLE_DBSTAT_VTAB + auto res = sync_schema_result::already_in_sync; + bool attempt_to_preserve = true; + + auto schema_stat = this->schema_status(table, db, preserve, &attempt_to_preserve); + if(schema_stat != sync_schema_result::already_in_sync) { + if(schema_stat == sync_schema_result::new_table_created) { + this->create_table(db, table.name, table); + res = sync_schema_result::new_table_created; + } else { + if(schema_stat == sync_schema_result::old_columns_removed || + schema_stat == sync_schema_result::new_columns_added || + schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { + + // get table info provided in `make_table` call.. + auto storageTableInfo = table.get_table_info(); + + // now get current table info from db using `PRAGMA table_xinfo` query.. + auto dbTableInfo = this->pragma.table_xinfo(table.name); // should include generated columns + + // this vector will contain pointers to columns that gotta be added.. + std::vector columnsToAdd; + + this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + + if(schema_stat == sync_schema_result::old_columns_removed) { +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + for(auto& tableInfo: dbTableInfo) { + this->drop_column(db, table.name, tableInfo.name); + } + res = sync_schema_result::old_columns_removed; +#else + // extra table columns than storage columns + this->backup_table(db, table, {}); + res = sync_schema_result::old_columns_removed; +#endif + } + + if(schema_stat == sync_schema_result::new_columns_added) { + for(const table_xinfo* colInfo: columnsToAdd) { + table.for_each_column([this, colInfo, &tableName = table.name, db](auto& column) { + if(column.name != colInfo->name) { + return; + } + this->add_column(db, tableName, column); + }); + } + res = sync_schema_result::new_columns_added; + } + + if(schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { + + auto storageTableInfo = table.get_table_info(); + this->add_generated_cols(columnsToAdd, storageTableInfo); + + // remove extra columns and generated columns + this->backup_table(db, table, columnsToAdd); + res = sync_schema_result::new_columns_added_and_old_columns_removed; + } + } else if(schema_stat == sync_schema_result::dropped_and_recreated) { + // now get current table info from db using `PRAGMA table_xinfo` query.. + auto dbTableInfo = this->pragma.table_xinfo(table.name); // should include generated columns + auto storageTableInfo = table.get_table_info(); + + // this vector will contain pointers to columns that gotta be added.. + std::vector columnsToAdd; + + this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + + this->add_generated_cols(columnsToAdd, storageTableInfo); + + if(preserve && attempt_to_preserve) { + this->backup_table(db, table, columnsToAdd); + } else { + this->drop_create_with_loss(db, table); + } + res = schema_stat; + } + } + } + return res; + } + + template + template + void storage_t::copy_table( + sqlite3* db, + const std::string& sourceTableName, + const std::string& destinationTableName, + const Table& table, + const std::vector& columnsToIgnore) const { // must ignore generated columns + std::vector> columnNames; + columnNames.reserve(table.count_columns_amount()); + table.for_each_column([&columnNames, &columnsToIgnore](const column_identifier& column) { + auto& columnName = column.name; +#if __cpp_lib_ranges >= 201911L + auto columnToIgnoreIt = std::ranges::find(columnsToIgnore, columnName, &table_xinfo::name); +#else + auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), + columnsToIgnore.end(), + [&columnName](const table_xinfo* tableInfo) { + return columnName == tableInfo->name; + }); +#endif + if(columnToIgnoreIt == columnsToIgnore.end()) { + columnNames.push_back(cref(columnName)); + } + }); + + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(destinationTableName) << " (" + << streaming_identifiers(columnNames) << ") " + << "SELECT " << streaming_identifiers(columnNames) << " FROM " << streaming_identifier(sourceTableName) + << std::flush; + perform_void_exec(db, ss.str()); + } + } +} + +#pragma once + +#if defined(_MSC_VER) +__pragma(pop_macro("max")) +__pragma(pop_macro("min")) +#endif // defined(_MSC_VER) diff --git a/libs/sqlite_orm-1.8.2/logo.png b/libs/sqlite_orm-1.8.2/logo.png new file mode 100644 index 0000000..985d7f6 Binary files /dev/null and b/libs/sqlite_orm-1.8.2/logo.png differ diff --git a/libs/sqlite_orm-1.8.2/not_single_header_include/sqlite_orm/sqlite_orm.h b/libs/sqlite_orm-1.8.2/not_single_header_include/sqlite_orm/sqlite_orm.h new file mode 100644 index 0000000..0ca2700 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/not_single_header_include/sqlite_orm/sqlite_orm.h @@ -0,0 +1,38 @@ +#pragma once + +#include "../../dev/functional/start_macros.h" +// though each header is required to include everything it needs +// we include the configuration and all underlying c++ core features in order to make it universally available +#include "../../dev/functional/config.h" +#include "../../dev/type_traits.h" +#include "../../dev/error_code.h" +#include "../../dev/type_printer.h" +#include "../../dev/collate_argument.h" +#include "../../dev/constraints.h" +#include "../../dev/type_is_nullable.h" +#include "../../dev/operators.h" +#include "../../dev/column.h" +#include "../../dev/field_printer.h" +#include "../../dev/conditions.h" +#include "../../dev/alias.h" +#include "../../dev/core_functions.h" +#include "../../dev/select_constraints.h" +#include "../../dev/table_info.h" +#include "../../dev/triggers.h" +#include "../../dev/statement_finalizer.h" +#include "../../dev/arithmetic_tag.h" +#include "../../dev/pointer_value.h" +#include "../../dev/statement_binder.h" +#include "../../dev/row_extractor.h" +#include "../../dev/sync_schema_result.h" +#include "../../dev/index.h" +#include "../../dev/rowid.h" +#include "../../dev/table.h" +#include "../../dev/storage_impl.h" +#include "../../dev/default_value_extractor.h" +#include "../../dev/storage.h" +#include "../../dev/get_prepared_statement.h" +#include "../../dev/carray.h" +#include "../../dev/dbstat.h" +#include "../../dev/interface_definitions.h" +#include "../../dev/functional/finish_macros.h" diff --git a/libs/sqlite_orm-1.8.2/packaging/CMakeLists.txt b/libs/sqlite_orm-1.8.2/packaging/CMakeLists.txt new file mode 100644 index 0000000..0451942 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/packaging/CMakeLists.txt @@ -0,0 +1,35 @@ +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +set(SQLITE_ORM_INSTALL_CMAKEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/SqliteOrm CACHE STRING "Path to SqliteOrm cmake files") + +configure_file(${PROJECT_SOURCE_DIR}/cmake/SqliteOrmConfig.cmake.in SqliteOrmConfig.cmake @ONLY) + +write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/SqliteOrmConfigVersion.cmake + VERSION ${sqlite_orm_VERSION} + COMPATIBILITY SameMajorVersion + ) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/SqliteOrmConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/SqliteOrmConfigVersion.cmake + DESTINATION ${SQLITE_ORM_INSTALL_CMAKEDIR}) + +install(TARGETS sqlite_orm + EXPORT SqliteOrmTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + COMPONENT sqlite_orm_runtime + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT sqlite_orm_runtime + NAMELINK_COMPONENT sqlite_orm_development + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT sqlite_orm_development + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +INSTALL(DIRECTORY ${PROJECT_SOURCE_DIR}/include/sqlite_orm DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +install(EXPORT SqliteOrmTargets + DESTINATION ${SQLITE_ORM_INSTALL_CMAKEDIR} + NAMESPACE sqlite_orm:: + COMPONENT sqlite_orm_development +) \ No newline at end of file diff --git a/libs/sqlite_orm-1.8.2/tests/CMakeLists.txt b/libs/sqlite_orm-1.8.2/tests/CMakeLists.txt new file mode 100644 index 0000000..945d275 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/CMakeLists.txt @@ -0,0 +1,176 @@ +cmake_minimum_required (VERSION 3.16) + +# note: find_package(SQLite3 REQUIRED) already done in top-level CMakeLists +FetchContent_MakeAvailable(Catch2) + +option(SQLITE_ORM_OMITS_CODECVT "Omits codec testing" OFF) + +add_executable(unit_tests + static_tests/functional/static_if_tests.cpp + static_tests/functional/mpl.cpp + static_tests/functional/same_or_void.cpp + static_tests/functional/tuple_conc.cpp + static_tests/functional/tuple_filter.cpp + static_tests/functional/tuple_traits.cpp + static_tests/functional/tuple_transform.cpp + static_tests/is_printable.cpp + static_tests/is_bindable.cpp + static_tests/iterator_t.cpp + static_tests/arithmetic_operators_result_type.cpp + static_tests/node_tuple.cpp + static_tests/bindable_filter.cpp + static_tests/member_traits_tests.cpp + static_tests/select_return_type.cpp + static_tests/column.cpp + static_tests/foreign_key.cpp + static_tests/function_static_tests.cpp + static_tests/aggregate_function_return_types.cpp + static_tests/core_function_return_types.cpp + static_tests/column_result_t.cpp + static_tests/alias.cpp + static_tests/is_primary_key_insertable.cpp + static_tests/is_column_with_insertable_primary_key.cpp + static_tests/operators_adl.cpp + tuple_iteration.cpp + sync_schema_tests.cpp + tests.cpp + tests2.cpp + tests3.cpp + tests4.cpp + tests5.cpp + column_tests.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 + prepared_statement_tests/column_names.cpp + pragma_tests.cpp + simple_query.cpp + constraints/default.cpp + constraints/unique.cpp + constraints/foreign_key.cpp + constraints/check.cpp + table_tests.cpp + statement_serializer_tests/column_constraints/generated.cpp + statement_serializer_tests/column_constraints/default.cpp + statement_serializer_tests/column_constraints/primary_key.cpp + statement_serializer_tests/column_constraints/autoincrement.cpp + statement_serializer_tests/column_constraints/unique.cpp + statement_serializer_tests/column_constraints/check.cpp + statement_serializer_tests/bindables.cpp + statement_serializer_tests/ast/upsert_clause.cpp + statement_serializer_tests/ast/excluded.cpp + statement_serializer_tests/ast/set.cpp + statement_serializer_tests/arithmetic_operators.cpp + statement_serializer_tests/base_types.cpp + statement_serializer_tests/collate.cpp + statement_serializer_tests/comparison_operators.cpp + statement_serializer_tests/core_functions.cpp + statement_serializer_tests/rowid.cpp + statement_serializer_tests/column_names.cpp + statement_serializer_tests/foreign_key.cpp + statement_serializer_tests/schema/index.cpp + statement_serializer_tests/schema/trigger.cpp + statement_serializer_tests/schema/new_old.cpp + statement_serializer_tests/schema/raise.cpp + statement_serializer_tests/indexed_column.cpp + statement_serializer_tests/logical_operators.cpp + statement_serializer_tests/statements/select.cpp + statement_serializer_tests/select_constraints.cpp + statement_serializer_tests/conditions.cpp + statement_serializer_tests/statements/insert_replace.cpp + statement_serializer_tests/statements/update.cpp + statement_serializer_tests/statements/remove.cpp + statement_serializer_tests/statements/update_all.cpp + statement_serializer_tests/aggregate_functions.cpp + statement_serializer_tests/alias_extractor.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 + unique_cases/issue86.cpp + unique_cases/issue937.cpp + unique_cases/issue663.cpp + get_all_custom_containers.cpp + select_constraints_tests.cpp + backup_tests.cpp + transaction_tests.cpp + json.cpp + row_id.cpp + trigger_tests.cpp + ast_iterator_tests.cpp + table_name_collector.cpp + pointer_passing_interface.cpp +) + +if(SQLITE_ORM_OMITS_CODECVT) + message(STATUS "SQLITE_ORM_OMITS_CODECVT is enabled") + target_compile_definitions(unit_tests PRIVATE SQLITE_ORM_OMITS_CODECVT=1) +endif() + +if (MSVC) + target_compile_options(unit_tests PUBLIC + # multi-processor compilation + /MP) + if (MSVC_VERSION LESS_EQUAL 1900) + target_compile_options(unit_tests PUBLIC + # C4503: decorated name length exceeded + /wd4503 + # C4800: forcing value to bool (performance warning) + /wd4800) + else() + target_compile_options(unit_tests PUBLIC + # warning-level 4 + /W4 + # C4127: conditional expression is constant + /wd4127 + # C4456: declaration of 'symbol' hides previous local declaration + /wd4456 + # C4458: declaration of 'symbol' hides class member + /wd4458) + endif() +endif() + +target_precompile_headers(unit_tests PRIVATE + + ) + +# note: sqlite3 already linked in top-level CMakeLists +target_link_libraries(unit_tests PRIVATE sqlite_orm Catch2::Catch2WithMain) + +add_test(NAME "All_in_one_unit_test" + COMMAND unit_tests + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/libs/sqlite_orm-1.8.2/tests/ast_iterator_tests.cpp b/libs/sqlite_orm-1.8.2/tests/ast_iterator_tests.cpp new file mode 100644 index 0000000..4bf1262 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/ast_iterator_tests.cpp @@ -0,0 +1,300 @@ +#include +#include + +#include // std::type_index + +using namespace sqlite_orm; +using internal::alias_column_t; +using internal::alias_holder; +using internal::column_alias; +using internal::column_pointer; +using internal::iterate_ast; + +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("bindables") { + auto node = select(1); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + SECTION("aggregate functions") { + SECTION("avg") { + auto node = avg(&User::id); + expected.push_back(typeid(&User::id)); + iterate_ast(node, lambda); + } + SECTION("avg filter") { + auto node = avg(&User::id).filter(where(length(&User::name) > 5)); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(&User::name)); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + SECTION("count(*)") { + auto node = count(); + expected.push_back(typeid(node)); + iterate_ast(node, lambda); + } + SECTION("count(*) filter") { + auto node = count().filter(where(length(&User::name) > 5)); + expected.push_back(typeid(decltype(count()))); + expected.push_back(typeid(&User::name)); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + SECTION("count(X)") { + auto node = count(&User::id); + expected.push_back(typeid(&User::id)); + iterate_ast(node, lambda); + } + SECTION("count(X) filter") { + auto node = count(&User::id).filter(where(length(&User::name) > 5)); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(&User::name)); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + SECTION("group_concat(X)") { + auto node = group_concat(&User::id); + expected.push_back(typeid(&User::id)); + iterate_ast(node, lambda); + } + SECTION("group_concat(X) filter") { + auto node = group_concat(&User::id).filter(where(length(&User::name) > 5)); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(&User::name)); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + SECTION("group_concat(X,Y)") { + auto node = group_concat(&User::id, std::string("-")); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(std::string)); + iterate_ast(node, lambda); + } + SECTION("group_concat(X,Y) filter") { + auto node = group_concat(&User::id, std::string("-")).filter(where(length(&User::name) > 5)); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(std::string)); + expected.push_back(typeid(&User::name)); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + SECTION("max(X)") { + auto node = max(&User::id); + expected.push_back(typeid(&User::id)); + iterate_ast(node, lambda); + } + SECTION("max(X) filter") { + auto node = max(&User::id).filter(where(length(&User::name) > 5)); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(&User::name)); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + SECTION("min(X)") { + auto node = min(&User::id); + expected.push_back(typeid(&User::id)); + iterate_ast(node, lambda); + } + SECTION("min(X) filter") { + auto node = min(&User::id).filter(where(length(&User::name) > 5)); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(&User::name)); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + SECTION("sum(X)") { + auto node = sum(&User::id); + expected.push_back(typeid(&User::id)); + iterate_ast(node, lambda); + } + SECTION("sum(X) filter") { + auto node = sum(&User::id).filter(where(length(&User::name) > 5)); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(&User::name)); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + SECTION("total(X)") { + auto node = total(&User::id); + expected.push_back(typeid(&User::id)); + iterate_ast(node, lambda); + } + SECTION("total(X) filter") { + auto node = total(&User::id).filter(where(length(&User::name) > 5)); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(&User::name)); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + } + SECTION("scalar functions") { + SECTION("max(X,Y)") { + auto node = max(&User::id, 4); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + SECTION("min(X,Y)") { + auto node = min(&User::id, 4); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + } + SECTION("on") { + auto node = on(&User::id == c(0)); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + SECTION("using") { + auto node = using_(&User::id); + expected.push_back(typeid(column_pointer)); + iterate_ast(node, lambda); + } + SECTION("exists") { + auto node = exists(select(5)); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + SECTION("order_by") { + SECTION("expression") { + auto node = order_by(c(&User::id) == 0); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + SECTION("bindable") { + auto node = order_by(""); + expected.push_back(typeid(const char*)); + iterate_ast(node, lambda); + } + SECTION("positional ordinal") { + auto node = order_by(1); + iterate_ast(node, lambda); + } + SECTION("sole column alias") { + auto node = order_by(get()); + iterate_ast(node, lambda); + } + SECTION("direct sole column alias") { + auto node = order_by(colalias_a{}); + iterate_ast(node, lambda); + } + SECTION("column alias in expression") { + auto node = order_by(get() > c(1)); + expected.push_back(typeid(int)); + iterate_ast(node, lambda); + } + } + SECTION("group_by") { + auto node = group_by(&User::id); + expected.push_back(typeid(&User::id)); + iterate_ast(node, lambda); + } + SECTION("excluded") { + auto node = excluded(&User::id); + expected.push_back(typeid(&User::id)); + iterate_ast(node, lambda); + } +#if SQLITE_VERSION_NUMBER >= 3024000 + 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)); + iterate_ast(node, lambda); + } +#endif + SECTION("into") { + auto node = into(); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + iterate_ast(node, lambda); + } + SECTION("aliases") { + SECTION("holder") { + auto expression = get() > c(0); + expected.push_back(typeid(int)); + iterate_ast(expression, lambda); + } + } + SECTION("aliased regular column") { + using als = alias_z; + auto expression = alias_column(&User::id); + expected.push_back(typeid(alias_column_t, decltype(&User::id)>)); + iterate_ast(expression, lambda); + } + SECTION("aliased regular column pointer") { + using als = alias_z; + auto expression = alias_column(column(&User::id)); + expected.push_back(typeid(alias_column_t, column_pointer>)); + iterate_ast(expression, lambda); + } + REQUIRE(typeIndexes == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/backup_tests.cpp b/libs/sqlite_orm-1.8.2/tests/backup_tests.cpp new file mode 100644 index 0000000..3d4df08 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/backup_tests.cpp @@ -0,0 +1,159 @@ +#include +#include +#include // remove + +using namespace sqlite_orm; + +namespace { + struct User { + int id = 0; + std::string name; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User() = default; + User(int id, std::string name) : id{id}, name{std::move(name)} {} +#endif + }; + + bool operator==(const User& lhs, const User& rhs) { + return lhs.id == rhs.id && lhs.name == rhs.name; + } + + struct MarvelHero { + int id; + std::string name; + std::string abilities; + }; + + auto initStorageMarvel(const std::string& path) { + auto storage = make_storage(path, + make_table("marvel", + make_column("id", &MarvelHero::id, primary_key()), + make_column("name", &MarvelHero::name), + make_column("abilities", &MarvelHero::abilities))); + return storage; + } +} + +TEST_CASE("backup") { + using Catch::Matchers::UnorderedEquals; + + const std::string usersTableName = "users"; + 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))); + }; + static int filenameSuffix = 0; // to make an unique filename for every test + const std::string backupFilename = "backup" + std::to_string(filenameSuffix++) + ".sqlite"; + SECTION("to") { + ::remove(backupFilename.c_str()); + auto storage2 = makeStorage(backupFilename); + auto storage = makeStorage(""); + storage.sync_schema(); + storage.replace(User{1, "Sharon"}); + storage.replace(User{2, "Maitre"}); + storage.replace(User{3, "Rita"}); + REQUIRE(!storage2.table_exists(usersTableName)); + SECTION("filename") { + storage.backup_to(backupFilename); + } + SECTION("storage") { + storage.backup_to(storage2); + } + SECTION("filename step -1") { + auto backup = storage.make_backup_to(backupFilename); + backup.step(-1); + } + SECTION("filename step 1") { + auto backup = storage.make_backup_to(backupFilename); + do { + backup.step(1); + } while(backup.remaining() > 0); + } + SECTION("storage step -1") { + auto backup = storage.make_backup_to(storage2); + backup.step(-1); + } + SECTION("storage step 1") { + auto backup = storage.make_backup_to(storage2); + do { + backup.step(1); + } while(backup.remaining() > 0); + } + REQUIRE(storage2.table_exists(usersTableName)); + auto rowsFromBackup = storage2.get_all(); + auto expectedRows = storage.get_all(); + REQUIRE_THAT(rowsFromBackup, UnorderedEquals(expectedRows)); + } + SECTION("from") { + ::remove(backupFilename.c_str()); + auto storage = makeStorage(backupFilename); + storage.sync_schema(); + storage.replace(User{1, "Sharon"}); + storage.replace(User{2, "Maitre"}); + storage.replace(User{3, "Rita"}); + auto storage2 = makeStorage(""); + SECTION("filename") { + storage2.backup_from(backupFilename); + } + SECTION("filename step -1") { + auto backup = storage2.make_backup_from(backupFilename); + backup.step(-1); + } + SECTION("filename step 1") { + auto backup = storage2.make_backup_from(backupFilename); + do { + backup.step(1); + } while(backup.remaining() > 0); + } + SECTION("storage") { + storage2.backup_from(storage); + } + SECTION("storage step -1") { + auto backup = storage2.make_backup_from(storage); + backup.step(-1); + } + SECTION("storage step -1") { + auto backup = storage2.make_backup_from(storage); + do { + backup.step(1); + } while(backup.remaining() > 0); + } + REQUIRE(storage2.table_exists(usersTableName)); + auto rowsFromBackup = storage2.get_all(); + REQUIRE_THAT(rowsFromBackup, UnorderedEquals(storage.get_all())); + } +} + +TEST_CASE("Backup crash") { + using MarvelStorage = decltype(initStorageMarvel("")); + + // --- Create a shared pointer to the MarvelStorage + std::string fp("iteration.sqlite"); + std::shared_ptr db = std::make_shared(initStorageMarvel(fp)); + auto storage{*db}; + + storage.sync_schema(); + storage.remove_all(); + + // --- Insert values + storage.insert(MarvelHero{-1, "Tony Stark", "Iron man, playboy, billionaire, philanthropist"}); + storage.insert(MarvelHero{-1, "Thor", "Storm god"}); + storage.insert(MarvelHero{-1, "Vision", "Min Stone"}); + storage.insert(MarvelHero{-1, "Captain America", "Vibranium shield"}); + storage.insert(MarvelHero{-1, "Hulk", "Strength"}); + storage.insert(MarvelHero{-1, "Star Lord", "Humor"}); + storage.insert(MarvelHero{-1, "Peter Parker", "Spiderman"}); + storage.insert(MarvelHero{-1, "Clint Barton", "Hawkeye"}); + storage.insert(MarvelHero{-1, "Natasha Romanoff", "Black widow"}); + storage.insert(MarvelHero{-1, "Groot", "I am Groot!"}); + REQUIRE(storage.count() == 10); + + // --- Create backup file name and verify that the file does not exist + std::string backupFilename{"backup.sqlite"}; + + // --- Backup the current storage to the file + auto backup = storage.make_backup_to(backupFilename); + backup.step(-1); +} diff --git a/libs/sqlite_orm-1.8.2/tests/built_in_functions_tests/core_functions_tests.cpp b/libs/sqlite_orm-1.8.2/tests/built_in_functions_tests/core_functions_tests.cpp new file mode 100644 index 0000000..3faf662 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/built_in_functions_tests/core_functions_tests.cpp @@ -0,0 +1,1401 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("substr") { + struct Test { + std::string text; + int x = 0; + int y = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Test() = default; + Test(std::string text, int x, int y) : text{std::move(text)}, x{x}, y{y} {} +#endif + }; + 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, 1}); + 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; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Test() = default; + Test(int value) : value{value} {} +#endif + }; + + 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; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Department() = default; + Department(int id, std::string name, int managerId, int locationId) : + id{id}, name{std::move(name)}, managerId{managerId}, locationId{locationId} {} +#endif + }; + 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; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Employee() = default; + Employee(int id, std::string firstName, std::string lastName, std::string address) : + id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, address{std::move(address)} {} +#endif + }; + + 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; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Contact() = default; + Contact(int id, std::string firstName, std::string lastName, std::string phone) : + id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, phone{std::move(phone)} {} +#endif + }; + + 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") { + using Catch::Matchers::Equals; + using std::nullopt, std::optional, std::vector; + + struct Foo { + std::optional field; + }; + + auto storage = make_storage({}, make_table("foo", make_column("field", &Foo::field))); + storage.sync_schema(); + storage.transaction([&storage]() { + storage.insert({}); + storage.insert({1.}); + return true; + }); + + 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)); + } + } + SECTION("common return type") { + auto rows = storage.select(coalesce(&Foo::field, 0), order_by(1)); + REQUIRE_THAT(rows, Equals(vector>{0., 1.})); + } +} +#endif + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +TEST_CASE("nullif") { + using Catch::Matchers::Equals; + using std::nullopt, std::optional, std::vector; + + struct Foo { + bool field; + }; + + auto storage = make_storage({}, make_table("foo", make_column("field", &Foo::field))); + storage.sync_schema(); + storage.transaction([&storage]() { + storage.insert({false}); + storage.insert({true}); + return true; + }); + + SECTION("explicit return type") { + auto rows = storage.select(&Foo::field, where(nullif>(&Foo::field, false))); + REQUIRE(rows.size() == 1); + REQUIRE(rows[0] == true); + } +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + SECTION("common return type") { + auto rows = storage.select(&Foo::field, where(nullif(&Foo::field, false))); + REQUIRE(rows.size() == 1); + REQUIRE(rows[0] == true); + } + SECTION("null if 0") { + auto rows = storage.select(nullif(&Foo::field, 0), order_by(1)); + REQUIRE_THAT(rows, Equals(vector>{nullopt, 1})); + } + SECTION("null if 1") { + auto rows = storage.select(nullif(&Foo::field, 1), order_by(1)); + REQUIRE_THAT(rows, Equals(vector>{nullopt, 0})); + } +#endif +} +#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; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Customer() = default; + Customer(int id, + 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, + decltype(fax) fax, + std::string email, + int supportRepId) : + id{id}, + firstName{std::move(firstName)}, lastName{std::move(lastName)}, company{std::move(company)}, + address{std::move(address)}, city{std::move(city)}, state{std::move(state)}, country{std::move(country)}, + postalCode{std::move(postalCode)}, phone{std::move(phone)}, fax{std::move(fax)}, email{std::move(email)}, + supportRepId{supportRepId} {} +#endif + }; + 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/libs/sqlite_orm-1.8.2/tests/built_in_functions_tests/datetime_function_tests.cpp b/libs/sqlite_orm-1.8.2/tests/built_in_functions_tests/datetime_function_tests.cpp new file mode 100644 index 0000000..b6e0812 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/built_in_functions_tests/datetime_function_tests.cpp @@ -0,0 +1,65 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("time") { + auto storage = make_storage({}); + { + auto rows = storage.select(time("12:00", "localtime")); + REQUIRE(rows.size() == 1); + REQUIRE(!rows.front().empty()); + } + { + auto rows = storage.select(time("12:00", "utc")); + REQUIRE(rows.size() == 1); + REQUIRE(!rows.front().empty()); + } +} + +TEST_CASE("julianday") { + struct Test { + std::string text; + }; + + 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 rows = storage.select(julianday(arg)); + REQUIRE(rows.size() == 1); + REQUIRE((rows.front() - expected) < 0.001); // too much precision + } + { + storage.insert(Test{arg}); + auto rows = storage.select(julianday(&Test::text)); + REQUIRE(rows.size() == 1); + REQUIRE((rows.front() - expected) < 0.001); + storage.remove_all(); + } + }; + singleTestCase("2016-10-18", 2457679.5); + singleTestCase("2016-10-18 16:45", 2457680.19791667); + singleTestCase("2016-10-18 16:45:30", 2457680.19826389); +} + +TEST_CASE("datetime") { + auto storage = make_storage({}); + auto rows = storage.select(datetime("now")); + REQUIRE(rows.size() == 1); + REQUIRE(!rows.front().empty()); +} + +TEST_CASE("date") { + auto storage = make_storage({}); + auto rows = storage.select(date("now", "start of month", "+1 month", "-1 day")); + REQUIRE(rows.size() == 1); + REQUIRE(!rows.front().empty()); +} + +TEST_CASE("strftime") { + auto storage = make_storage({}); + auto rows = storage.select(strftime("%Y %m %d", "now")); + REQUIRE(rows.size() == 1); + REQUIRE(!rows.front().empty()); +} diff --git a/libs/sqlite_orm-1.8.2/tests/built_in_functions_tests/math_functions.cpp b/libs/sqlite_orm-1.8.2/tests/built_in_functions_tests/math_functions.cpp new file mode 100644 index 0000000..eb8ee7e --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/built_in_functions_tests/math_functions.cpp @@ -0,0 +1,466 @@ +#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); +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + 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; + } + }; +#endif + 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/libs/sqlite_orm-1.8.2/tests/column_tests.cpp b/libs/sqlite_orm-1.8.2/tests/column_tests.cpp new file mode 100644 index 0000000..df7c25d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/column_tests.cpp @@ -0,0 +1,32 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("column tests is_generated") { + struct User { + int id = 0; + int age = 0; + }; + SECTION("no constraints") { + auto column = make_column("id", &User::id); + REQUIRE(!column.is_generated()); + } +#if SQLITE_VERSION_NUMBER >= 3031000 + SECTION("1 constraint: generated") { + SECTION("full") { + auto column = make_column("age", &User::age, generated_always_as(add(&User::id, 5))); + REQUIRE(column.is_generated()); + } + + SECTION("not full") { + auto column = make_column("age", &User::age, as(add(&User::id, 5))); + REQUIRE(column.is_generated()); + } + } +#endif + SECTION("1 constraint: primary key") { + auto column = make_column("id", &User::id, primary_key()); + REQUIRE(!column.is_generated()); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/constraints/check.cpp b/libs/sqlite_orm-1.8.2/tests/constraints/check.cpp new file mode 100644 index 0000000..233d848 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/constraints/check.cpp @@ -0,0 +1,52 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("check") { + SECTION("table level") { + struct Contact { + int id = 0; + std::string firstName; + std::string lastName; + std::unique_ptr email; + std::string phone; + }; + 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("email", &Contact::email), + make_column("phone", &Contact::phone), + check(length(&Contact::phone) >= 10))); + storage.sync_schema(); + } + SECTION("column level") { + struct Book { + int id = 0; + std::string name; + std::string pubName; + int price = 0; + }; + SECTION(">") { + auto storage = make_storage({}, + make_table("BOOK", + make_column("Book_id", &Book::id, primary_key()), + make_column("Book_name", &Book::name), + make_column("Pub_name", &Book::pubName), + make_column("PRICE", &Book::price, check(c(&Book::price) > 0)))); + storage.sync_schema(); + } + SECTION("like") { + auto storage = make_storage({}, + make_table("BOOK", + make_column("Book_id", &Book::id, primary_key()), + make_column("Book_name", &Book::name), + make_column("Pub_name", &Book::pubName), + make_column("PRICE", &Book::price, check(c(&Book::price) > 0)), + check(like(&Book::pubName, "M%")))); + storage.sync_schema(); + } + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/constraints/composite_key.cpp b/libs/sqlite_orm-1.8.2/tests/constraints/composite_key.cpp new file mode 100644 index 0000000..91d9b06 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/constraints/composite_key.cpp @@ -0,0 +1,64 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Composite key") { + struct Record { + int year = 0; + int month = 0; + int amount = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Record() = default; + Record(int year, int month, int amount) : year{year}, month{month}, amount{amount} {} +#endif + }; + + auto recordsTableName = "records"; + auto storage = make_storage({}, + make_table(recordsTableName, + make_column("year", &Record::year), + make_column("month", &Record::month), + make_column("amount", &Record::amount), + primary_key(&Record::year, &Record::month))); + + storage.sync_schema(); + REQUIRE(storage.sync_schema().at(recordsTableName) == sqlite_orm::sync_schema_result::already_in_sync); + + // after #18 + SECTION("Repeat sync") { + auto storage2 = make_storage("", + make_table(recordsTableName, + make_column("year", &Record::year), + make_column("month", &Record::month), + make_column("amount", &Record::amount), + primary_key(&Record::month, &Record::year))); + storage2.sync_schema(); + REQUIRE(storage2.sync_schema().at(recordsTableName) == sqlite_orm::sync_schema_result::already_in_sync); + + auto storage3 = make_storage("", + make_table(recordsTableName, + make_column("year", &Record::year), + make_column("month", &Record::month), + make_column("amount", &Record::amount), + primary_key(&Record::amount, &Record::month, &Record::year))); + storage3.sync_schema(); + REQUIRE(storage3.sync_schema().at(recordsTableName) == sqlite_orm::sync_schema_result::already_in_sync); + } + + // after #348 + SECTION("get & get_pointer") { + storage.replace(Record{1, 2, 3}); + + REQUIRE(storage.count() == 1); + + auto record = storage.get(1, 2); + REQUIRE(record.year == 1); + REQUIRE(record.month == 2); + REQUIRE(record.amount == 3); + + auto recordPointer = storage.get_pointer(1, 2); + REQUIRE(recordPointer); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/constraints/default.cpp b/libs/sqlite_orm-1.8.2/tests/constraints/default.cpp new file mode 100644 index 0000000..e59c63e --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/constraints/default.cpp @@ -0,0 +1,73 @@ +#include +#include + +using namespace sqlite_orm; + +// appeared after #55 +TEST_CASE("Default value") { + struct User { + int userId; + std::string name; + int age; + std::string email; + }; + + 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), + make_column("Age", &User::age))); + storage1.sync_schema(); + storage1.remove_all(); + + auto emailColumn = make_column("Email", &User::email, default_value("example@email.com")); + + auto storage2 = make_storage(filename, + make_table("User", + make_column("Id", &User::userId, primary_key()), + make_column("Name", &User::name), + make_column("Age", &User::age), + emailColumn)); + storage2.sync_schema(); + storage2.insert(User{0, "Tom", 15, ""}); + + auto emailDefault = emailColumn.default_value(); + REQUIRE(emailDefault); + auto& emailDefaultString = *emailDefault; + REQUIRE(emailDefaultString == "'example@email.com'"); +} + +TEST_CASE("Default datetime") { + struct Induction { + std::string time; + }; + + auto storage = make_storage( + {}, + make_table("induction", + 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/libs/sqlite_orm-1.8.2/tests/constraints/foreign_key.cpp b/libs/sqlite_orm-1.8.2/tests/constraints/foreign_key.cpp new file mode 100644 index 0000000..436b695 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/constraints/foreign_key.cpp @@ -0,0 +1,129 @@ +#include +#include + +#include // std::is_same + +#include "../static_tests/static_tests_storage_traits.h" + +using namespace sqlite_orm; + +TEST_CASE("Foreign key") { + + struct Location { + int id; + std::string place; + std::string country; + std::string city; + int distance; + }; + + struct Visit { + int id; + std::unique_ptr location; + std::unique_ptr user; + int visited_at; + uint8_t mark; + }; + + // this case didn't compile on linux until `typedef constraints_type` was added to `foreign_key_t` + auto storage = make_storage("test_fk.sqlite", + make_table("location", + make_column("id", &Location::id, primary_key()), + make_column("place", &Location::place), + make_column("country", &Location::country), + make_column("city", &Location::city), + make_column("distance", &Location::distance)), + make_table("visit", + make_column("id", &Visit::id, primary_key()), + make_column("location", &Visit::location), + make_column("user", &Visit::user), + make_column("visited_at", &Visit::visited_at), + make_column("mark", &Visit::mark), + foreign_key(&Visit::location).references(&Location::id))); + { + using namespace sqlite_orm::internal::storage_traits; + + using Storage = decltype(storage); + STATIC_REQUIRE(storage_foreign_keys_count::value == 1); + STATIC_REQUIRE(storage_foreign_keys_count::value == 0); + + using LocationFks = storage_fk_references::type; + STATIC_REQUIRE(std::is_same>::value); + + using VisitFks = storage_fk_references::type; + STATIC_REQUIRE(std::is_same>::value); + } + storage.sync_schema(); + + int fromDate = int(std::time(nullptr)); + int toDate = int(std::time(nullptr)); + int toDistance = 100; + auto id = 10; + storage.select(columns(&Visit::mark, &Visit::visited_at, &Location::place), + inner_join(on(is_equal(&Visit::location, &Location::id))), + where(is_equal(&Visit::user, id) and greater_than(&Visit::visited_at, fromDate) and + lesser_than(&Visit::visited_at, toDate) and lesser_than(&Location::distance, toDistance)), + order_by(&Visit::visited_at)); +} + +// appeared after #57 +TEST_CASE("Foreign key 2") { + class test1 { + public: + // Constructors + test1(){}; + + // Variables + int id; + std::string val1; + std::string val2; + }; + + class test2 { + public: + // Constructors + test2(){}; + + // Variables + int id; + int fk_id; + std::string val1; + std::string val2; + }; + + auto table1 = make_table("test_1", + make_column("id", &test1::id, primary_key()), + make_column("val1", &test1::val1), + make_column("val2", &test1::val2)); + + auto table2 = make_table("test_2", + make_column("id", &test2::id, primary_key()), + make_column("fk_id", &test2::fk_id), + make_column("val1", &test2::val1), + make_column("val2", &test2::val2), + foreign_key(&test2::fk_id).references(&test1::id)); + + auto storage = make_storage("test.sqlite", table1, table2); + + storage.sync_schema(); + + test1 t1; + t1.val1 = "test"; + t1.val2 = "test"; + storage.insert(t1); + + test1 t1_copy; + t1_copy.val1 = "test"; + t1_copy.val2 = "test"; + storage.insert(t1_copy); + + test2 t2; + t2.fk_id = 1; + t2.val1 = "test"; + t2.val2 = "test"; + storage.insert(t2); + + t2.fk_id = 2; + + storage.update(t2); +} diff --git a/libs/sqlite_orm-1.8.2/tests/constraints/unique.cpp b/libs/sqlite_orm-1.8.2/tests/constraints/unique.cpp new file mode 100644 index 0000000..dd7a81b --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/constraints/unique.cpp @@ -0,0 +1,68 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Unique") { + using Catch::Matchers::ContainsSubstring; + + struct Contact { + int id = 0; + std::string firstName; + std::string lastName; + std::string email; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Contact() = default; + Contact(int id, std::string firstName, std::string lastName, std::string email) : + id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, email{std::move(email)} {} +#endif + }; + struct Shape { + int id = 0; + std::string backgroundColor; + std::string foregroundColor; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Shape() = default; + Shape(int id, std::string backgroundColor, std::string foregroundColor) : + id{id}, backgroundColor{std::move(backgroundColor)}, foregroundColor{std::move(foregroundColor)} {} +#endif + }; + struct List { + int id = 0; + std::unique_ptr email; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + List() = default; + List(int id, decltype(email) email) : id{id}, email{std::move(email)} {} +#endif + }; + + 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("email", &Contact::email, unique())), + make_table("shapes", + make_column("shape_id", &Shape::id, primary_key()), + make_column("background_color", &Shape::backgroundColor), + make_column("foreground_color", &Shape::foregroundColor), + sqlite_orm::unique(&Shape::backgroundColor, &Shape::foregroundColor)), + make_table("lists", make_column("list_id", &List::id, primary_key()), make_column("email", &List::email))); + storage.sync_schema(); + + storage.insert(Contact{0, "John", "Doe", "john.doe@gmail.com"}); + + REQUIRE_THROWS_WITH(storage.insert(Contact{0, "Johnny", "Doe", "john.doe@gmail.com"}), + ContainsSubstring("constraint failed")); + + storage.insert(Shape{0, "red", "green"}); + storage.insert(Shape{0, "red", "blue"}); + REQUIRE_THROWS_WITH(storage.insert(Shape{0, "red", "green"}), ContainsSubstring("constraint failed")); + + std::vector lists(2); + REQUIRE_NOTHROW(storage.insert_range(lists.begin(), lists.end())); +} diff --git a/libs/sqlite_orm-1.8.2/tests/explicit_columns.cpp b/libs/sqlite_orm-1.8.2/tests/explicit_columns.cpp new file mode 100644 index 0000000..5762aa8 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/explicit_columns.cpp @@ -0,0 +1,95 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Explicit columns") { + struct Object { + int id; + }; + + struct User : Object { + std::string name; + + User(decltype(id) id_, decltype(name) name_) : Object{id_}, name(std::move(name_)) {} + }; + + struct Token : Object { + std::string token; + int usedId; + + Token(decltype(id) id_, decltype(token) token_, decltype(usedId) usedId_) : + Object{id_}, token(std::move(token_)), usedId(usedId_) {} + }; + + auto storage = make_storage( + "column_pointer.sqlite", + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)), + make_table("tokens", + make_column("id", &Token::id, primary_key()), + make_column("token", &Token::token), + make_column("user_id", &Token::usedId), + foreign_key(&Token::usedId).references(column(&User::id)))); + storage.sync_schema(); + REQUIRE(storage.table_exists("users")); + REQUIRE(storage.table_exists("tokens")); + + storage.remove_all(); + storage.remove_all(); + + auto brunoId = storage.insert(User{0, "Bruno"}); + auto zeddId = storage.insert(User{0, "Zedd"}); + + REQUIRE(storage.count() == 2); + + { + auto w = where(is_equal(&User::name, "Bruno")); + + { + auto rows = storage.select(column(&User::id), w); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == brunoId); + } + + { + auto rows2 = storage.select(columns(column(&User::id)), w); + REQUIRE(rows2.size() == 1); + REQUIRE(std::get<0>(rows2.front()) == brunoId); + + auto rows3 = storage.select(columns(column(&Object::id)), w); + REQUIRE(rows3 == rows2); + } + } + + { + auto rows = storage.select(column(&User::id), where(is_equal(&User::name, "Zedd"))); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == zeddId); + } + + { + auto abcId = storage.insert(Token(0, "abc", brunoId)); + + auto w = where(is_equal(&Token::token, "abc")); + { + auto rows = storage.select(column(&Token::id), w); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == abcId); + } + + { + auto rows2 = storage.select(columns(column(&Token::id), &Token::usedId), w); + REQUIRE(rows2.size() == 1); + REQUIRE(std::get<0>(rows2.front()) == abcId); + REQUIRE(std::get<1>(rows2.front()) == brunoId); + } + } + + { + auto joinedRows = storage.select(columns(&User::name, &Token::token), + join(on(is_equal(&Token::usedId, column(&User::id))))); + REQUIRE(joinedRows.size() == 1); + REQUIRE(std::get<0>(joinedRows.front()) == "Bruno"); + REQUIRE(std::get<1>(joinedRows.front()) == "abc"); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/filename.cpp b/libs/sqlite_orm-1.8.2/tests/filename.cpp new file mode 100644 index 0000000..e779ab8 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/filename.cpp @@ -0,0 +1,19 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("filename") { + { + auto storage = make_storage(""); + REQUIRE(storage.filename() == ""); + } + { + auto storage = make_storage(":memory:"); + REQUIRE(storage.filename() == ":memory:"); + } + { + auto storage = make_storage("myDatabase.sqlite"); + REQUIRE(storage.filename() == "myDatabase.sqlite"); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/get_all_custom_containers.cpp b/libs/sqlite_orm-1.8.2/tests/get_all_custom_containers.cpp new file mode 100644 index 0000000..99d05bc --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/get_all_custom_containers.cpp @@ -0,0 +1,138 @@ +#include +#include + +#include +#include + +using namespace sqlite_orm; + +namespace { + struct User { + int id = 0; + std::string name; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User() = default; + User(int id, std::string name) : id{id}, name{std::move(name)} {} +#endif + }; + + struct Comparator { + + 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 { + if(lhs) { + return this->operator()(*lhs, rhs); + } else { + return false; + } + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + bool operator()(const std::optional& lhs, const User& rhs) const { + if(lhs.has_value()) { + return this->operator()(*lhs, rhs); + } else { + return false; + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + }; +} + +struct Tester { + const std::vector& expected; + + template + void testContainer(const T& users) const { + REQUIRE(std::equal(users.begin(), users.end(), this->expected.begin(), this->expected.end(), Comparator{})); + STATIC_REQUIRE(std::is_same::value); + } + + template + void testPreparedStatement(S& storage, const T& statement) const { + this->testContainer(storage.execute(statement)); + } +}; + +TEST_CASE("get_all deque") { + using Catch::Matchers::UnorderedEquals; + + auto storage = make_storage( + {}, + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name))); + storage.sync_schema(); + + User user1{1, "Nicki"}; + User user2{2, "Karol"}; + storage.replace(user1); + storage.replace(user2); + + std::vector expected; + expected.push_back(user1); + expected.push_back(user2); + + Tester tester{expected}; + + // get_all + tester.testContainer>(storage.get_all()); + tester.testContainer>(storage.get_all>()); + tester.testContainer>(storage.get_all>()); + tester.testContainer>(storage.get_all>()); + tester.testPreparedStatement>(storage, storage.prepare(get_all())); + tester.testPreparedStatement>(storage, storage.prepare(get_all>())); + tester.testPreparedStatement>(storage, storage.prepare(get_all>())); + tester.testPreparedStatement>(storage, storage.prepare(get_all>())); + + // get_all_pointer + { + using UserP = std::unique_ptr; + tester.testContainer>(storage.get_all_pointer()); + { + using Container = std::vector; + tester.testContainer(storage.get_all_pointer()); + } + { + using Container = std::deque; + tester.testContainer(storage.get_all_pointer()); + } + { + using Container = std::list; + tester.testContainer(storage.get_all_pointer()); + } + tester.testPreparedStatement>(storage, storage.prepare(get_all_pointer())); + { + using Container = std::vector; + tester.testPreparedStatement(storage, storage.prepare(get_all_pointer())); + } + { + using Container = std::deque; + tester.testPreparedStatement(storage, storage.prepare(get_all_pointer())); + } + { + using Container = std::list; + tester.testPreparedStatement(storage, storage.prepare(get_all_pointer())); + } + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + // get_all_optional + { + using UserP = std::optional; + tester.testPreparedStatement>(storage, storage.prepare(get_all_optional())); + { + using Container = std::vector; + tester.testPreparedStatement(storage, storage.prepare(get_all_optional())); + } + { + using Container = std::deque; + tester.testPreparedStatement(storage, storage.prepare(get_all_optional())); + } + { + using Container = std::list; + tester.testPreparedStatement(storage, storage.prepare(get_all_optional())); + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} diff --git a/libs/sqlite_orm-1.8.2/tests/index_tests.cpp b/libs/sqlite_orm-1.8.2/tests/index_tests.cpp new file mode 100644 index 0000000..0809676 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/index_tests.cpp @@ -0,0 +1,128 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("index") { + struct User { + int id = 0; + std::string name; + }; + + auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); + SECTION("simple id") { + auto storage = make_storage({}, make_index("id_index", &User::id), table); + REQUIRE_NOTHROW(storage.sync_schema()); + } + SECTION("asc") { + auto storage = make_storage({}, make_index("id_index", indexed_column(&User::id).asc()), table); + REQUIRE_NOTHROW(storage.sync_schema()); + } + SECTION("desc") { + auto storage = make_storage({}, make_index("id_index", indexed_column(&User::id).desc()), table); + REQUIRE_NOTHROW(storage.sync_schema()); + } + SECTION("simple name") { + auto storage = make_storage({}, make_index("name_index", &User::name), table); + REQUIRE_NOTHROW(storage.sync_schema()); + } + SECTION("explicit name") { + auto storage = make_storage({}, make_index("name_index", indexed_column(&User::name)), table); + REQUIRE_NOTHROW(storage.sync_schema()); + } +#ifdef SQLITE_ENABLE_JSON1 + SECTION("json implicit") { + auto storage = + make_storage({}, make_index("name_index", json_extract(&User::name, "$.field")), table); + REQUIRE_NOTHROW(storage.sync_schema()); + } + SECTION("json explicit") { + auto storage = + make_storage({}, + make_index("name_index", indexed_column(json_extract(&User::name, "$.field"))), + table); + REQUIRE_NOTHROW(storage.sync_schema()); + } +#endif // SQLITE_ENABLE_JSON1 + SECTION("collate") { + auto storage = make_storage({}, make_index("name_index", indexed_column(&User::name).collate("binary")), table); + REQUIRE_NOTHROW(storage.sync_schema()); + } + SECTION("collate asc") { + auto storage = + make_storage({}, make_index("name_index", indexed_column(&User::name).collate("binary").asc()), table); + REQUIRE_NOTHROW(storage.sync_schema()); + } + SECTION("collate desc") { + auto storage = + make_storage({}, make_index("name_index", indexed_column(&User::name).collate("binary").desc()), table); + REQUIRE_NOTHROW(storage.sync_schema()); + } +} + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +TEST_CASE("filtered index") { + using Catch::Matchers::ContainsSubstring; + + struct Test { + std::optional field1 = 0; + std::optional field2 = 0; + }; + SECTION("1") { + auto storage = make_storage( + {}, + make_unique_index("ix2", &Test::field1, ifnull(&Test::field2, 0)), + make_table("test", make_column("Field1", &Test::field1), make_column("Field2", &Test::field2))); + REQUIRE_NOTHROW(storage.sync_schema()); + + storage.insert(Test{1, std::nullopt}); + REQUIRE_THROWS_WITH(storage.insert(Test{1, std::nullopt}), ContainsSubstring("constraint failed")); + } + SECTION("2") { + auto storage = make_storage( + {}, + make_unique_index("ix2", &Test::field1, where(is_not_null(&Test::field1))), + make_table("test", make_column("Field1", &Test::field1), make_column("Field2", &Test::field2))); + REQUIRE_NOTHROW(storage.sync_schema()); + } +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + +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))); + REQUIRE_NOTHROW(storage.sync_schema()); +} + +TEST_CASE("Compound index") { + struct User { + int id = 0; + std::string name; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User() = default; + User(int id, std::string name) : id{id}, name{std::move(name)} {} +#endif + }; + auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); + + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + std::string value; + decltype(value) expected; + + auto index = + make_index("idx_users_id_and_name", indexed_column(&User::id).asc(), indexed_column(&User::name).asc()); + value = internal::serialize(index, context); + expected = R"(CREATE INDEX IF NOT EXISTS "idx_users_id_and_name" ON "users" ("id" ASC, "name" ASC))"; + REQUIRE(value == expected); + auto storage = make_storage("compound_index.sqlite", index, table); + REQUIRE_NOTHROW(storage.sync_schema()); + REQUIRE_NOTHROW(storage.insert(User{1, "juan"})); +} diff --git a/libs/sqlite_orm-1.8.2/tests/json.cpp b/libs/sqlite_orm-1.8.2/tests/json.cpp new file mode 100644 index 0000000..ebc2238 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/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 = std::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/libs/sqlite_orm-1.8.2/tests/operators/arithmetic_operators.cpp b/libs/sqlite_orm-1.8.2/tests/operators/arithmetic_operators.cpp new file mode 100644 index 0000000..c54e4a6 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/operators/arithmetic_operators.cpp @@ -0,0 +1,168 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Arithmetic operators") { + struct Object { + std::string name; + int nameLen; + int number; + }; + + auto storage = make_storage("", + make_table("objects", + make_column("name", &Object::name), + make_column("name_len", &Object::nameLen), + make_column("number", &Object::number))); + storage.sync_schema(); + + std::vector names{ + "Zombie", + "Eminem", + "Upside down", + }; + auto number = 10; + 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]; + 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]; + REQUIRE(int(std::get<0>(row)) == name.length() + 1000); + } + } + { // || + 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]; + REQUIRE(row == name + suffix); + } + } + { // daisy-chaining ||, left + 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]; + REQUIRE(row == name + suffix + "."); + } + } + { // daisy-chaining ||, right + std::string prefix = "ototo"; + auto rows = storage.select(prefix || (c(&Object::name) || ".")); + for(size_t i = 0; i < rows.size(); ++i) { + auto& row = rows[i]; + auto& name = names[i]; + REQUIRE(row == prefix + name + "."); + } + } + { // || + 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]; + REQUIRE(std::get<0>(row) == name + suffix); + } + } + { // different + std::string suffix = "ototo"; + auto rows = storage.select(columns(conc(&Object::name, suffix), + c(&Object::name) || suffix, + &Object::name || c(suffix), + c(&Object::name) || c(suffix), + + add(&Object::nameLen, &Object::number), + c(&Object::nameLen) + &Object::number, + &Object::nameLen + c(&Object::number), + c(&Object::nameLen) + c(&Object::number), + c(&Object::nameLen) + 1000, + + sub(&Object::nameLen, &Object::number), + c(&Object::nameLen) - &Object::number, + &Object::nameLen - c(&Object::number), + c(&Object::nameLen) - c(&Object::number), + c(&Object::nameLen) - 1000, + + mul(&Object::nameLen, &Object::number), + c(&Object::nameLen) * &Object::number, + &Object::nameLen * c(&Object::number), + c(&Object::nameLen) * c(&Object::number), + c(&Object::nameLen) * 1000, + + div(&Object::nameLen, &Object::number), + c(&Object::nameLen) / &Object::number, + &Object::nameLen / c(&Object::number), + c(&Object::nameLen) / c(&Object::number), + c(&Object::nameLen) / 2)); + + for(size_t i = 0; i < rows.size(); ++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)); + REQUIRE(std::get<3>(row) == std::get<2>(row)); + + auto expectedAddNumber = int(name.length()) + number; + REQUIRE(std::get<4>(row) == expectedAddNumber); + REQUIRE(std::get<5>(row) == std::get<4>(row)); + REQUIRE(std::get<6>(row) == std::get<5>(row)); + REQUIRE(std::get<7>(row) == std::get<6>(row)); + { + auto& rowValue = std::get<8>(row); + REQUIRE(rowValue == int(name.length()) + 1000); + } + + auto expectedSubNumber = int(name.length()) - number; + REQUIRE(std::get<9>(row) == expectedSubNumber); + REQUIRE(std::get<10>(row) == std::get<9>(row)); + REQUIRE(std::get<11>(row) == std::get<10>(row)); + REQUIRE(std::get<12>(row) == std::get<11>(row)); + REQUIRE(std::get<13>(row) == int(name.length()) - 1000); + + auto expectedMulNumber = int(name.length()) * number; + REQUIRE(std::get<14>(row) == expectedMulNumber); + REQUIRE(std::get<15>(row) == std::get<14>(row)); + REQUIRE(std::get<16>(row) == std::get<15>(row)); + REQUIRE(std::get<17>(row) == std::get<16>(row)); + REQUIRE(std::get<18>(row) == int(name.length()) * 1000); + + auto expectedDivNumber = int(name.length()) / number; + REQUIRE(std::get<19>(row) == expectedDivNumber); + REQUIRE(std::get<20>(row) == std::get<19>(row)); + REQUIRE(std::get<21>(row) == std::get<20>(row)); + REQUIRE(std::get<22>(row) == std::get<21>(row)); + REQUIRE(std::get<23>(row) == int(name.length()) / 2); + } + } + { // % + auto rows = storage.select(columns(mod(&Object::nameLen, &Object::number), + c(&Object::nameLen) % &Object::number, + &Object::nameLen % c(&Object::number), + 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]; + 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)); + REQUIRE(std::get<3>(row) == std::get<2>(row)); + REQUIRE(std::get<4>(row) == static_cast(name.length()) % 5); + } + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/operators/between.cpp b/libs/sqlite_orm-1.8.2/tests/operators/between.cpp new file mode 100644 index 0000000..c1e52f1 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/operators/between.cpp @@ -0,0 +1,24 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Between") { + struct Object { + int id = 0; + }; + + auto storage = + make_storage("", make_table("objects", make_column("id", &Object::id, primary_key().autoincrement()))); + storage.sync_schema(); + + storage.insert(Object{}); + storage.insert(Object{}); + storage.insert(Object{}); + storage.insert(Object{}); + storage.insert(Object{}); + + auto allObjects = storage.get_all(); + auto rows = storage.select(&Object::id, where(between(&Object::id, 1, 3))); + REQUIRE(rows.size() == 3); +} diff --git a/libs/sqlite_orm-1.8.2/tests/operators/binary_operators.cpp b/libs/sqlite_orm-1.8.2/tests/operators/binary_operators.cpp new file mode 100644 index 0000000..89ad866 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/operators/binary_operators.cpp @@ -0,0 +1,174 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("binary operators") { + using Catch::Matchers::UnorderedEquals; + + struct User { + int id = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User() = default; + User(int id) : id{id} {} +#endif + }; + 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/libs/sqlite_orm-1.8.2/tests/operators/bitwise.cpp b/libs/sqlite_orm-1.8.2/tests/operators/bitwise.cpp new file mode 100644 index 0000000..7492a2c --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/operators/bitwise.cpp @@ -0,0 +1,63 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("bitwise operators") { + struct Entry { + int lhs = 0; + int rhs = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Entry() = default; + Entry(int lhs, int rhs) : lhs{lhs}, rhs{rhs} {} +#endif + }; + auto storage = + make_storage({}, make_table("entries", make_column("lhs", &Entry::lhs), make_column("rhs", &Entry::rhs))); + storage.sync_schema(); + + { + auto rows = storage.select(bitwise_or(60, 13)); + REQUIRE(rows == std::vector{61}); + } + { + auto rows = storage.select(bitwise_and(60, 13)); + REQUIRE(rows == std::vector{12}); + } + { + auto rows = storage.select(bitwise_shift_left(60, 2)); + REQUIRE(rows == std::vector{240}); + } + { + auto rows = storage.select(bitwise_shift_right(60, 2)); + REQUIRE(rows == std::vector{15}); + } + { + auto rows = storage.select(bitwise_not(60)); + REQUIRE(rows == std::vector{-61}); + } + storage.insert(Entry{60, 13}); + { + auto rows = storage.select(bitwise_or(&Entry::lhs, &Entry::rhs)); + REQUIRE(rows == std::vector{61}); + } + { + auto rows = storage.select(bitwise_and(&Entry::lhs, &Entry::rhs)); + REQUIRE(rows == std::vector{12}); + } + storage.remove_all(); + storage.insert(Entry{60, 2}); + { + auto rows = storage.select(bitwise_shift_left(&Entry::lhs, &Entry::rhs)); + REQUIRE(rows == std::vector{240}); + } + { + auto rows = storage.select(bitwise_shift_right(&Entry::lhs, &Entry::rhs)); + REQUIRE(rows == std::vector{15}); + } + { + auto rows = storage.select(bitwise_not(&Entry::lhs)); + REQUIRE(rows == std::vector{-61}); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/operators/cast.cpp b/libs/sqlite_orm-1.8.2/tests/operators/cast.cpp new file mode 100644 index 0000000..9122f72 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/operators/cast.cpp @@ -0,0 +1,35 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Cast") { + struct Student { + int id; + float scoreFloat; + std::string scoreString; + }; + + auto storage = make_storage("", + make_table("students", + make_column("id", &Student::id, primary_key()), + make_column("score_float", &Student::scoreFloat), + make_column("score_str", &Student::scoreString))); + storage.sync_schema(); + + storage.replace(Student{1, 10.1f, "14.5"}); + + { + auto rows = storage.select(columns(cast(&Student::scoreFloat), cast(&Student::scoreString))); + REQUIRE(rows.size() == 1); + 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(); + REQUIRE(row == "5"); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/operators/glob.cpp b/libs/sqlite_orm-1.8.2/tests/operators/glob.cpp new file mode 100644 index 0000000..43ebf51 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/operators/glob.cpp @@ -0,0 +1,96 @@ +/** + * Obtained from here https://www.tutlane.com/tutorial/sqlite/sqlite-glob-operator + */ +#include +#include +#include // std::vector +#include // std::find_if, std::count + +using namespace sqlite_orm; + +namespace { + struct Employee { + int id = 0; + std::string firstName; + std::string lastName; + float salary = 0; + int deptId = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Employee() = default; + Employee(int id, std::string firstName, std::string lastName, float salary, int deptId) : + id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, salary{salary}, deptId{deptId} {} +#endif + }; +} +TEST_CASE("Glob") { + auto storage = make_storage({}, + make_table("emp_master", + make_column("emp_id", &Employee::id, primary_key().autoincrement()), + make_column("first_name", &Employee::firstName), + make_column("last_name", &Employee::lastName), + make_column("salary", &Employee::salary), + make_column("dept_id", &Employee::deptId))); + storage.sync_schema(); + { + std::vector employees = { + {1, "Honey", "Patel", 10100, 1}, + {2, "Shweta", "Jariwala", 19300, 2}, + {3, "Vinay", "Jariwala", 35100, 3}, + {4, "Jagruti", "Viras", 9500, 2}, + {5, "Shweta", "Rana", 12000, 3}, + {6, "sonal", "Menpara", 13000, 1}, + {7, "Yamini", "Patel", 10000, 2}, + {8, "Khyati", "Shah", 500000, 3}, + {9, "Shwets", "Jariwala", 19400, 2}, + }; + storage.replace_range(employees.begin(), employees.end()); + } + + auto expectIds = [](const std::vector& employees, const std::vector ids) { + for(auto expectedId: ids) { + REQUIRE(find_if(employees.begin(), employees.end(), [expectedId](auto& employee) { + return employee.id == expectedId; + }) != employees.end()); + } + return false; + }; + { + auto employees = storage.get_all(where(glob(&Employee::salary, "1*"))); + REQUIRE(employees.size() == 6); + expectIds(employees, {1, 2, 5, 6, 7, 9}); + } + { + auto employees = storage.get_all(where(glob(&Employee::firstName, "Shwet?"))); + REQUIRE(employees.size() == 3); + expectIds(employees, {2, 5, 9}); + } + { + auto employees = storage.get_all(where(glob(&Employee::lastName, "[A-J]*"))); + REQUIRE(employees.size() == 3); + expectIds(employees, {2, 3, 9}); + } + { + auto employees = storage.get_all(where(glob(&Employee::lastName, "[^A-J]*"))); + REQUIRE(employees.size() == 6); + expectIds(employees, {1, 4, 5, 6, 7, 8}); + } + { + auto rows = storage.select(glob(&Employee::firstName, "S*")); + REQUIRE(rows.size() == 9); + auto trueValuesCount = std::count(rows.begin(), rows.end(), true); + REQUIRE(trueValuesCount == 3); + } + { + auto rows = storage.select(glob(distinct(&Employee::firstName), "S*")); + REQUIRE(rows.size() == 2); + auto trueValuesCount = std::count(rows.begin(), rows.end(), true); + REQUIRE(trueValuesCount == 1); + } + { + auto rows = storage.select(columns(not glob(&Employee::firstName, "S*"))); + REQUIRE(rows.size() == 9); + auto trueValuesCount = std::count(rows.begin(), rows.end(), std::tuple{true}); + REQUIRE(trueValuesCount == 6); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/operators/in.cpp b/libs/sqlite_orm-1.8.2/tests/operators/in.cpp new file mode 100644 index 0000000..2997403 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/operators/in.cpp @@ -0,0 +1,115 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("In") { + using Catch::Matchers::UnorderedEquals; + { + struct User { + int id = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User() = default; + User(int id) : id{id} {} +#endif + + 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()))); + storage.sync_schema(); + storage.replace(User{1}); + storage.replace(User{2}); + storage.replace(User{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); + 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 { + int id; + std::string name; + }; + auto storage = make_storage( + "", + make_table("letters", make_column("id", &Letter::id, primary_key()), make_column("name", &Letter::name))); + storage.sync_schema(); + + storage.replace(Letter{1, "A"}); + storage.replace(Letter{2, "B"}); + storage.replace(Letter{3, "C"}); + + { + auto letters = storage.get_all(where(in(&Letter::id, {1, 2, 3}))); + REQUIRE(letters.size() == 3); + } + { + auto rows = storage.select(columns(&Letter::name), where(in(&Letter::id, {1, 2, 3}))); + REQUIRE(rows.size() == 3); + } + { + auto rows2 = storage.select(&Letter::name, where(in(&Letter::id, {1, 2, 3}))); + REQUIRE(rows2.size() == 3); + } + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/operators/is_null.cpp b/libs/sqlite_orm-1.8.2/tests/operators/is_null.cpp new file mode 100644 index 0000000..473559e --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/operators/is_null.cpp @@ -0,0 +1,30 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Is null") { + struct User { + int id = 0; + std::unique_ptr name; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User() = default; + User(int id, decltype(name) name = nullptr) : id{id}, name{std::move(name)} {} +#endif + }; + auto storage = make_storage( + "", + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name))); + storage.sync_schema(); + + REQUIRE(storage.count() == 0); + storage.replace(User{1, std::make_unique("Sheldon")}); + REQUIRE(storage.count() == 1); + storage.replace(User{2}); + REQUIRE(storage.count() == 2); + storage.replace(User{3, std::make_unique("Leonard")}); + REQUIRE(storage.count() == 3); + REQUIRE(storage.count(where(is_null(&User::name))) == 1); + REQUIRE(storage.count(where(is_not_null(&User::name))) == 2); +} diff --git a/libs/sqlite_orm-1.8.2/tests/operators/like.cpp b/libs/sqlite_orm-1.8.2/tests/operators/like.cpp new file mode 100644 index 0000000..65b68ae --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/operators/like.cpp @@ -0,0 +1,69 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Like operator") { + struct User { + int id = 0; + std::string name; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User() = default; + User(int id, std::string name) : id{id}, name{std::move(name)} {} +#endif + }; + struct Pattern { + std::string value; + }; + + auto storage = make_storage("", + make_table("users", + make_column("id", &User::id, primary_key().autoincrement()), + make_column("name", &User::name)), + make_table("patterns", make_column("value", &Pattern::value))); + storage.sync_schema(); + + storage.insert(User{0, "Sia"}); + storage.insert(User{0, "Stark"}); + storage.insert(User{0, "Index"}); + + auto whereCondition = where(like(&User::name, "S%")); + { + auto users = storage.get_all(whereCondition); + REQUIRE(users.size() == 2); + } + { + auto rows = storage.select(&User::id, whereCondition); + REQUIRE(rows.size() == 2); + } + { + auto rows = storage.select(like("ototo", "ot_to")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == true); + } + { + auto rows = storage.select(not like("ototo", "ot_to")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == false); + } + { + auto rows = storage.select(like(&User::name, "S%a")); + REQUIRE(rows.size() == 3); + REQUIRE(count_if(rows.begin(), rows.end(), [](bool arg) { + return arg == true; + }) == 1); + } + + storage.insert(Pattern{"o%o"}); + { + auto rows = storage.select(like("ototo", &Pattern::value)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == true); + } + { + auto rows = storage.select(like("aaa", &Pattern::value)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == false); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/operators/not_operator.cpp b/libs/sqlite_orm-1.8.2/tests/operators/not_operator.cpp new file mode 100644 index 0000000..78b40c1 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/operators/not_operator.cpp @@ -0,0 +1,73 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Not operator") { + struct Object { + int id = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Object() = default; + Object(int id) : id{id} {} +#endif + }; + + auto storage = make_storage("", make_table("objects", make_column("id", &Object::id, primary_key()))); + storage.sync_schema(); + + storage.replace(Object{2}); + storage.replace(Object{3}); + + std::vector rows; + std::vector expected; + SECTION("is_equal") { + rows = storage.select(&Object::id, where(not is_equal(&Object::id, 1))); + expected.push_back(2); + expected.push_back(3); + } + SECTION("is_not_equal") { + rows = storage.select(&Object::id, where(not is_not_equal(&Object::id, 3))); + expected.push_back(3); + } + SECTION("greater_than") { + rows = storage.select(&Object::id, where(not greater_than(&Object::id, 2))); + expected.push_back(2); + } + SECTION("greater_or_equal") { + rows = storage.select(&Object::id, where(not greater_or_equal(&Object::id, 3))); + expected.push_back(2); + } + SECTION("lesser_than") { + rows = storage.select(&Object::id, where(not lesser_than(&Object::id, 3))); + expected.push_back(3); + } + SECTION("lesser_or_equal") { + rows = storage.select(&Object::id, where(not lesser_or_equal(&Object::id, 2))); + expected.push_back(3); + } + SECTION("in") { + rows = storage.select(&Object::id, where(not in(&Object::id, {1, 2}))); + expected.push_back(3); + } + SECTION("is_null") { + rows = storage.select(&Object::id, where(not is_null(&Object::id))); + expected.push_back(2); + expected.push_back(3); + } + SECTION("is_not_null") { + rows = storage.select(&Object::id, where(not is_not_null(&Object::id))); + } + SECTION("like") { + rows = storage.select(&Object::id, where(not like(cast(&Object::id), "2"))); + expected.push_back(3); + } + SECTION("glob") { + rows = storage.select(&Object::id, where(not like(cast(&Object::id), "3"))); + expected.push_back(2); + } + SECTION("exists") { + rows = storage.select(&Object::id, where(not exists(select(&Object::id, where(is_equal(&Object::id, 2)))))); + } + REQUIRE(rows == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/pointer_passing_interface.cpp b/libs/sqlite_orm-1.8.2/tests/pointer_passing_interface.cpp new file mode 100644 index 0000000..8a6a4a0 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/pointer_passing_interface.cpp @@ -0,0 +1,160 @@ +#include +#include + +using namespace sqlite_orm; +using std::unique_ptr; + +#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +namespace { + struct delete_int64 { + static int64 lastSelectedId; + static bool deleted; + + void operator()(int64* p) const { + // must not double-delete + REQUIRE(!deleted); + + lastSelectedId = *p; + delete p; + deleted = true; + } + }; + + int64 delete_int64::lastSelectedId = -1; + bool delete_int64::deleted = false; + + struct Object { + int64 id = 0; + }; +} + +TEST_CASE("pointer-passing") { + // accept and return a pointer of type "carray" + struct pass_thru_pointer_fn { + using bindable_carray_ptr_t = static_carray_pointer_binding; + + bindable_carray_ptr_t operator()(carray_pointer_arg pv) const { + return rebind_statically(pv); + } + + static const char* name() { + return "pass_thru_pointer"; + } + }; + + // return a pointer of type "carray" + struct make_pointer_fn { + using bindable_carray_ptr_t = carray_pointer_binding; + + bindable_carray_ptr_t operator()() const { + return bindable_pointer(new int64{-1}); + // outline: low-level; must compile + return bindable_carray_pointer(new int64{-1}, delete_int64{}); + } + + static const char* name() { + return "make_pointer"; + } + }; + + // return value from a pointer of type "carray" + struct fetch_from_pointer_fn { + int64 operator()(carray_pointer_arg pv) const { + if(const int64* v = pv) { + return *v; + } + return 0; + } + + static const char* name() { + return "fetch_from_pointer"; + } + }; + + auto storage = + make_storage("", make_table("objects", make_column("id", &Object::id, primary_key().autoincrement()))); + storage.sync_schema(); + + storage.insert(Object{}); + + storage.create_scalar_function>(); + storage.create_scalar_function(); + storage.create_scalar_function(); + storage.create_scalar_function(); + + // test the note_value function + SECTION("note_value, statically_bindable_pointer") { + int64 lastUpdatedId = -1; + storage.update_all(set( + c(&Object::id) = + add(1ll, + func>(&Object::id, statically_bindable_pointer(&lastUpdatedId))))); + REQUIRE(lastUpdatedId == 1); + storage.update_all(set( + c(&Object::id) = + add(1ll, func>(&Object::id, statically_bindable_carray_pointer(&lastUpdatedId))))); + REQUIRE(lastUpdatedId == 2); + } + + // test passing a pointer into another function + SECTION("test_pass_thru, statically_bindable_pointer") { + int64 lastSelectedId = -1; + auto v = storage.select(func>( + &Object::id, + func(statically_bindable_carray_pointer(&lastSelectedId)))); + REQUIRE(v.back() == lastSelectedId); + } + + SECTION("bindable_pointer") { + delete_int64::lastSelectedId = -1; + delete_int64::deleted = false; + + SECTION("unbound is deleted") { + try { + unique_ptr x{new int64(42)}; + auto ast = select(func(bindable_pointer(std::move(x)))); + auto stmt = storage.prepare(std::move(ast)); + throw std::system_error{0, std::system_category()}; + } catch(const std::system_error&) { + } + // unbound pointer value must be deleted in face of exceptions (unregistered sql function) + REQUIRE(delete_int64::deleted == true); + } + + SECTION("deleted with prepared statement") { + { + unique_ptr x{new int64(42)}; + auto ast = select(func(bindable_pointer(std::move(x)))); + auto stmt = storage.prepare(std::move(ast)); + + storage.execute(stmt); + // bound pointer value must not be deleted while executing statements + REQUIRE(delete_int64::deleted == false); + storage.execute(stmt); + REQUIRE(delete_int64::deleted == false); + } + // bound pointer value must be deleted when prepared statement is going out of scope + REQUIRE(delete_int64::deleted == true); + } + + SECTION("ownership transfer") { + auto ast = select(func>(&Object::id, func())); + auto stmt = storage.prepare(std::move(ast)); + + auto results = storage.execute(stmt); + // returned pointers must be deleted by sqlite after executing the statement + REQUIRE(delete_int64::deleted == true); + REQUIRE(results.back() == delete_int64::lastSelectedId); + } + + // test passing a pointer into another function + SECTION("test_pass_thru") { + auto v = storage.select(func>( + &Object::id, + func(bindable_carray_pointer(new int64{-1}, delete_int64{})))); + REQUIRE(delete_int64::deleted == true); + REQUIRE(v.back() == delete_int64::lastSelectedId); + } + } +} +#endif diff --git a/libs/sqlite_orm-1.8.2/tests/pragma_tests.cpp b/libs/sqlite_orm-1.8.2/tests/pragma_tests.cpp new file mode 100644 index 0000000..691e9cb --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/pragma_tests.cpp @@ -0,0 +1,155 @@ +#include +#include // ::remove +#include + +using namespace sqlite_orm; + +TEST_CASE("Journal mode") { + auto filename = "journal_mode.sqlite"; + ::remove(filename); + auto storage = make_storage(filename); + auto storageCopy = storage; + decltype(storage)* stor = nullptr; + SECTION("Storage as is") { + stor = &storage; + }; + SECTION("Storage copy") { + stor = &storageCopy; + } + auto jm = stor->pragma.journal_mode(); + REQUIRE(jm == journal_mode::DELETE); + + for(auto i = 0; i < 2; ++i) { + if(i == 0) { + stor->begin_transaction(); + } + stor->pragma.journal_mode(journal_mode::MEMORY); + jm = stor->pragma.journal_mode(); + REQUIRE(jm == journal_mode::MEMORY); + + if(i == 1) { // WAL cannot be set within a transaction + stor->pragma.journal_mode(journal_mode::WAL); + jm = stor->pragma.journal_mode(); + REQUIRE(jm == journal_mode::WAL); + } + + stor->pragma.journal_mode(journal_mode::OFF); + jm = stor->pragma.journal_mode(); + // REQUIRE(jm == journal_mode::OFF); + // fnc12: dunno why it doesn't work. Probably journal_mode::OFF cannot be set. Anyway its SQLite's issue not sqlite_orm's + + stor->pragma.journal_mode(journal_mode::PERSIST); + jm = stor->pragma.journal_mode(); + REQUIRE(jm == journal_mode::PERSIST); + + stor->pragma.journal_mode(journal_mode::TRUNCATE); + jm = stor->pragma.journal_mode(); + REQUIRE(jm == journal_mode::TRUNCATE); + + if(i == 0) { + stor->rollback(); + } + } +} + +TEST_CASE("Synchronous") { + auto storage = make_storage(""); + const auto value = 1; + storage.pragma.synchronous(value); + REQUIRE(storage.pragma.synchronous() == value); + + storage.begin_transaction(); + + const auto newValue = 2; + try { + storage.pragma.synchronous(newValue); + throw std::runtime_error("Must not fire"); + } catch(const std::system_error&) { + // Safety level may not be changed inside a transaction + REQUIRE(storage.pragma.synchronous() == value); + } + + storage.commit(); +} + +TEST_CASE("User version") { + auto storage = make_storage(""); + auto version = storage.pragma.user_version(); + + storage.pragma.user_version(version + 1); + REQUIRE(storage.pragma.user_version() == version + 1); + + storage.begin_transaction(); + storage.pragma.user_version(version + 2); + REQUIRE(storage.pragma.user_version() == version + 2); + storage.commit(); +} + +TEST_CASE("Auto vacuum") { + auto filename = "autovacuum.sqlite"; + ::remove(filename); + + auto storage = make_storage(filename); + + storage.pragma.auto_vacuum(0); + REQUIRE(storage.pragma.auto_vacuum() == 0); + + storage.pragma.auto_vacuum(1); + REQUIRE(storage.pragma.auto_vacuum() == 1); + + storage.pragma.auto_vacuum(2); + REQUIRE(storage.pragma.auto_vacuum() == 2); +} + +TEST_CASE("busy_timeout") { + auto storage = make_storage({}); + + auto value = storage.pragma.busy_timeout(); + REQUIRE(value == 0); + + storage.pragma.busy_timeout(10); + value = storage.pragma.busy_timeout(); + REQUIRE(value == 10); + + storage.pragma.busy_timeout(20); + value = storage.pragma.busy_timeout(); + REQUIRE(value == 20); + + storage.pragma.busy_timeout(-1); + 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"}); +} + +TEST_CASE("application_id") { + auto filename = "application_id.sqlite"; + ::remove(filename); + + auto storage = make_storage(filename); + storage.pragma.application_id(3); + REQUIRE(storage.pragma.application_id() == 3); +} diff --git a/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/column_names.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/column_names.cpp new file mode 100644 index 0000000..2bb5cbe --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/column_names.cpp @@ -0,0 +1,36 @@ +#include +#include + +using namespace sqlite_orm; + +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + +using namespace std::literals; + +TEST_CASE("column_name_method") { + + struct UserIdAlias : alias_tag { + static const std::string& get() { + static const std::string res = "USER_ID"; + return res; + } + }; + + struct User { + int id = 0; + std::string name; + }; + auto storage = make_storage("column_name.sqlite", + make_table("users", make_column("id", &User::id), make_column("name", &User::name))); + + storage.sync_schema(); + + auto statement = storage.prepare(select(columns(as(&User::id), &User::name))); + std::string id_header{statement.column_name(0)}; + std::string_view name_header = statement.column_name(1); + + REQUIRE(id_header == "USER_ID"s); + REQUIRE(name_header == "name"s); +} + +#endif diff --git a/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get.cpp new file mode 100644 index 0000000..5add455 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get.cpp @@ -0,0 +1,138 @@ +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Prepared get") { + using namespace PreparedStatementTests; + using Catch::Matchers::ContainsSubstring; + using Catch::Matchers::UnorderedEquals; + + 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))); + 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"}); + + { + auto statement = storage.prepare(get(1)); + REQUIRE(get<0>(statement) == 1); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + { + auto user = storage.execute(statement); + REQUIRE(user == User{1, "Team BS"}); + } + { + get<0>(statement) = 2; + REQUIRE(get<0>(statement) == 2); + auto user = storage.execute(statement); + REQUIRE(user == User{2, "Shy'm"}); + } + } + } + { + auto statement = storage.prepare(get(2)); + REQUIRE(get<0>(statement) == 2); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + { + auto user = storage.execute(statement); + REQUIRE(user == User{2, "Shy'm"}); + } + { + get<0>(statement) = 4; + REQUIRE_THROWS_WITH(storage.execute(statement), ContainsSubstring("Not found")); + } + } + } + { + auto statement = storage.prepare(get(3)); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto user = storage.execute(statement); + REQUIRE(user == User{3, "Maître Gims"}); + } + } + { + auto statement = storage.prepare(get(4)); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + REQUIRE_THROWS_WITH(storage.execute(statement), ContainsSubstring("Not found")); + } + } + { + 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)); + REQUIRE(get<0>(statement) == 2); + REQUIRE(get<1>(statement) == 1); + { + auto userAndVisit = storage.execute(statement); + REQUIRE(userAndVisit.userId == 2); + REQUIRE(userAndVisit.visitId == 1); + REQUIRE(userAndVisit.description == "Glad you came"); + } + { + get<0>(statement) = 3; + auto userAndVisit = storage.execute(statement); + REQUIRE(userAndVisit.userId == 3); + REQUIRE(userAndVisit.visitId == 1); + REQUIRE(userAndVisit.description == "Shine on"); + } + } + { + auto id = 1; + auto statement = storage.prepare(get(std::ref(id))); + REQUIRE(get<0>(statement) == id); + REQUIRE(&get<0>(statement) == &id); + { + auto user = storage.execute(statement); + REQUIRE(user == User{1, "Team BS"}); + } + id = 2; + REQUIRE(get<0>(statement) == id); + REQUIRE(&get<0>(statement) == &id); + { + auto user = storage.execute(statement); + REQUIRE(user == User{2, "Shy'm"}); + } + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get_all.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get_all.cpp new file mode 100644 index 0000000..5f40fa7 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get_all.cpp @@ -0,0 +1,220 @@ +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Prepared get all") { + using namespace PreparedStatementTests; + using Catch::Matchers::UnorderedEquals; + + 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))); + 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"}); + + { + auto statement = storage.prepare(get_all()); + auto str = storage.dump(statement); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto rows = storage.execute(statement); + 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"}); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + } + { // by var + auto statement = storage.prepare(get_all(where(lesser_than(&User::id, 3)))); + auto str = storage.dump(statement); + { + using Statement = decltype(statement); + using ExpressionType = Statement::expression_type; + using NodeTuple = internal::node_tuple::type; + STATIC_REQUIRE(std::tuple_size::value == 2); + { + using Arg0 = std::tuple_element<0, NodeTuple>::type; + STATIC_REQUIRE(std::is_same::value); + } + { + using Arg1 = std::tuple_element<1, NodeTuple>::type; + STATIC_REQUIRE(std::is_same::value); + } + } + REQUIRE(get<0>(statement) == 3); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto rows = storage.execute(statement); + std::vector expected; + expected.push_back(User{1, "Team BS"}); + expected.push_back(User{2, "Shy'm"}); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + } + { // by ref + auto id = 3; + auto statement = storage.prepare(get_all(where(lesser_than(&User::id, std::ref(id))))); + auto str = storage.dump(statement); + { + using Statement = decltype(statement); + using ExpressionType = Statement::expression_type; + using NodeTuple = internal::node_tuple::type; + STATIC_REQUIRE(std::tuple_size::value == 2); + { + using Arg0 = std::tuple_element<0, NodeTuple>::type; + STATIC_REQUIRE(std::is_same::value); + } + { + using Arg1 = std::tuple_element<1, NodeTuple>::type; + STATIC_REQUIRE(std::is_same::value); + } + } + REQUIRE(get<0>(statement) == id); + REQUIRE(&get<0>(statement) == &id); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + { + auto rows = storage.execute(statement); + std::vector expected; + expected.push_back(User{1, "Team BS"}); + expected.push_back(User{2, "Shy'm"}); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + id = 2; + REQUIRE(get<0>(statement) == 2); + REQUIRE(&get<0>(statement) == &id); + { + auto rows = storage.execute(statement); + std::vector expected; + expected.push_back(User{1, "Team BS"}); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + } + } + { // by val + auto statement = storage.prepare( + get_all(where(lesser_or_equal(&User::id, 1) and is_equal(&User::name, "Team BS")), limit(10))); + auto str = storage.dump(statement); + REQUIRE(get<0>(statement) == 1); + REQUIRE(strcmp(get<1>(statement), "Team BS") == 0); + REQUIRE(get<2>(statement) == 10); + } + { // by ref + auto id = 1; + std::string name = "Team BS"; + auto statement = storage.prepare( + get_all(where(lesser_or_equal(&User::id, std::ref(id)) and is_equal(&User::name, std::ref(name))))); + auto str = storage.dump(statement); + REQUIRE(get<0>(statement) == 1); + REQUIRE(&get<0>(statement) == &id); + REQUIRE(get<1>(statement) == "Team BS"); + REQUIRE(&get<1>(statement) == &name); + } + { + auto statement = storage.prepare( + get_all(where(lesser_or_equal(&User::id, 2) and (like(&User::name, "T%") or glob(&User::name, "*S"))), + limit(20.0f))); + auto str = storage.dump(statement); + REQUIRE(get<0>(statement) == 2); + REQUIRE(strcmp(get<1>(statement), "T%") == 0); + REQUIRE(strcmp(get<2>(statement), "*S") == 0); + REQUIRE(get<3>(statement) == 20.0f); + } + { + { + { // by val + auto statement = storage.prepare(get_all(where(lesser_than(&User::id, 2)))); + auto str = storage.dump(statement); + std::vector expected; + REQUIRE(get<0>(statement) == 2); + expected.push_back(User{1, "Team BS"}); + { + auto rows = storage.execute(statement); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + + get<0>(statement) = 3; + REQUIRE(get<0>(statement) == 3); + expected.push_back(User{2, "Shy'm"}); + { + auto rows = storage.execute(statement); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + + get<0>(statement) = 4; + REQUIRE(get<0>(statement) == 4); + expected.push_back(User{3, "Maître Gims"}); + { + auto rows = storage.execute(statement); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + } + { // by ref + auto id = 2; + auto statement = storage.prepare(get_all(where(lesser_than(&User::id, std::ref(id))))); + auto str = storage.dump(statement); + std::vector expected; + REQUIRE(get<0>(statement) == 2); + REQUIRE(&get<0>(statement) == &id); + expected.push_back(User{1, "Team BS"}); + { + auto rows = storage.execute(statement); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + + id = 3; + REQUIRE(get<0>(statement) == 3); + REQUIRE(&get<0>(statement) == &id); + expected.push_back(User{2, "Shy'm"}); + { + auto rows = storage.execute(statement); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + + id = 4; + REQUIRE(get<0>(statement) == 4); + REQUIRE(&get<0>(statement) == &id); + expected.push_back(User{3, "Maître Gims"}); + { + auto rows = storage.execute(statement); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + } + } + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get_all_optional.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get_all_optional.cpp new file mode 100644 index 0000000..55db551 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get_all_optional.cpp @@ -0,0 +1,135 @@ +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +TEST_CASE("Prepared get all optional") { + using namespace PreparedStatementTests; + using Catch::Matchers::UnorderedEquals; + + 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))); + 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"}); + + { + auto statement = storage.prepare(get_all_optional()); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto rows = storage.execute(statement); + const std::vector expected = {{1, "Team BS"}, {2, "Shy'm"}, {3, "Maître Gims"}}; + REQUIRE(rows.size() == expected.size()); + REQUIRE(*rows[0] == expected[0]); + REQUIRE(*rows[1] == expected[1]); + REQUIRE(*rows[2] == expected[2]); + } + } + { // by val + auto statement = storage.prepare(get_all_optional(where(lesser_than(&User::id, 3)))); + using Statement = decltype(statement); + using Expression = Statement::expression_type; + using NodeTuple = internal::node_tuple::type; + using BindTuple = internal::bindable_filter_t; + { + STATIC_REQUIRE(std::tuple_size::value == 1); + { + using Arg0 = std::tuple_element_t<0, BindTuple>; + STATIC_REQUIRE(std::is_same::value); + } + } + REQUIRE(get<0>(statement) == 3); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + { + auto rows = storage.execute(statement); + std::vector expected; + expected.push_back(User{1, "Team BS"}); + expected.push_back(User{2, "Shy'm"}); + REQUIRE(rows.size() == expected.size()); + REQUIRE(*rows[0] == expected[0]); + REQUIRE(*rows[1] == expected[1]); + } + { + get<0>(statement) = 2; + REQUIRE(get<0>(statement) == 2); + auto rows = storage.execute(statement); + std::vector expected; + expected.push_back(User{1, "Team BS"}); + REQUIRE(rows.size() == expected.size()); + REQUIRE(*rows[0] == expected[0]); + } + } + } + { // by ref + auto id = 3; + auto statement = storage.prepare(get_all_optional(where(lesser_than(&User::id, std::ref(id))))); + using Statement = decltype(statement); + using Expression = Statement::expression_type; + using NodeTuple = internal::node_tuple::type; + using BindTuple = internal::bindable_filter_t; + { + STATIC_REQUIRE(std::tuple_size::value == 1); + { + using Arg0 = std::tuple_element_t<0, BindTuple>; + STATIC_REQUIRE(std::is_same::value); + } + } + REQUIRE(get<0>(statement) == 3); + REQUIRE(&get<0>(statement) == &id); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + { + auto rows = storage.execute(statement); + const std::vector expected = {{1, "Team BS"}, {2, "Shy'm"}}; + REQUIRE(rows.size() == expected.size()); + REQUIRE(*rows[0] == expected[0]); + REQUIRE(*rows[1] == expected[1]); + } + { + id = 2; + REQUIRE(get<0>(statement) == 2); + REQUIRE(&get<0>(statement) == &id); + auto rows = storage.execute(statement); + const std::vector expected = {{1, "Team BS"}}; + REQUIRE(rows.size() == expected.size()); + REQUIRE(*rows[0] == expected[0]); + } + } + } +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED diff --git a/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get_all_pointer.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get_all_pointer.cpp new file mode 100644 index 0000000..f02326b --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get_all_pointer.cpp @@ -0,0 +1,140 @@ +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Prepared get all pointer") { + using namespace PreparedStatementTests; + using Catch::Matchers::UnorderedEquals; + + 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))); + 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"}); + + SECTION("no conditions") { + auto statement = storage.prepare(get_all_pointer()); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto rows = storage.execute(statement); + 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"}); + REQUIRE(rows.size() == expected.size()); + REQUIRE(*rows[0].get() == expected[0]); + REQUIRE(*rows[1].get() == expected[1]); + REQUIRE(*rows[2].get() == expected[2]); + } + } + SECTION("with conditions by val") { + auto statement = storage.prepare(get_all_pointer(where(lesser_than(&User::id, 3)))); + using Statement = decltype(statement); + using Expression = Statement::expression_type; + using NodeTuple = internal::node_tuple::type; + using BindTuple = internal::bindable_filter_t; + { + STATIC_REQUIRE(std::tuple_size::value == 1); + { + using Arg0 = std::tuple_element_t<0, BindTuple>; + STATIC_REQUIRE(std::is_same::value); + } + } + REQUIRE(get<0>(statement) == 3); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + { + auto rows = storage.execute(statement); + std::vector expected; + expected.push_back(User{1, "Team BS"}); + expected.push_back(User{2, "Shy'm"}); + REQUIRE(rows.size() == expected.size()); + REQUIRE(*rows[0].get() == expected[0]); + REQUIRE(*rows[1].get() == expected[1]); + } + { + get<0>(statement) = 2; + REQUIRE(get<0>(statement) == 2); + auto rows = storage.execute(statement); + std::vector expected; + expected.push_back(User{1, "Team BS"}); + REQUIRE(rows.size() == expected.size()); + REQUIRE(*rows[0].get() == expected[0]); + } + } + } + SECTION("with conditions by ref") { + auto id = 3; + auto statement = storage.prepare(get_all_pointer(where(lesser_than(&User::id, std::ref(id))))); + + using Statement = decltype(statement); + using Expression = Statement::expression_type; + using NodeTuple = internal::node_tuple::type; + using BindTuple = typename internal::bindable_filter_t; + { + STATIC_REQUIRE(std::tuple_size::value == 1); + { + using Arg0 = std::tuple_element_t<0, BindTuple>; + STATIC_REQUIRE(std::is_same::value); + } + } + REQUIRE(get<0>(statement) == 3); + REQUIRE(&get<0>(statement) == &id); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + { + auto rows = storage.execute(statement); + std::vector expected; + expected.push_back(User{1, "Team BS"}); + expected.push_back(User{2, "Shy'm"}); + REQUIRE(rows.size() == expected.size()); + REQUIRE(*rows[0].get() == expected[0]); + REQUIRE(*rows[1].get() == expected[1]); + } + { + id = 2; + REQUIRE(get<0>(statement) == 2); + REQUIRE(&get<0>(statement) == &id); + auto rows = storage.execute(statement); + std::vector expected; + expected.push_back(User{1, "Team BS"}); + REQUIRE(rows.size() == expected.size()); + REQUIRE(*rows[0].get() == expected[0]); + } + } + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get_optional.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get_optional.cpp new file mode 100644 index 0000000..0ce3bd5 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get_optional.cpp @@ -0,0 +1,149 @@ +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +TEST_CASE("Prepared get optional") { + using namespace PreparedStatementTests; + using Catch::Matchers::UnorderedEquals; + + 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))); + 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"}); + + { + auto statement = storage.prepare(get_optional(1)); + REQUIRE(get<0>(statement) == 1); + std::ignore = get<0>(static_cast(statement)); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + { + auto user = storage.execute(statement); + REQUIRE(user.has_value()); + REQUIRE(*user == User{1, "Team BS"}); + } + get<0>(statement) = 2; + REQUIRE(get<0>(statement) == 2); + { + auto user = storage.execute(statement); + REQUIRE(user.has_value()); + REQUIRE(*user == User{2, "Shy'm"}); + } + } + } + { + auto statement = storage.prepare(get_optional(2)); + REQUIRE(get<0>(statement) == 2); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto user = storage.execute(statement); + REQUIRE(user.has_value()); + REQUIRE(*user == User{2, "Shy'm"}); + } + } + { + auto statement = storage.prepare(get_optional(3)); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto user = storage.execute(statement); + REQUIRE(user.has_value()); + REQUIRE(*user == User{3, "Maître Gims"}); + } + } + { + auto statement = storage.prepare(get_optional(4)); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto user = storage.execute(statement); + REQUIRE(!user.has_value()); + } + } + { + 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)); + REQUIRE(get<0>(statement) == 2); + REQUIRE(get<1>(statement) == 1); + { + auto userAndVisit = storage.execute(statement); + REQUIRE(userAndVisit.has_value()); + REQUIRE(userAndVisit->userId == 2); + REQUIRE(userAndVisit->visitId == 1); + REQUIRE(userAndVisit->description == "Glad you came"); + } + { + get<0>(statement) = 3; + auto userAndVisit = storage.execute(statement); + REQUIRE(userAndVisit.has_value()); + REQUIRE(userAndVisit->userId == 3); + REQUIRE(userAndVisit->visitId == 1); + REQUIRE(userAndVisit->description == "Shine on"); + } + } + { + auto id = 1; + auto statement = storage.prepare(get_optional(std::ref(id))); + REQUIRE(get<0>(statement) == id); + REQUIRE(&get<0>(statement) == &id); + { + auto user = storage.execute(statement); + REQUIRE(user.has_value()); + REQUIRE(*user == User{1, "Team BS"}); + } + id = 2; + REQUIRE(get<0>(statement) == id); + REQUIRE(&get<0>(statement) == &id); + { + auto user = storage.execute(statement); + REQUIRE(user.has_value()); + REQUIRE(*user == User{2, "Shy'm"}); + } + } + { + // Testing the direct access to storage_t::get_optional() + auto user = storage.get_optional(2); + REQUIRE(user.has_value()); + REQUIRE(*user == User{2, "Shy'm"}); + } +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED \ No newline at end of file diff --git a/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get_pointer.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get_pointer.cpp new file mode 100644 index 0000000..e5b8c26 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/get_pointer.cpp @@ -0,0 +1,141 @@ +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Prepared get pointer") { + using namespace PreparedStatementTests; + using Catch::Matchers::UnorderedEquals; + + 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))); + 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"}); + + { + auto statement = storage.prepare(get_pointer(1)); + REQUIRE(get<0>(statement) == 1); + std::ignore = get<0>(static_cast(statement)); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + { + auto user = storage.execute(statement); + REQUIRE(user); + REQUIRE(*user == User{1, "Team BS"}); + } + get<0>(statement) = 2; + REQUIRE(get<0>(statement) == 2); + { + auto user = storage.execute(statement); + REQUIRE(user); + REQUIRE(*user == User{2, "Shy'm"}); + } + } + } + { + auto statement = storage.prepare(get_pointer(2)); + REQUIRE(get<0>(statement) == 2); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto user = storage.execute(statement); + REQUIRE(user); + REQUIRE(*user == User{2, "Shy'm"}); + } + } + { + auto statement = storage.prepare(get_pointer(3)); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto user = storage.execute(statement); + REQUIRE(user); + REQUIRE(*user == User{3, "Maître Gims"}); + } + } + { + auto statement = storage.prepare(get_pointer(4)); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + auto user = storage.execute(statement); + REQUIRE(!user); + } + } + { + 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)); + REQUIRE(get<0>(statement) == 2); + REQUIRE(get<1>(statement) == 1); + { + auto userAndVisit = storage.execute(statement); + REQUIRE(userAndVisit); + REQUIRE(userAndVisit->userId == 2); + REQUIRE(userAndVisit->visitId == 1); + REQUIRE(userAndVisit->description == "Glad you came"); + } + { + get<0>(statement) = 3; + auto userAndVisit = storage.execute(statement); + REQUIRE(userAndVisit); + REQUIRE(userAndVisit->userId == 3); + REQUIRE(userAndVisit->visitId == 1); + REQUIRE(userAndVisit->description == "Shine on"); + } + } + { + auto id = 1; + auto statement = storage.prepare(get_pointer(std::ref(id))); + REQUIRE(get<0>(statement) == id); + REQUIRE(&get<0>(statement) == &id); + { + auto user = storage.execute(statement); + REQUIRE(user); + REQUIRE(*user == User{1, "Team BS"}); + } + id = 2; + REQUIRE(get<0>(statement) == id); + REQUIRE(&get<0>(statement) == &id); + { + auto user = storage.execute(statement); + REQUIRE(user); + REQUIRE(*user == User{2, "Shy'm"}); + } + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/insert.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/insert.cpp new file mode 100644 index 0000000..e5e0081 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/insert.cpp @@ -0,0 +1,240 @@ +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +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(std::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)), + 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"))); + } + + 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"))); + } + + 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()); + } + + auto allArtists = storage.get_all(); + decltype(allArtists) expected; + expected.push_back({1, nullptr}); + REQUIRE(allArtists == 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); + } + SECTION("no statement") { + storage.insert(into(), select(columns(&Artist::id, &Artist::name))); + } + + auto allArtistBackups = storage.get_all(); + decltype(allArtistBackups) expected; + expected.push_back(artist1); + expected.push_back(artist4); + REQUIRE_THAT(allArtistBackups, 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") { + //.. + } + 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); + } + } + 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/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/insert_explicit.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/insert_explicit.cpp new file mode 100644 index 0000000..f787f1f --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/insert_explicit.cpp @@ -0,0 +1,114 @@ +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Prepared insert explicit") { + using namespace PreparedStatementTests; + using Catch::Matchers::ContainsSubstring; + using Catch::Matchers::UnorderedEquals; + + 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))); + 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"}); + + SECTION("user two columns") { + 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)); + { + auto insertedId = storage.execute(statement); + REQUIRE(insertedId == user.id); + REQUIRE(storage.count(where(is_equal(&User::name, "Eminem"))) == 1); + } + { + user.id = 6; + user.name = "Nate Dogg"; + auto insertedId = storage.execute(statement); + REQUIRE(insertedId == 6); + REQUIRE(storage.count(where(is_equal(&User::name, "Nate Dogg"))) == 1); + REQUIRE(&user == &get<0>(statement)); + } + } + SECTION("by val") { + auto statement = storage.prepare(insert(user, columns(&User::id, &User::name))); + { + auto insertedId = storage.execute(statement); + REQUIRE(insertedId == user.id); + REQUIRE(storage.count(where(is_equal(&User::name, "Eminem"))) == 1); + } + { + user.id = 6; + user.name = "Nate Dogg"; + REQUIRE_THROWS_WITH(storage.execute(statement), ContainsSubstring("constraint failed")); + + get<0>(statement) = user; + auto insertedId = storage.execute(statement); + REQUIRE(insertedId == user.id); + REQUIRE(storage.count(where(is_equal(&User::name, "Nate Dogg"))) == 1); + REQUIRE(&user != &get<0>(statement)); + } + } + } + SECTION("user id column only") { + User user{4, "Eminem"}; + auto statement = storage.prepare(insert(user, columns(&User::name))); + auto insertedId = storage.execute(statement); + REQUIRE(insertedId == user.id); + } + SECTION("visit") { + { + Visit visit{1, 1, 100000}; + auto statement = storage.prepare(insert(visit, columns(&Visit::id, &Visit::userId, &Visit::time))); + auto insertedId = storage.execute(statement); + REQUIRE(insertedId == visit.id); + } + { + Visit visit{2, 1, defaultVisitTime + 1}; // time must differ + auto statement = storage.prepare(insert(visit, columns(&Visit::id, &Visit::userId))); + auto insertedId = storage.execute(statement); + REQUIRE(insertedId == visit.id); + auto insertedVisit = storage.get(insertedId); + REQUIRE(insertedVisit.id == visit.id); + REQUIRE(insertedVisit.userId == visit.userId); + REQUIRE(insertedVisit.time == defaultVisitTime); + } + { + Visit visit{-1, 2, defaultVisitTime + 2}; + auto statement = storage.prepare(insert(visit, columns(&Visit::userId))); + auto insertedId = storage.execute(statement); + REQUIRE(insertedId == 3); + auto insertedVisit = storage.get(insertedId); + REQUIRE(insertedVisit.id == 3); + REQUIRE(insertedVisit.userId == visit.userId); + REQUIRE(insertedVisit.time == defaultVisitTime); + } + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/insert_range.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/insert_range.cpp new file mode 100644 index 0000000..7cfb118 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/insert_range.cpp @@ -0,0 +1,140 @@ +#include +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Prepared insert range") { + using namespace PreparedStatementTests; + using Catch::Matchers::ContainsSubstring; + using Catch::Matchers::UnorderedEquals; + + 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))); + 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 users; + 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"}); + + SECTION("empty") { + SECTION("strict") { + REQUIRE_THROWS_WITH(storage.prepare(insert_range(users.begin(), users.end())), + ContainsSubstring("incomplete input")); + } + SECTION("container with pointers") { + std::vector> usersPointers; + REQUIRE_THROWS_WITH( + storage.prepare( + insert_range(usersPointers.begin(), usersPointers.end(), &std::unique_ptr::operator*)), + ContainsSubstring("incomplete input")); + } + } + SECTION("one") { + User user{4, "The Weeknd"}; + users.push_back(user); + 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(), &std::unique_ptr::operator*)); + REQUIRE(get<0>(statement) == usersPointers.begin()); + REQUIRE(get<1>(statement) == usersPointers.end()); + storage.execute(statement); + } + expected.push_back(user); + } + SECTION("two") { + User user1{4, "The Weeknd"}; + User user2{5, "Eva"}; + users.push_back(user1); + users.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); + + std::ignore = get<0>(static_cast(statement)); + std::ignore = get<1>(static_cast(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(), &std::unique_ptr::operator*)); + REQUIRE(get<0>(statement) == usersPointers.begin()); + REQUIRE(get<1>(statement) == usersPointers.end()); + storage.execute(statement); + expected.push_back(user1); + expected.push_back(user2); + + 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/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/prepared_common.h b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/prepared_common.h new file mode 100644 index 0000000..f45d302 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/prepared_common.h @@ -0,0 +1,69 @@ +#pragma once + +#include // std::string +#include // std::ignore +#include +#include // std::ostream + +namespace PreparedStatementTests { + struct User { + int id = 0; + std::string name; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User() = default; + User(int id, std::string name) : id{id}, name{std::move(name)} {} +#endif + }; + + struct Visit { + int id = 0; + decltype(User::id) userId; + long time = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Visit() = default; + Visit(int id, decltype(Visit::userId) userId, long time) : id{id}, userId{userId}, time{time} {} +#endif + }; + + struct UserAndVisit { + decltype(User::id) userId; + decltype(Visit::id) visitId; + std::string description; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + UserAndVisit() = default; + UserAndVisit(decltype(UserAndVisit::userId) userId, + decltype(UserAndVisit::visitId) visitId, + std::string description) : + userId{userId}, + visitId{visitId}, description{std::move(description)} {} +#endif + }; + + 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) { + return !(lhs == rhs); + } + + inline void testSerializing(const sqlite_orm::internal::prepared_statement_base& statement) { + auto sql = statement.sql(); + std::ignore = sql; +#if SQLITE_VERSION_NUMBER >= 3014000 + auto expanded = statement.expanded_sql(); + std::ignore = expanded; +#endif +#if SQLITE_VERSION_NUMBER >= 3026000 and defined(SQLITE_ENABLE_NORMALIZE) + auto normalized = statement.normalized_sql(); + std::ignore = normalized; +#endif + } + + inline std::ostream& operator<<(std::ostream& os, const User& user) { + return os << "{" << user.id << ", " << user.name << "}"; + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/remove.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/remove.cpp new file mode 100644 index 0000000..7e0664d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/remove.cpp @@ -0,0 +1,99 @@ +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Prepared remove") { + using namespace PreparedStatementTests; + using Catch::Matchers::UnorderedEquals; + + 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))); + 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"}); + + SECTION("by val") { + { + auto statement = storage.prepare(remove(1)); + std::ignore = get<0>(static_cast(statement)); + REQUIRE(get<0>(statement) == 1); + testSerializing(statement); + storage.execute(statement); + REQUIRE(storage.get_pointer(1) == nullptr); + REQUIRE(storage.get_pointer(2) != nullptr); + REQUIRE(storage.get_pointer(3) != nullptr); + REQUIRE(storage.count() == 2); + } + { + auto statement = storage.prepare(remove(2)); + REQUIRE(get<0>(statement) == 2); + testSerializing(statement); + storage.execute(statement); + REQUIRE(storage.get_pointer(1) == nullptr); + REQUIRE(storage.get_pointer(2) == nullptr); + REQUIRE(storage.get_pointer(3) != nullptr); + REQUIRE(storage.count() == 1); + + get<0>(statement) = 3; + storage.execute(statement); + REQUIRE(storage.get_pointer(1) == nullptr); + REQUIRE(storage.get_pointer(2) == nullptr); + REQUIRE(storage.get_pointer(3) == nullptr); + REQUIRE(storage.count() == 0); + } + } + SECTION("be ref") { + auto id = 1; + auto statement = storage.prepare(remove(std::ref(id))); + REQUIRE(get<0>(statement) == id); + REQUIRE(&get<0>(statement) == &id); + testSerializing(statement); + storage.execute(statement); + REQUIRE(storage.get_pointer(1) == nullptr); + REQUIRE(storage.get_pointer(2) != nullptr); + REQUIRE(storage.get_pointer(3) != nullptr); + + id = 2; + REQUIRE(get<0>(statement) == id); + REQUIRE(&get<0>(statement) == &id); + storage.execute(statement); + REQUIRE(storage.get_pointer(1) == nullptr); + REQUIRE(storage.get_pointer(2) == nullptr); + REQUIRE(storage.get_pointer(3) != nullptr); + + get<0>(statement) = 3; + REQUIRE(id == 3); + REQUIRE(get<0>(statement) == 3); + REQUIRE(&get<0>(statement) == &id); + storage.execute(statement); + REQUIRE(storage.get_pointer(1) == nullptr); + REQUIRE(storage.get_pointer(2) == nullptr); + REQUIRE(storage.get_pointer(3) == nullptr); + REQUIRE(storage.count() == 0); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/remove_all.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/remove_all.cpp new file mode 100644 index 0000000..fafc0fd --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/remove_all.cpp @@ -0,0 +1,183 @@ +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Prepared remove all") { + using namespace PreparedStatementTests; + using Catch::Matchers::UnorderedEquals; + + 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))); + 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"}); + + SECTION("Without conditions") { + auto statement = storage.prepare(remove_all()); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + storage.execute(statement); + REQUIRE(storage.count() == 0); + } + } + SECTION("With conditions") { + SECTION("1") { + SECTION("by val") { + auto statement = storage.prepare(remove_all(where(is_equal(&User::id, 2)))); + REQUIRE(get<0>(statement) == 2); + testSerializing(statement); + storage.execute(statement); + REQUIRE(storage.count() == 2); + { + auto ids = storage.select(&User::id); + decltype(ids) expected; + expected.push_back(1); + expected.push_back(3); + REQUIRE_THAT(ids, UnorderedEquals(expected)); + } + get<0>(statement) = 1; + REQUIRE(get<0>(statement) == 1); + storage.execute(statement); + REQUIRE(storage.count() == 1); + + get<0>(statement) = 3; + REQUIRE(get<0>(statement) == 3); + storage.execute(statement); + REQUIRE(storage.count() == 0); + } + SECTION("by ref") { + auto id = 2; + auto statement = storage.prepare(remove_all(where(is_equal(&User::id, std::ref(id))))); + REQUIRE(get<0>(statement) == 2); + REQUIRE(&get<0>(statement) == &id); + testSerializing(statement); + storage.execute(statement); + REQUIRE(storage.count() == 2); + { + auto ids = storage.select(&User::id); + decltype(ids) expected; + expected.push_back(1); + expected.push_back(3); + REQUIRE_THAT(ids, UnorderedEquals(expected)); + } + id = 1; + REQUIRE(get<0>(statement) == 1); + storage.execute(statement); + REQUIRE(storage.count() == 1); + + id = 3; + REQUIRE(get<0>(statement) == 3); + storage.execute(statement); + REQUIRE(storage.count() == 0); + } + } + SECTION("2") { + SECTION("by val") { + auto statement = storage.prepare( + remove_all(where(is_equal(&User::name, "Shy'm") and lesser_than(&User::id, 10)))); + REQUIRE(strcmp(get<0>(statement), "Shy'm") == 0); + REQUIRE(get<1>(statement) == 10); + testSerializing(statement); + storage.execute(statement); + { + auto ids = storage.select(&User::id); + decltype(ids) expected; + expected.push_back(1); + expected.push_back(3); + REQUIRE_THAT(ids, UnorderedEquals(expected)); + } + get<0>(statement) = "Team BS"; + 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); + { + auto ids = storage.select(&User::id); + decltype(ids) expected; + expected.push_back(3); + REQUIRE_THAT(ids, UnorderedEquals(expected)); + } + get<0>(statement) = "C Bool"; + get<1>(statement) = 30.0f; + REQUIRE(strcmp(get<0>(statement), "C Bool") == 0); + REQUIRE(get<1>(statement) == 30); + storage.execute(statement); + { + auto ids = storage.select(&User::id); + decltype(ids) expected; + expected.push_back(3); + REQUIRE_THAT(ids, UnorderedEquals(expected)); + } + } + SECTION("by ref") { + std::string name = "Shy'm"; + auto id = 10; + auto statement = storage.prepare(remove_all( + where(is_equal(&User::name, std::ref(name)) and lesser_than(&User::id, std::ref(id))))); + REQUIRE(get<0>(statement) == "Shy'm"); + REQUIRE(&get<0>(statement) == &name); + REQUIRE(get<1>(statement) == 10); + REQUIRE(&get<1>(statement) == &id); + testSerializing(statement); + storage.execute(statement); + { + auto ids = storage.select(&User::id); + decltype(ids) expected; + expected.push_back(1); + expected.push_back(3); + REQUIRE_THAT(ids, UnorderedEquals(expected)); + } + name = "Team BS"; + id = 20.0; // assign double to int + REQUIRE(get<0>(statement) == "Team BS"); + REQUIRE(get<1>(statement) == 20); + storage.execute(statement); + { + auto ids = storage.select(&User::id); + decltype(ids) expected; + expected.push_back(3); + REQUIRE_THAT(ids, UnorderedEquals(expected)); + } + name = "C Bool"; + id = 30.0f; + REQUIRE(get<0>(statement) == "C Bool"); + REQUIRE(get<1>(statement) == 30); + storage.execute(statement); + { + auto ids = storage.select(&User::id); + decltype(ids) expected; + expected.push_back(3); + REQUIRE_THAT(ids, UnorderedEquals(expected)); + } + } + } + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/replace.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/replace.cpp new file mode 100644 index 0000000..45f1775 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/replace.cpp @@ -0,0 +1,161 @@ +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Prepared replace") { + using namespace PreparedStatementTests; + using Catch::Matchers::UnorderedEquals; + + 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, default_value(""))), + 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))); + storage.sync_schema(); + + std::vector expected; + User user; + 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("crud") { + 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"}); + 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("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)); + } + 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); + } + } + auto rows = storage.get_all(); + REQUIRE_THAT(rows, UnorderedEquals(expected)); +} diff --git a/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/replace_range.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/replace_range.cpp new file mode 100644 index 0000000..aeca312 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/replace_range.cpp @@ -0,0 +1,131 @@ +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Prepared replace range") { + using namespace PreparedStatementTests; + using Catch::Matchers::UnorderedEquals; + + 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))); + 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 users; + std::vector> userPointers; + std::vector expected; + auto lambda = [](const std::unique_ptr& pointer) -> const User& { + return *pointer; + }; + SECTION("empty") { + using Catch::Matchers::ContainsSubstring; + + expected.push_back(User{1, "Team BS"}); + expected.push_back(User{2, "Shy'm"}); + expected.push_back(User{3, "Maître Gims"}); + SECTION("straight") { + REQUIRE_THROWS_WITH(storage.prepare(replace_range(users.begin(), users.end())), + ContainsSubstring("incomplete input")); + } + SECTION("pointers") { + REQUIRE_THROWS_WITH(storage.prepare(replace_range(userPointers.begin(), userPointers.end(), lambda)), + ContainsSubstring("incomplete input")); + } + } + SECTION("one existing") { + User user{1, "Raye"}; + expected.push_back(user); + expected.push_back(User{2, "Shy'm"}); + expected.push_back(User{3, "Maître Gims"}); + 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"}; + User user2{4, "Bebe Rexha"}; + expected.push_back(User{1, "Team BS"}); + expected.push_back(user); + expected.push_back(User{3, "Maître Gims"}); + expected.push_back(user2); + 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") { + 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; + 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/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/select.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/select.cpp new file mode 100644 index 0000000..255879d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/select.cpp @@ -0,0 +1,344 @@ +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Prepared select") { + using namespace PreparedStatementTests; + using Catch::Matchers::UnorderedEquals; + + 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))); + 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"}); + + SECTION("const access to bindable") { + auto statement = storage.prepare(select(10)); + REQUIRE(get<0>(static_cast(statement)) == 10); + } + SECTION("one simple argument") { + SECTION("by val") { + SECTION("int") { + 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("null") { + auto statement = storage.prepare(select(nullptr)); + REQUIRE(get<0>(statement) == nullptr); + auto rows = storage.execute(statement); + REQUIRE_THAT(rows, UnorderedEquals({nullptr})); + } + SECTION("optional") { +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + auto statement = storage.prepare(select(std::optional())); + REQUIRE(get<0>(statement) == std::nullopt); + auto rows = storage.execute(statement); + REQUIRE_THAT(rows, UnorderedEquals>({std::nullopt})); +#endif + } + } + 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({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})); + } + } + 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("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 == 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)}); + } + } + 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)}); + } + } + 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("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"})); + } + } + } + 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("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})); + } + } + } + SECTION("two columns") { + auto statement = storage.prepare(select(columns(&User::id, &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(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("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("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)); + } + } + } +} + +TEST_CASE("dumping") { + auto storage = make_storage(""); + + std::string value, expected; + + SECTION("expression") { + auto expression = select(1); + SECTION("default") { + value = storage.dump(expression); + expected = "SELECT 1"; + } + SECTION("parametrized") { + value = storage.dump(expression, false); + expected = "SELECT 1"; + } + SECTION("dump") { + value = storage.dump(expression, true); + expected = "SELECT ?"; + } + } + SECTION("statement") { + auto statement = storage.prepare(select(1)); + SECTION("default") { + value = storage.dump(statement); + expected = "SELECT ?"; + } + SECTION("parametrized") { + value = storage.dump(statement, true); + expected = "SELECT ?"; + } + SECTION("dump") { + value = storage.dump(statement, false); + expected = "SELECT 1"; + } + } + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/update.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/update.cpp new file mode 100644 index 0000000..666274d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/update.cpp @@ -0,0 +1,102 @@ +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Prepared update") { + using namespace PreparedStatementTests; + using Catch::Matchers::UnorderedEquals; + + 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))); + 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"}); + + User user{2, "Stromae"}; + SECTION("by ref") { + auto statement = storage.prepare(update(std::ref(user))); + REQUIRE(get<0>(statement) == user); + std::ignore = get<0>(static_cast(statement)); + REQUIRE(&get<0>(statement) == &user); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + storage.execute(statement); + REQUIRE(storage.get(2) == user); + { + auto names = storage.select(&User::name); + REQUIRE(find(names.begin(), names.end(), "Shy'm") == names.end()); + REQUIRE(find(names.begin(), names.end(), "Stromae") != names.end()); + } + user.name = "Sia"; + storage.execute(statement); + REQUIRE(storage.get(2) == user); + { + auto names = storage.select(&User::name); + REQUIRE(find(names.begin(), names.end(), "Shy'm") == names.end()); + REQUIRE(find(names.begin(), names.end(), "Sia") != names.end()); + } + } + } + SECTION("by val") { + auto statement = storage.prepare(update(user)); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + storage.execute(statement); + REQUIRE(storage.get(2) == user); + { + auto names = storage.select(&User::name); + REQUIRE(find(names.begin(), names.end(), "Shy'm") == names.end()); + REQUIRE(find(names.begin(), names.end(), "Stromae") != names.end()); + } + + // try to change original user's name and perform the query. We expect that nothing will change + user.name = "Sia"; + storage.execute(statement); + { + auto names = storage.select(&User::name); + REQUIRE(find(names.begin(), names.end(), "Sia") == names.end()); + REQUIRE(find(names.begin(), names.end(), "Stromae") != names.end()); + } + + // now let's change statement's user's name. This time it musk work! + get<0>(statement).name = "Sia"; + storage.execute(statement); + REQUIRE(storage.count(where(is_equal(&User::name, "Sia"))) == 1); + + get<0>(statement) = {user.id, "Paris"}; + storage.execute(statement); + REQUIRE(storage.count(where(is_equal(&User::name, "Paris"))) == 1); + } + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/update_all.cpp b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/update_all.cpp new file mode 100644 index 0000000..cfa7117 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/prepared_statement_tests/update_all.cpp @@ -0,0 +1,177 @@ +#include +#include + +#include "prepared_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Prepared update all") { + using namespace PreparedStatementTests; + using Catch::Matchers::UnorderedEquals; + + 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))); + 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"}); + + { // by val + auto statement = storage.prepare(update_all(set(assign(&User::name, conc(&User::name, "_"))))); + using Statement = decltype(statement); + using Expression = Statement::expression_type; + using SetTuple = internal::node_tuple::set_tuple; + using SetBind = internal::bindable_filter_t; + STATIC_REQUIRE(std::tuple_size::value == 1); + { + using Arg0 = std::tuple_element_t<0, SetBind>; + STATIC_REQUIRE(std::is_same::value); + } + REQUIRE(strcmp(get<0>(statement), "_") == 0); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + storage.execute(statement); + { + auto names = storage.select(&User::name); + std::vector expected; + expected.push_back("Team BS_"); + expected.push_back("Shy'm_"); + expected.push_back("Maître Gims_"); + REQUIRE_THAT(names, UnorderedEquals(expected)); + } + get<0>(statement) = "123"; + storage.execute(statement); + REQUIRE(strcmp(get<0>(statement), "123") == 0); + { + auto names = storage.select(&User::name); + std::vector expected; + expected.push_back("Team BS_123"); + expected.push_back("Shy'm_123"); + expected.push_back("Maître Gims_123"); + REQUIRE_THAT(names, UnorderedEquals(expected)); + } + auto statement2 = storage.prepare( + update_all(set(c(&User::name) = c(&User::name) || "!"), where(like(&User::name, "T%")))); + REQUIRE(strcmp(get<0>(statement2), "!") == 0); + REQUIRE(strcmp(get<1>(statement2), "T%") == 0); + storage.execute(statement2); + { + auto names = storage.select(&User::name); + std::vector expected; + expected.push_back("Team BS_123!"); + expected.push_back("Shy'm_123"); + expected.push_back("Maître Gims_123"); + REQUIRE_THAT(names, UnorderedEquals(expected)); + } + get<0>(statement2) = "@"; + get<1>(statement2) = "Sh%"; + REQUIRE(strcmp(get<0>(statement2), "@") == 0); + REQUIRE(strcmp(get<1>(statement2), "Sh%") == 0); + storage.execute(statement2); + { + auto names = storage.select(&User::name); + std::vector expected; + expected.push_back("Team BS_123!"); + expected.push_back("Shy'm_123@"); + expected.push_back("Maître Gims_123"); + REQUIRE_THAT(names, UnorderedEquals(expected)); + } + } + } + { // by ref + std::string str = "_"; + auto statement = storage.prepare(update_all(set(assign(&User::name, conc(&User::name, std::ref(str)))))); + using Statement = decltype(statement); + using Expression = Statement::expression_type; + using SetTuple = internal::node_tuple::set_tuple; + using SetBind = internal::bindable_filter_t; + STATIC_REQUIRE(std::tuple_size::value == 1); + { + using Arg0 = std::tuple_element_t<0, SetBind>; + STATIC_REQUIRE(std::is_same::value); + } + REQUIRE(get<0>(statement) == "_"); + REQUIRE(&get<0>(statement) == &str); + testSerializing(statement); + SECTION("nothing") { + //.. + } + SECTION("execute") { + storage.execute(statement); + { + auto names = storage.select(&User::name); + std::vector expected; + expected.push_back("Team BS_"); + expected.push_back("Shy'm_"); + expected.push_back("Maître Gims_"); + REQUIRE_THAT(names, UnorderedEquals(expected)); + } + str = "123"; + storage.execute(statement); + REQUIRE(get<0>(statement) == "123"); + REQUIRE(&get<0>(statement) == &str); + { + auto names = storage.select(&User::name); + std::vector expected; + expected.push_back("Team BS_123"); + expected.push_back("Shy'm_123"); + expected.push_back("Maître Gims_123"); + REQUIRE_THAT(names, UnorderedEquals(expected)); + } + std::string name = "!"; + std::string pattern = "T%"; + auto statement2 = storage.prepare(update_all(set(c(&User::name) = c(&User::name) || std::ref(name)), + where(like(&User::name, std::ref(pattern))))); + REQUIRE(get<0>(statement2) == "!"); + REQUIRE(&get<0>(statement2) == &name); + REQUIRE(get<1>(statement2) == "T%"); + REQUIRE(&get<1>(statement2) == &pattern); + storage.execute(statement2); + { + auto names = storage.select(&User::name); + std::vector expected; + expected.push_back("Team BS_123!"); + expected.push_back("Shy'm_123"); + expected.push_back("Maître Gims_123"); + REQUIRE_THAT(names, UnorderedEquals(expected)); + } + name = "@"; + pattern = "Sh%"; + REQUIRE(get<0>(statement2) == "@"); + REQUIRE(get<1>(statement2) == "Sh%"); + storage.execute(statement2); + { + auto names = storage.select(&User::name); + std::vector expected; + expected.push_back("Team BS_123!"); + expected.push_back("Shy'm_123@"); + expected.push_back("Maître Gims_123"); + REQUIRE_THAT(names, UnorderedEquals(expected)); + } + } + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/private_getters_tests.cpp b/libs/sqlite_orm-1.8.2/tests/private_getters_tests.cpp new file mode 100644 index 0000000..b5a138c --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/private_getters_tests.cpp @@ -0,0 +1,49 @@ +#include +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Issue 343") { + class A { + public: + A() = default; + + A(int id_, std::string name_) : name(std::move(name_)), id(id_) {} + + int getId() const { + return this->id; + } + + void setId(int id_) { + this->id = id_; + } + + std::string name; + + private: + int id = 0; + }; + + auto storage = make_storage( + {}, + make_table("a", make_column("role", &A::getId, &A::setId, primary_key()), make_column("name", &A::name))); + storage.sync_schema(); + + A object(1, "Ototo"); + storage.insert(object); + + storage.replace(object); + + storage.update(object); + + storage.remove(object.getId()); + + std::vector vec{object}; + storage.insert_range(vec.begin(), vec.end()); + + storage.replace_range(vec.begin(), vec.end()); + + auto all = storage.get_all(); + std::ignore = all; +} diff --git a/libs/sqlite_orm-1.8.2/tests/row_id.cpp b/libs/sqlite_orm-1.8.2/tests/row_id.cpp new file mode 100644 index 0000000..2b65c1d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/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/libs/sqlite_orm-1.8.2/tests/select_constraints_tests.cpp b/libs/sqlite_orm-1.8.2/tests/select_constraints_tests.cpp new file mode 100644 index 0000000..c1d4d7d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/select_constraints_tests.cpp @@ -0,0 +1,585 @@ +#include +#include + +using namespace sqlite_orm; + +namespace { + 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; + } + }; +} +TEST_CASE("select constraints") { + using Catch::Matchers::UnorderedEquals; + + 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{paul, allen, teddy, mark, david, kim, 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 +} + +namespace { + struct User1 { + int id = 0; + std::string name; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User1() = default; + User1(int id, std::string name) : id{id}, name{std::move(name)} {} +#endif + }; + + struct Visit1 { + int id = 0; + int userId = 0; + time_t time = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Visit1() = default; + Visit1(int id, int userId, time_t time) : id{id}, userId{userId}, time{time} {} +#endif + }; +} +TEST_CASE("Exists") { + auto storage = make_storage( + "", + make_table("users", make_column("id", &User1::id, primary_key()), make_column("name", &User1::name)), + make_table("visits", + make_column("id", &Visit1::id, primary_key()), + make_column("userId", &Visit1::userId), + make_column("time", &Visit1::time), + foreign_key(&Visit1::userId).references(&User1::id))); + storage.sync_schema(); + + storage.replace(User1{1, "Daddy Yankee"}); + storage.replace(User1{2, "Don Omar"}); + + storage.replace(Visit1{1, 1, 100000}); + storage.replace(Visit1{2, 1, 100001}); + storage.replace(Visit1{3, 1, 100002}); + storage.replace(Visit1{4, 1, 200000}); + storage.replace(Visit1{5, 2, 100000}); + + auto rows = storage.select( + &User1::id, + where(exists(select(&Visit1::id, where(c(&Visit1::time) == 200000 and eq(&Visit1::userId, &User1::id)))))); + REQUIRE(rows.empty() == false); +} + +namespace { + struct User2 { + int id = 0; + std::string firstName; + std::string lastName; + std::string country; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User2() = default; + User2(int id, std::string firstName, std::string lastName, std::string country) : + id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, country{country} {} +#endif + }; + + struct Track2 { + int id = 0; + std::string name; + long milliseconds = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Track2() = default; + Track2(int id, std::string name, long milliseconds) : + id{id}, name{std::move(name)}, milliseconds{milliseconds} {} +#endif + }; + +} +TEST_CASE("Case") { + auto storage = make_storage({}, + make_table("users", + make_column("id", &User2::id, primary_key().autoincrement()), + make_column("first_name", &User2::firstName), + make_column("last_name", &User2::lastName), + make_column("country", &User2::country)), + make_table("tracks", + make_column("trackid", &Track2::id, primary_key().autoincrement()), + make_column("name", &Track2::name), + make_column("milliseconds", &Track2::milliseconds))); + storage.sync_schema(); + + struct GradeAlias : alias_tag { + static const std::string& get() { + static const std::string res = "Grade"; + return res; + } + }; + + { + storage.insert(User2{0, "Roberto", "Almeida", "Mexico"}); + storage.insert(User2{0, "Julia", "Bernett", "USA"}); + storage.insert(User2{0, "Camille", "Bernard", "Argentina"}); + storage.insert(User2{0, "Michelle", "Brooks", "USA"}); + storage.insert(User2{0, "Robet", "Brown", "USA"}); + + auto rows = storage.select( + columns(case_(&User2::country).when("USA", then("Dosmetic")).else_("Foreign").end()), + multi_order_by(order_by(&User2::lastName), order_by(&User2::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_(&User2::country).when("USA", then("Dosmetic")).else_("Foreign").end())), + multi_order_by(order_by(&User2::lastName), order_by(&User2::firstName))); + + verifyRows(rows); + } + { + storage.insert(Track2{0, "For Those About To Rock", 400000}); + storage.insert(Track2{0, "Balls to the Wall", 500000}); + storage.insert(Track2{0, "Fast as a Shark", 200000}); + storage.insert(Track2{0, "Restless and Wild", 100000}); + storage.insert(Track2{0, "Princess of the Dawn", 50000}); + + auto rows = storage.select( + case_() + .when(c(&Track2::milliseconds) < 60000, then("short")) + .when(c(&Track2::milliseconds) >= 60000 and c(&Track2::milliseconds) < 300000, then("medium")) + .else_("long") + .end(), + order_by(&Track2::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(&Track2::milliseconds) < 60000, then("short")) + .when(c(&Track2::milliseconds) >= 60000 and c(&Track2::milliseconds) < 300000, then("medium")) + .else_("long") + .end()), + order_by(&Track2::name)); + verifyRows(rows); + } +} + +namespace { + struct User3 { + int id = 0; + int age = 0; + std::string name; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User3() = default; + User3(int id, int age, std::string name) : id{id}, age{age}, name{std::move(name)} {} +#endif + }; + +} +TEST_CASE("Where") { + auto storage = make_storage("", + make_table("users", + make_column("id", &User3::id, primary_key()), + make_column("age", &User3::age), + make_column("name", &User3::name))); + storage.sync_schema(); + + storage.replace(User3{1, 4, "Jeremy"}); + storage.replace(User3{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(&User3::id) == 1)); + REQUIRE(users4.size() == 1); + REQUIRE(users4.front().id == 1); + + auto users5 = storage.get_all(where(false and c(&User3::id) == 1)); + REQUIRE(users5.size() == 0); + + auto users6 = storage.get_all(where((false or c(&User3::id) == 4) and (false or c(&User3::age) == 18))); + REQUIRE(users6.empty()); +} + +namespace { + struct User4 { + int id = 0; + std::string firstName; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User4() = default; + User4(int id, std::string firstName) : id{id}, firstName{std::move(firstName)} {} +#endif + + bool operator==(const User4& user) const { + return this->id == user.id && this->firstName == user.firstName; + } + }; +} +TEST_CASE("collate") { + auto storage = make_storage({}, + make_table("users", + make_column("id", &User4::id, primary_key()), + make_column("first_name", &User4::firstName))); + storage.sync_schema(); + User4 user1{1, "HELLO"}; + User4 user2{2, "Hello"}; + User4 user3{3, "HEllo"}; + + storage.replace(user1); + storage.replace(user2); + storage.replace(user3); + + auto rows = storage.get_all(where(is_equal(&User4::firstName, "hello").collate_nocase())); + std::vector expected = {user1, user2, user3}; + REQUIRE(rows == expected); +} + +namespace { + struct User5 { + int id = 0; + std::string firstName; + std::string lastName; + long registerTime = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User5() = default; + User5(int id, std::string firstName, std::string lastName, long registerTime) : + id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, registerTime{registerTime} {} +#endif + }; + +} +TEST_CASE("Dynamic order by") { + auto storage = make_storage({}, + make_table("users", + make_column("id", &User5::id, primary_key()), + make_column("first_name", &User5::firstName), + make_column("last_name", &User5::lastName), + make_column("register_time", &User5::registerTime))); + storage.sync_schema(); + + storage.replace(User5{1, "Jack", "Johnson", 100}); + storage.replace(User5{2, "John", "Jackson", 90}); + storage.replace(User5{3, "Elena", "Alexandra", 80}); + storage.replace(User5{4, "Kaye", "Styles", 70}); + + auto orderBy = dynamic_order_by(storage); + std::vector expectedIds; + + SECTION("id") { + auto ob = order_by(&User5::id); + orderBy.push_back(ob); + expectedIds = { + 1, + 2, + 3, + 4, + }; + } + + SECTION("id desc") { + orderBy.push_back(order_by(&User5::id).desc()); + expectedIds = { + 4, + 3, + 2, + 1, + }; + } + + SECTION("firstName") { + orderBy.push_back(order_by(&User5::firstName)); + expectedIds = { + 3, + 1, + 2, + 4, + }; + } + + SECTION("firstName asc") { + orderBy.push_back(order_by(&User5::firstName).asc()); + expectedIds = { + 3, + 1, + 2, + 4, + }; + } + + SECTION("firstName desc") { + orderBy.push_back(order_by(&User5::firstName).desc()); + expectedIds = { + 4, + 2, + 1, + 3, + }; + } + + SECTION("firstName asc + id desc") { + orderBy.push_back(order_by(&User5::firstName).asc()); + orderBy.push_back(order_by(&User5::id).desc()); + expectedIds = { + 3, + 1, + 2, + 4, + }; + } + + SECTION("lastName + firstName + id") { + orderBy.push_back(order_by(&User5::lastName)); + orderBy.push_back(order_by(&User5::firstName)); + orderBy.push_back(order_by(&User5::id)); + expectedIds = { + 3, + 2, + 1, + 4, + }; + } + + SECTION("lastName + firstName desc + id") { + orderBy.push_back(order_by(&User5::lastName)); + orderBy.push_back(order_by(&User5::firstName).desc()); + orderBy.push_back(order_by(&User5::id)); + expectedIds = { + 3, + 2, + 1, + 4, + }; + } + + auto rows = storage.get_all(orderBy); + REQUIRE(rows.size() == 4); + for(size_t i = 0; i < 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{true}; + REQUIRE(rows == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/simple_query.cpp b/libs/sqlite_orm-1.8.2/tests/simple_query.cpp new file mode 100644 index 0000000..e986175 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/simple_query.cpp @@ -0,0 +1,51 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Simple query") { + auto storage = make_storage(""); + { + // SELECT 1 + auto one = storage.select(1); + REQUIRE(one.size() == 1); + REQUIRE(one.front() == 1); + } + { + // SELECT 'ototo' + auto ototo = storage.select("ototo"); + REQUIRE(ototo.size() == 1); + REQUIRE(ototo.front() == "ototo"); + } + { + // SELECT 1 + 1 + auto two = storage.select(c(1) + 1); + REQUIRE(two.size() == 1); + REQUIRE(two.front() == 2); + + auto twoAgain = storage.select(add(1, 1)); + REQUIRE(two == twoAgain); + } + { + // SELECT 10 / 5, 2 * 4 + auto math = storage.select(columns(sqlite_orm::div(10, 5), mul(2, 4))); + REQUIRE(math.size() == 1); + REQUIRE(math.front() == std::make_tuple(2, 8)); + } + { + // SELECT 1, 2 + auto twoRows = storage.select(columns(1, 2)); + REQUIRE(twoRows.size() == 1); + REQUIRE(std::get<0>(twoRows.front()) == 1); + REQUIRE(std::get<1>(twoRows.front()) == 2); + } + { + // SELECT 1, 2 + // UNION ALL + // SELECT 3, 4; + auto twoRowsUnion = storage.select(union_all(select(columns(1, 2)), select(columns(3, 4)))); + REQUIRE(twoRowsUnion.size() == 2); + REQUIRE(twoRowsUnion[0] == std::make_tuple(1, 2)); + REQUIRE(twoRowsUnion[1] == std::make_tuple(3, 4)); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/aggregate_functions.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/aggregate_functions.cpp new file mode 100644 index 0000000..5474ddc --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/aggregate_functions.cpp @@ -0,0 +1,260 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer aggregate functions") { + using internal::serialize; + struct User { + int id = 0; + std::string name; + }; + auto table = make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + + std::string value; + decltype(value) expected; + + SECTION("avg") { + SECTION("simple") { + SECTION("with filter") { + auto expression = avg(&User::id); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((AVG("id")))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(AVG("id"))"; + } + value = serialize(expression, context); + } + SECTION("without filter") { + auto expression = avg(&User::id).filter(where(lesser_than(&User::id, 10))); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((AVG("id")) FILTER (WHERE ("id" < 10)))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(AVG("id") FILTER (WHERE "id" < 10))"; + } + value = serialize(expression, context); + } + } + } + SECTION("count(X)") { + SECTION("simple") { + SECTION("with filter") { + auto expression = count(&User::id); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((COUNT("id")))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(COUNT("id"))"; + } + value = serialize(expression, context); + } + SECTION("without filter") { + auto expression = count(&User::id).filter(where(lesser_than(&User::id, 10))); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((COUNT("id")) FILTER (WHERE ("id" < 10)))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(COUNT("id") FILTER (WHERE "id" < 10))"; + } + value = serialize(expression, context); + } + } + } + SECTION("count(*)") { + SECTION("simple") { + SECTION("with filter") { + auto expression = count(); + value = serialize(expression, context); + expected = R"(COUNT(*))"; + } + SECTION("without filter") { + auto expression = count().filter(where(lesser_than(&User::id, 10))); + value = serialize(expression, context); + expected = R"(COUNT(*) FILTER (WHERE ("id" < 10)))"; + } + } + } + SECTION("group_concat(X)") { + SECTION("simple") { + SECTION("with filter") { + auto expression = group_concat(&User::id); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((GROUP_CONCAT("id")))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(GROUP_CONCAT("id"))"; + } + value = serialize(expression, context); + } + SECTION("without filter") { + auto expression = group_concat(&User::id).filter(where(lesser_than(&User::id, 10))); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((GROUP_CONCAT("id")) FILTER (WHERE ("id" < 10)))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(GROUP_CONCAT("id") FILTER (WHERE "id" < 10))"; + } + value = serialize(expression, context); + } + } + } + SECTION("group_concat(X,Y)") { + SECTION("simple") { + SECTION("with filter") { + auto expression = group_concat(&User::id, "-"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((GROUP_CONCAT("id", '-')))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(GROUP_CONCAT("id", '-'))"; + } + value = serialize(expression, context); + } + SECTION("without filter") { + auto expression = group_concat(&User::id, "-").filter(where(lesser_than(&User::id, 10))); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((GROUP_CONCAT("id", '-')) FILTER (WHERE ("id" < 10)))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(GROUP_CONCAT("id", '-') FILTER (WHERE "id" < 10))"; + } + value = serialize(expression, context); + } + } + } + SECTION("max(X)") { + SECTION("simple") { + SECTION("with filter") { + auto expression = max(&User::id); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((MAX("id")))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(MAX("id"))"; + } + value = serialize(expression, context); + } + SECTION("without filter") { + auto expression = max(&User::id).filter(where(lesser_than(&User::id, 10))); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((MAX("id")) FILTER (WHERE ("id" < 10)))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(MAX("id") FILTER (WHERE "id" < 10))"; + } + value = serialize(expression, context); + } + } + } + SECTION("min(X)") { + SECTION("simple") { + SECTION("with filter") { + auto expression = min(&User::id); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((MIN("id")))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(MIN("id"))"; + } + value = serialize(expression, context); + } + SECTION("without filter") { + auto expression = min(&User::id).filter(where(lesser_than(&User::id, 10))); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((MIN("id")) FILTER (WHERE ("id" < 10)))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(MIN("id") FILTER (WHERE "id" < 10))"; + } + value = serialize(expression, context); + } + } + } + SECTION("sum(X)") { + SECTION("simple") { + SECTION("with filter") { + auto expression = sum(&User::id); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((SUM("id")))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(SUM("id"))"; + } + value = serialize(expression, context); + } + SECTION("without filter") { + auto expression = sum(&User::id).filter(where(lesser_than(&User::id, 10))); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((SUM("id")) FILTER (WHERE ("id" < 10)))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(SUM("id") FILTER (WHERE "id" < 10))"; + } + value = serialize(expression, context); + } + } + } + SECTION("total(X)") { + SECTION("simple") { + SECTION("with filter") { + auto expression = total(&User::id); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((TOTAL("id")))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(TOTAL("id"))"; + } + value = serialize(expression, context); + } + SECTION("without filter") { + auto expression = total(&User::id).filter(where(lesser_than(&User::id, 10))); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"((TOTAL("id")) FILTER (WHERE ("id" < 10)))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"(TOTAL("id") FILTER (WHERE "id" < 10))"; + } + value = serialize(expression, context); + } + } + } + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/alias_extractor.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/alias_extractor.cpp new file mode 100644 index 0000000..462dd89 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/alias_extractor.cpp @@ -0,0 +1,22 @@ +#include +#include + +using namespace sqlite_orm; +using internal::alias_extractor; + +TEST_CASE("alias extractor") { + struct User {}; + + SECTION("column alias") { + REQUIRE(alias_extractor::extract() == "a"); + REQUIRE(alias_extractor::as_alias() == "a"); + } + SECTION("table") { + REQUIRE(alias_extractor::extract() == ""); + REQUIRE(alias_extractor::as_alias() == ""); + } + SECTION("table alias") { + REQUIRE(alias_extractor>::extract() == "a"); + REQUIRE(alias_extractor>::as_alias() == "a"); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/arithmetic_operators.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/arithmetic_operators.cpp new file mode 100644 index 0000000..29248eb --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/arithmetic_operators.cpp @@ -0,0 +1,57 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer arithmetic operators") { + internal::db_objects_tuple<> storage; + internal::serializer_context> context{storage}; + std::string value; + decltype(value) expected; + SECTION("add") { + SECTION("func") { + value = serialize(add(3, 5), context); + } + SECTION("operator") { + value = serialize(c(3) + 5, context); + } + expected = "(3 + 5)"; + } + SECTION("sub") { + SECTION("func") { + value = serialize(sub(5, -9), context); + } + SECTION("operator") { + value = serialize(c(5) - -9, context); + } + expected = "(5 - -9)"; + } + SECTION("mul") { + SECTION("func") { + value = serialize(mul(10, 0.5), context); + } + SECTION("operator") { + value = serialize(c(10) * 0.5, context); + } + expected = "(10 * 0.5)"; + } + SECTION("div") { + SECTION("func") { + value = serialize(sqlite_orm::div(10, 2), context); + } + SECTION("operator") { + value = serialize(c(10) / 2, context); + } + expected = "(10 / 2)"; + } + SECTION("mod") { + SECTION("func") { + value = serialize(mod(20, 3), context); + } + SECTION("operator") { + value = serialize(c(20) % 3, context); + } + expected = "(20 % 3)"; + } + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/ast/excluded.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/ast/excluded.cpp new file mode 100644 index 0000000..d84b64f --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_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 db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + + std::string value; + decltype(value) expected; + SECTION("word") { + auto statement = excluded(&Vocabulary::word); + value = serialize(statement, context); + expected = R"(excluded."word")"; + } + SECTION("count") { + auto statement = excluded(&Vocabulary::count); + value = serialize(statement, context); + expected = R"(excluded."count")"; + } + + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/ast/set.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/ast/set.cpp new file mode 100644 index 0000000..cf7dc36 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/ast/set.cpp @@ -0,0 +1,62 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("set") { + struct User { + int id = 0; + std::string name; + }; + auto table = make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + + std::string value; + std::string expected; + SECTION("one item") { + SECTION("static") { + auto expression = set(assign(&User::id, 5)); + value = internal::serialize(expression, context); + } + SECTION("dynamic") { + auto storage = make_storage("", table); + auto expression = dynamic_set(storage); + expression.push_back(assign(&User::id, 5)); + SECTION("empty") { + //.. + } + SECTION("clear and push_back") { + expression.clear(); + expression.push_back(assign(&User::id, 5)); + } + value = internal::serialize(expression, context); + } + expected = "SET \"id\" = 5"; + } + SECTION("two items") { + SECTION("static") { + auto expression = set(assign(&User::id, 5), assign(&User::name, "ototo")); + value = internal::serialize(expression, context); + } + SECTION("dynamic") { + auto storage = make_storage("", table); + auto expression = dynamic_set(storage); + expression.push_back(assign(&User::id, 5)); + expression.push_back(assign(&User::name, "ototo")); + SECTION("empty") { + //.. + } + SECTION("clear and push_back") { + expression.clear(); + expression.push_back(assign(&User::id, 5)); + expression.push_back(assign(&User::name, "ototo")); + } + value = internal::serialize(expression, context); + } + expected = "SET \"id\" = 5, \"name\" = 'ototo'"; + } + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/ast/upsert_clause.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/ast/upsert_clause.cpp new file mode 100644 index 0000000..1f615e8 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/ast/upsert_clause.cpp @@ -0,0 +1,106 @@ +#include +#include + +using namespace sqlite_orm; + +#if SQLITE_VERSION_NUMBER >= 3024000 +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 db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{vocabularyTable, usersTable}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + + 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 = R"(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 = R"(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 = R"(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 = R"(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 = + R"(ON CONFLICT ("id") DO UPDATE SET "firstname" = excluded."firstname", "lastname" = excluded."lastname")"; + } + REQUIRE(value == expected); +} +#endif diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/base_types.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/base_types.cpp new file mode 100644 index 0000000..142f649 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/base_types.cpp @@ -0,0 +1,60 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer base types") { + internal::db_objects_tuple<> storage; + internal::serializer_context> context{storage}; + std::string stringValue; + decltype(stringValue) expected; + SECTION("std::string") { + std::string str("calma"); + SECTION("no question") { + stringValue = serialize(str, context); + expected = "'calma'"; + } + SECTION("question") { + context.replace_bindable_with_question = true; + stringValue = serialize(str, context); + expected = "?"; + } + } + SECTION("const char *") { + const char* str = "baby"; + SECTION("no question") { + stringValue = serialize(str, context); + expected = "'baby'"; + } + SECTION("question") { + context.replace_bindable_with_question = true; + stringValue = serialize(str, context); + expected = "?"; + } + } +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + SECTION("std::string_view") { + std::string_view str = "agora"; + SECTION("no question") { + stringValue = serialize(str, context); + expected = "'agora'"; + } + SECTION("question") { + context.replace_bindable_with_question = true; + stringValue = serialize(str, context); + expected = "?"; + } + } +#endif + SECTION("blob") { + std::vector blob{}; + stringValue = serialize(blob, context); + expected = "x''"; + } + SECTION("escaped string") { + std::string str{"'"}; + stringValue = serialize(str, context); + expected = "''''"; + } + REQUIRE(stringValue == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/bindables.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/bindables.cpp new file mode 100644 index 0000000..51bd88c --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/bindables.cpp @@ -0,0 +1,297 @@ +#include +#include +#include +#include // std::fill_n +#include +#include + +using std::array; +using std::index_sequence; +using std::index_sequence_for; +using std::make_index_sequence; +using std::nullptr_t; +using std::shared_ptr; +using std::string; +using std::tuple; +using std::tuple_element_t; +using std::tuple_size; +using std::unique_ptr; +using std::vector; +using std::wstring; +using namespace sqlite_orm; + +template +constexpr T get_default() { + return T{}; +} + +template<> +constexpr auto get_default() -> const char* { + return ""; +} + +template<> +constexpr auto get_default() -> const wchar_t* { + return L""; +} + +template<> +constexpr auto get_default>() -> internal::literal_holder { + return {""}; +} + +template<> +constexpr auto get_default>() -> internal::literal_holder { + return {L""}; +} + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +template<> +constexpr auto get_default() -> std::nullopt_t { + return std::nullopt; +} + +template<> +constexpr auto get_default>() -> internal::literal_holder { + return {std::nullopt}; +} +#endif + +template +constexpr Tpl make_default_tuple(index_sequence) { + return {get_default>()...}; +} + +template +constexpr Tpl make_default_tuple() { + return make_default_tuple(make_index_sequence::value>{}); +} + +template +array single_value_array(const char* s) { + array a; + std::fill_n(a.data(), a.size(), s); + return a; +} + +template +using wrap_in_literal = internal::literal_holder; + +inline void require_string(const string& value, const string& expected) { + REQUIRE(value == expected); +} + +template +void require_strings(const array& values, + const array& expected, + index_sequence) { + for(size_t i = 0; i < sizeof...(Idx); ++i) { + require_string(values[i], expected[i]); + } +} + +template +void test_tuple(const tuple& t, const Ctx& ctx, const array& expected) { + require_strings({internal::serialize(get(t), ctx)...}, expected, index_sequence_for{}); +} + +namespace { + struct Custom {}; + template + class StringVeneer : public std::basic_string { + public: + using std::basic_string::basic_string; + }; +} + +namespace sqlite_orm { + template<> + struct statement_binder {}; + template<> + struct field_printer { + std::string operator()(const Custom&) const { + return "custom"; + } + }; +} + +TEST_CASE("bindables") { + using internal::serialize; + + struct Dummy {}; + auto table = make_table("dummy"); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + + SECTION("bindable_builtin_types") { + using Tuple = tuple; + + constexpr Tuple tpl = make_default_tuple(); + + array::value> e{"0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "''", + "null" +#ifndef SQLITE_ORM_OMITS_CODECVT + , + "''" +#endif +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + , + "null" +#endif + }; + + SECTION("dump") { + context.replace_bindable_with_question = false; + test_tuple(tpl, context, e); + } + SECTION("parametrized") { + context.replace_bindable_with_question = true; + test_tuple(tpl, context, single_value_array::value>("?")); + } + SECTION("non-bindable literals") { + context.replace_bindable_with_question = true; + constexpr auto t = make_default_tuple>(); + test_tuple(t, context, e); + } + } + + SECTION("bindable_types") { + using Tuple = tuple, +#endif + unique_ptr, + shared_ptr, + vector, +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + std::optional, + std::optional, +#endif +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::string_view, +#ifndef SQLITE_ORM_OMITS_CODECVT + std::wstring_view, +#endif +#endif + StringVeneer, + Custom, + unique_ptr>; + + Tuple tpl = make_default_tuple(); + + array::value> e{"''", +#ifndef SQLITE_ORM_OMITS_CODECVT + "''", + "''", +#endif + "null", + "null", + "x''", +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + "null", + "null", +#endif +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + "''", +#ifndef SQLITE_ORM_OMITS_CODECVT + "''", +#endif +#endif + "''", + "custom", + "null"}; + + SECTION("dump") { + context.replace_bindable_with_question = false; + test_tuple(tpl, context, e); + } + SECTION("parametrized") { + context.replace_bindable_with_question = true; + test_tuple(tpl, context, single_value_array::value>("?")); + } + SECTION("non-bindable literals") { + context.replace_bindable_with_question = true; + auto t = make_default_tuple>(); + test_tuple(t, context, e); + } + } + +#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED + SECTION("bindable_pointer") { + string value, expected; + context.replace_bindable_with_question = false; + + SECTION("null by itself") { + auto v = statically_bindable_pointer(nullptr); + value = serialize(v, context); + expected = "null"; + } + SECTION("null by itself 2") { + auto v = statically_bindable_pointer(&value); + value = serialize(v, context); + expected = "null"; + } + SECTION("null in select") { + auto ast = select(statically_bindable_pointer(nullptr)); + ast.highest_level = true; + value = serialize(ast, context); + expected = "SELECT null"; + } + SECTION("null as function argument") { + auto ast = func(1, statically_bindable_pointer(nullptr)); + value = serialize(ast, context); + expected = "remember(1, null)"; + } + SECTION("null as function argument 2") { + auto ast = func(1, nullptr); + value = serialize(ast, context); + expected = "remember(1, null)"; + } + + REQUIRE(value == expected); + } +#endif +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/collate.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/collate.cpp new file mode 100644 index 0000000..df60624 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/collate.cpp @@ -0,0 +1,24 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer collate") { + internal::db_objects_tuple<> storage; + internal::serializer_context> context{storage}; + { + auto col = collate_nocase(); + auto value = serialize(col, context); + REQUIRE(value == "COLLATE NOCASE"); + } + { + auto col = collate_binary(); + auto value = serialize(col, context); + REQUIRE(value == "COLLATE BINARY"); + } + { + auto col = collate_rtrim(); + auto value = serialize(col, context); + REQUIRE(value == "COLLATE RTRIM"); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/autoincrement.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/autoincrement.cpp new file mode 100644 index 0000000..08c48cf --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/autoincrement.cpp @@ -0,0 +1,12 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer autoincrement") { + internal::db_objects_tuple<> storage; + internal::serializer_context> context{storage}; + auto autoinc = autoincrement(); + auto value = serialize(autoinc, context); + REQUIRE(value == "AUTOINCREMENT"); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/check.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/check.cpp new file mode 100644 index 0000000..b5d8927 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/check.cpp @@ -0,0 +1,74 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer check") { + SECTION("greater than") { + struct Table { + int col1 = 0; + std::string col2; + int col3 = 0; + }; + auto ch = check(greater_than(&Table::col3, 0)); + auto table = make_table("tablename", + make_column("col1", &Table::col1, primary_key()), + make_column("col2", &Table::col2), + make_column("col3", &Table::col3, ch)); + + using db_objects_t = internal::db_objects_tuple; + + db_objects_t dbObjects{table}; + + using context_t = internal::serializer_context; + + context_t context{dbObjects}; + std::string value; + std::string expected; + SECTION("with parentheses") { + context.use_parentheses = true; + value = serialize(ch, context); + expected = R"(CHECK (("col3" > 0)))"; + } + SECTION("without parentheses") { + context.use_parentheses = false; + value = serialize(ch, context); + expected = R"(CHECK ("col3" > 0))"; + } + REQUIRE(value == expected); + } + SECTION("lesser than") { + struct Book { + int id = 0; + std::string name; + std::string pubName; + int price = 0; + }; + auto ch = check(lesser_than(0, &Book::price)); + auto table = make_table("BOOK", + make_column("Book_id", &Book::id, primary_key()), + make_column("Book_name", &Book::name), + make_column("Pub_name", &Book::pubName), + make_column("PRICE", &Book::price, ch)); + using db_objects_t = internal::db_objects_tuple; + + db_objects_t dbObjects{table}; + + using context_t = internal::serializer_context; + + context_t context{dbObjects}; + std::string value; + std::string expected; + SECTION("with parentheses") { + context.use_parentheses = true; + value = serialize(ch, context); + expected = R"(CHECK ((0 < "PRICE")))"; + } + SECTION("without parentheses") { + context.use_parentheses = false; + value = serialize(ch, context); + expected = R"(CHECK (0 < "PRICE"))"; + } + REQUIRE(value == expected); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/default.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/default.cpp new file mode 100644 index 0000000..0b78cbd --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/default.cpp @@ -0,0 +1,34 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer default") { + internal::db_objects_tuple<> storage; + internal::serializer_context> context{storage}; + std::string value; + decltype(value) expected; + SECTION("int literal") { + auto def = default_value(1); + value = serialize(def, context); + expected = "DEFAULT (1)"; + } + SECTION("string literal") { + auto def = default_value("hi"); + value = serialize(def, context); + expected = "DEFAULT ('hi')"; + } + SECTION("func") { + auto def = default_value(datetime("now")); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "DEFAULT ((DATETIME('now')))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "DEFAULT (DATETIME('now'))"; + } + value = serialize(def, context); + } + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/generated.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/generated.cpp new file mode 100644 index 0000000..3bd14d6 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/generated.cpp @@ -0,0 +1,81 @@ +#include +#include + +using namespace sqlite_orm; + +#if SQLITE_VERSION_NUMBER >= 3031000 +TEST_CASE("issue893") { + struct Object { + std::string name; + int nameLen; + int number; + }; + auto storage = + make_storage("", + make_table("objects", + make_column("name", &Object::name), + make_column("name_len", &Object::nameLen, generated_always_as(length(&Object::name))), + make_column("number", &Object::number))); + storage.sync_schema(); +} + +TEST_CASE("statement_serializer generated") { + using internal::serialize; + struct Type { + int a = 0; + int b = 0; + std::string c; + int d = 0; + std::string e; + }; + auto table = make_table( + "t1", + make_column("a", &Type::a, primary_key()), + make_column("b", &Type::b), + make_column("c", &Type::c), + make_column("d", &Type::d, generated_always_as(&Type::a * sqlite_orm::abs(&Type::b)).virtual_()), + make_column("e", &Type::e, generated_always_as(substr(&Type::c, &Type::b, add(&Type::b, 1))).stored())); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + std::string value; + decltype(value) expected; + SECTION("full") { + auto constraint = generated_always_as(&Type::a * sqlite_orm::abs(&Type::b)); + value = serialize(constraint, context); + expected = R"(GENERATED ALWAYS AS (("a" * (ABS("b")))))"; + } + SECTION("full virtual") { + auto constraint = generated_always_as(&Type::a * sqlite_orm::abs(&Type::b)).virtual_(); + value = serialize(constraint, context); + expected = R"(GENERATED ALWAYS AS (("a" * (ABS("b")))) VIRTUAL)"; + } + SECTION("full stored") { + auto constraint = generated_always_as(&Type::a * sqlite_orm::abs(&Type::b)).stored(); + value = serialize(constraint, context); + expected = R"(GENERATED ALWAYS AS (("a" * (ABS("b")))) STORED)"; + } + SECTION("not full") { + auto constraint = as(&Type::a * sqlite_orm::abs(&Type::b)); + value = serialize(constraint, context); + expected = R"(AS (("a" * (ABS("b")))))"; + } + SECTION("not full virtual") { + auto constraint = as(&Type::a * sqlite_orm::abs(&Type::b)).virtual_(); + value = serialize(constraint, context); + expected = R"(AS (("a" * (ABS("b")))) VIRTUAL)"; + } + SECTION("not full stored") { + auto constraint = as(&Type::a * sqlite_orm::abs(&Type::b)).stored(); + value = serialize(constraint, context); + expected = R"(AS (("a" * (ABS("b")))) STORED)"; + } + SECTION("length") { + auto constraint = generated_always_as(length(&Type::a)); + value = serialize(constraint, context); + expected = R"(GENERATED ALWAYS AS ((LENGTH("a"))))"; + } + REQUIRE(value == expected); +} +#endif diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/primary_key.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/primary_key.cpp new file mode 100644 index 0000000..53e5fad --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/primary_key.cpp @@ -0,0 +1,209 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer primary key") { + std::string value; + decltype(value) expected; + SECTION("empty") { + internal::db_objects_tuple<> storage; + internal::serializer_context> context{storage}; + auto pk = primary_key(); + value = serialize(pk, context); + expected = "PRIMARY KEY"; + } + SECTION("not empty") { + struct User { + int id = 0; + std::string name; + }; + auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + SECTION("single column pk") { + auto pk = primary_key(&User::id); + value = serialize(pk, context); + expected = R"(PRIMARY KEY("id"))"; + } + SECTION("double column pk") { + auto pk = primary_key(&User::id, &User::name); + value = serialize(pk, context); + expected = R"(PRIMARY KEY("id", "name"))"; + } + SECTION("empty pk asc") { + auto pk = primary_key().asc(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC"; + } + SECTION("empty pk asc autoincrement") { + auto pk = primary_key().asc().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC AUTOINCREMENT"; + } + SECTION("empty pk desc") { + auto pk = primary_key().desc(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC"; + } + SECTION("empty pk desc autoincrement") { + auto pk = primary_key().desc().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC AUTOINCREMENT"; + } + SECTION("empty pk on conflict rollback") { + auto pk = primary_key().on_conflict_rollback(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT ROLLBACK"; + } + SECTION("empty pk on conflict abort") { + auto pk = primary_key().on_conflict_abort(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT ABORT"; + } + SECTION("empty pk on conflict fail") { + auto pk = primary_key().on_conflict_fail(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT FAIL"; + } + SECTION("empty pk on conflict ignore") { + auto pk = primary_key().on_conflict_ignore(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT IGNORE"; + } + SECTION("empty pk on conflict replace") { + auto pk = primary_key().on_conflict_replace(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT REPLACE"; + } + SECTION("empty pk asc on conflict rollback") { + auto pk = primary_key().asc().on_conflict_rollback(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT ROLLBACK"; + } + SECTION("empty pk asc on conflict abort") { + auto pk = primary_key().asc().on_conflict_abort(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT ABORT"; + } + SECTION("empty pk asc on conflict fail") { + auto pk = primary_key().asc().on_conflict_fail(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT FAIL"; + } + SECTION("empty pk asc on conflict ignore") { + auto pk = primary_key().asc().on_conflict_ignore(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT IGNORE"; + } + SECTION("empty pk asc on conflict replace") { + auto pk = primary_key().asc().on_conflict_replace(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT REPLACE"; + } + SECTION("empty pk desc on conflict rollback") { + auto pk = primary_key().desc().on_conflict_rollback(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT ROLLBACK"; + } + SECTION("empty pk desc on conflict abort") { + auto pk = primary_key().desc().on_conflict_abort(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT ABORT"; + } + SECTION("empty pk desc on conflict fail") { + auto pk = primary_key().desc().on_conflict_fail(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT FAIL"; + } + SECTION("empty pk desc on conflict ignore") { + auto pk = primary_key().desc().on_conflict_ignore(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT IGNORE"; + } + SECTION("empty pk desc on conflict replace") { + auto pk = primary_key().desc().on_conflict_replace(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT REPLACE"; + } + + SECTION("empty pk on conflict rollback autoincrement") { + auto pk = primary_key().on_conflict_rollback().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT ROLLBACK AUTOINCREMENT"; + } + SECTION("empty pk on conflict abort autoincrement") { + auto pk = primary_key().on_conflict_abort().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT ABORT AUTOINCREMENT"; + } + SECTION("empty pk on conflict fail autoincrement") { + auto pk = primary_key().on_conflict_fail().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT FAIL AUTOINCREMENT"; + } + SECTION("empty pk on conflict ignore autoincrement") { + auto pk = primary_key().on_conflict_ignore().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT IGNORE AUTOINCREMENT"; + } + SECTION("empty pk on conflict replace autoincrement") { + auto pk = primary_key().on_conflict_replace().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT REPLACE AUTOINCREMENT"; + } + SECTION("empty pk asc on conflict rollback autoincrement") { + auto pk = primary_key().asc().on_conflict_rollback().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT ROLLBACK AUTOINCREMENT"; + } + SECTION("empty pk asc on conflict abort autoincrement") { + auto pk = primary_key().asc().on_conflict_abort().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT ABORT AUTOINCREMENT"; + } + SECTION("empty pk asc on conflict fail autoincrement") { + auto pk = primary_key().asc().on_conflict_fail().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT FAIL AUTOINCREMENT"; + } + SECTION("empty pk asc on conflict ignore autoincrement") { + auto pk = primary_key().asc().on_conflict_ignore().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT IGNORE AUTOINCREMENT"; + } + SECTION("empty pk asc on conflict replace autoincrement") { + auto pk = primary_key().asc().on_conflict_replace().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT REPLACE AUTOINCREMENT"; + } + SECTION("empty pk desc on conflict rollback autoincrement") { + auto pk = primary_key().desc().on_conflict_rollback().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT ROLLBACK AUTOINCREMENT"; + } + SECTION("empty pk desc on conflict abort autoincrement") { + auto pk = primary_key().desc().on_conflict_abort().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT ABORT AUTOINCREMENT"; + } + SECTION("empty pk desc on conflict fail autoincrement") { + auto pk = primary_key().desc().on_conflict_fail().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT FAIL AUTOINCREMENT"; + } + SECTION("empty pk desc on conflict ignore autoincrement") { + auto pk = primary_key().desc().on_conflict_ignore().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT IGNORE AUTOINCREMENT"; + } + SECTION("empty pk desc on conflict replace autoincrement") { + auto pk = primary_key().desc().on_conflict_replace().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT REPLACE AUTOINCREMENT"; + } + } + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/unique.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/unique.cpp new file mode 100644 index 0000000..a3768c9 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_constraints/unique.cpp @@ -0,0 +1,12 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer unique") { + internal::db_objects_tuple<> storage; + internal::serializer_context> context{storage}; + auto un = unique(); + auto value = serialize(un, context); + REQUIRE(value == "UNIQUE"); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_names.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_names.cpp new file mode 100644 index 0000000..1e3e11a --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/column_names.cpp @@ -0,0 +1,192 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer column names") { + SECTION("by member field pointer") { + struct User { + int id = 0; + std::string name; + }; + auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); + using db_objects_t = internal::db_objects_tuple; + db_objects_t dbObjects{table}; + { + using context_t = internal::serializer_context; + context_t context{dbObjects}; + SECTION("id") { + SECTION("skip table name") { + auto value = serialize(&User::id, context); + REQUIRE(value == R"("id")"); + } + SECTION("don't skip table name") { + context.skip_table_name = false; + auto value = serialize(&User::id, context); + REQUIRE(value == R"("users"."id")"); + } + } + SECTION("name") { + auto value = serialize(&User::name, context); + REQUIRE(value == R"("name")"); + } + } + } + SECTION("by getters and setters pointers") { + struct User { + + int getId() const { + return this->id; + } + + void setId(int value) { + this->id = value; + } + + const std::string& getName() const { + return this->name; + } + + void setName(std::string value) { + this->name = std::move(value); + } + + private: + int id = 0; + std::string name; + }; + SECTION("getters, setters") { + auto table = make_table("users", + make_column("id", &User::getId, &User::setId), + make_column("name", &User::getName, &User::setName)); + using db_objects_t = internal::db_objects_tuple; + db_objects_t dbObjects{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + std::string value; + decltype(value) expected; + SECTION("id") { + SECTION("getter") { + value = serialize(&User::getId, context); + } + SECTION("setter") { + value = serialize(&User::setId, context); + } + expected = R"("id")"; + } + SECTION("name") { + SECTION("getter") { + value = serialize(&User::getName, context); + } + SECTION("setter") { + value = serialize(&User::setName, context); + } + expected = R"("name")"; + } + REQUIRE(value == expected); + } + SECTION("setters, getters") { // column names by setters and getters pointers (reverse order) + auto table = make_table("users", + make_column("id", &User::setId, &User::getId), + make_column("name", &User::setName, &User::getName)); + using db_objects_t = internal::db_objects_tuple; + db_objects_t dbObjects{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + std::string value; + decltype(value) expected; + SECTION("id") { + SECTION("getter") { + value = serialize(&User::getId, context); + } + SECTION("setter") { + value = serialize(&User::setId, context); + } + expected = R"("id")"; + } + SECTION("name") { + SECTION("getter") { + value = serialize(&User::getName, context); + } + SECTION("setter") { + value = serialize(&User::setName, context); + } + expected = R"("name")"; + } + REQUIRE(value == expected); + } + } + // note: here we test whether the serializer serializes the correct column + // even if the object in question isn't the first in the table definition + SECTION("by explicit column pointer") { + struct Object1 { + int id = 0; + }; + struct Object2 { + int id = 0; + }; + auto table1 = make_table("object1", make_column("id1", &Object1::id)); + auto table2 = make_table("object2", make_column("id2", &Object2::id)); + using db_objects_t = internal::db_objects_tuple; + db_objects_t dbObjects{table1, table2}; + { + using context_t = internal::serializer_context; + context_t context{dbObjects}; + SECTION("name") { + auto value = serialize(column(&Object2::id), context); + REQUIRE(value == R"("id2")"); + } + } + } + SECTION("aliased column") { + struct Object { + int id = 0; + }; + struct Object2 { + int id = 0; + }; + auto table = make_table("object", make_column("id", &Object::id)); + using db_objects_t = internal::db_objects_tuple; + db_objects_t dbObjects{table}; + SECTION("regular") { + using context_t = internal::serializer_context; + context_t context{dbObjects}; + context.skip_table_name = false; + using als = alias_a; + auto value = serialize(alias_column(&Object::id), context); + REQUIRE(value == R"("a"."id")"); + } + } + SECTION("escaped identifiers") { + struct Object1 { + int id = 0; + }; + struct Object2 { + int id = 0; + }; + struct colalias : alias_tag { + static std::string get() { + return R"(a"s)"; + } + }; + auto table1 = make_table(R"(object1"")", make_column(R"(i"d)", &Object1::id)); + auto table2 = make_table(R"(ob"ject2)", make_column(R"(i"d)", &Object2::id)); + using db_objects_t = internal::db_objects_tuple; + db_objects_t dbObjects{table1, table2}; + { + using context_t = internal::serializer_context; + context_t context{dbObjects}; + + using als_d = alias_d; + auto expression = + select(columns(&Object1::id, as(&Object1::id), alias_column(&Object2::id)), + join(using_(&Object1::id)), + multi_order_by(order_by(get()), order_by(alias_column(&Object2::id)))); + expression.highest_level = true; + auto value = serialize(expression, context); + REQUIRE(value == + R"(SELECT "object1"""""."i""d", "object1"""""."i""d" AS "a""s", "d"."i""d" FROM "object1""""" )" + R"(JOIN "ob""ject2" "d" USING ("i""d") ORDER BY "a""s", "d"."i""d")"); + } + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/comparison_operators.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/comparison_operators.cpp new file mode 100644 index 0000000..6b4f41f --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/comparison_operators.cpp @@ -0,0 +1,84 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer comparison operators") { + internal::db_objects_tuple<> storage; + internal::serializer_context> context{storage}; + std::string value; + std::string expected; + SECTION("lesser_than") { + SECTION("func") { + value = serialize(lesser_than(4, 5), context); + } + SECTION("short func") { + value = serialize(lt(4, 5), context); + } + SECTION("operator") { + value = serialize(c(4) < 5, context); + } + expected = "(4 < 5)"; + } + SECTION("lesser_or_equal") { + SECTION("func") { + value = serialize(lesser_or_equal(10, 15), context); + } + SECTION("short func") { + value = serialize(le(10, 15), context); + } + SECTION("operator") { + value = serialize(c(10) <= 15, context); + } + expected = "(10 <= 15)"; + } + SECTION("greater_than") { + SECTION("func") { + value = serialize(greater_than(1, 0.5), context); + } + SECTION("short func") { + value = serialize(gt(1, 0.5), context); + } + SECTION("operator") { + value = serialize(c(1) > 0.5, context); + } + expected = "(1 > 0.5)"; + } + SECTION("greater_or_equal") { + SECTION("func") { + value = serialize(greater_or_equal(10, -5), context); + } + SECTION("short func") { + value = serialize(ge(10, -5), context); + } + SECTION("operator") { + value = serialize(c(10) >= -5, context); + } + expected = "(10 >= -5)"; + } + SECTION("is_equal") { + SECTION("func") { + value = serialize(is_equal("ototo", "Hey"), context); + } + SECTION("short func") { + value = serialize(eq("ototo", "Hey"), context); + } + SECTION("operator") { + value = serialize(c("ototo") == "Hey", context); + } + expected = "('ototo' = 'Hey')"; + } + SECTION("is_not_equal") { + SECTION("func") { + value = serialize(is_not_equal("lala", 7), context); + } + SECTION("short func") { + value = serialize(ne("lala", 7), context); + } + SECTION("operator") { + value = serialize(c("lala") != 7, context); + } + expected = "('lala' != 7)"; + } + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/conditions.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/conditions.cpp new file mode 100644 index 0000000..31a6db6 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/conditions.cpp @@ -0,0 +1,45 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer conditions") { + std::string value, expected; + + SECTION("using") { + struct User { + int64 id; + }; + + auto t1 = make_table("user", make_column("id", &User::id)); + auto storage = internal::db_objects_tuple{t1}; + using db_objects_tuple = decltype(storage); + + internal::serializer_context ctx{storage}; + + SECTION("using column") { + auto expression = using_(&User::id); + value = serialize(expression, ctx); + expected = R"(USING ("id"))"; + } + SECTION("using explicit column") { + auto expression = using_(column(&User::id)); + value = serialize(expression, ctx); + expected = R"(USING ("id"))"; + } + } + SECTION("order by") { + auto storage = internal::db_objects_tuple<>{}; + using db_objects_tuple = decltype(storage); + + internal::serializer_context ctx{storage}; + + SECTION("positional ordinal") { + auto expression = order_by(1); + value = serialize(expression, ctx); + expected = "ORDER BY 1"; + } + } + + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/core_functions.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/core_functions.cpp new file mode 100644 index 0000000..85291e3 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/core_functions.cpp @@ -0,0 +1,401 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer core functions") { + internal::db_objects_tuple<> storage; + internal::serializer_context> context{storage}; + std::string value; + decltype(value) expected; + SECTION("MAX(X,Y)") { + auto expression = max(3, 4); + context.use_parentheses = false; + expected = "MAX(3, 4)"; + value = serialize(expression, context); + } + SECTION("MIN(X,Y)") { + auto expression = min(3, 4); + context.use_parentheses = false; + expected = "MIN(3, 4)"; + value = serialize(expression, context); + } + SECTION("LENGTH") { + auto expression = length("hi"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(LENGTH('hi'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "LENGTH('hi')"; + } + value = serialize(expression, context); + } + SECTION("ABS") { + auto expression = sqlite_orm::abs(-100); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(ABS(-100))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "ABS(-100)"; + } + value = serialize(expression, context); + } + SECTION("LOWER") { + auto expression = lower("dancefloor"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(LOWER('dancefloor'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "LOWER('dancefloor')"; + } + value = serialize(expression, context); + } + SECTION("UPPER") { + auto expression = upper("call"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(UPPER('call'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "UPPER('call')"; + } + value = serialize(expression, context); + } + SECTION("TOTAL_CHANGES") { + auto expression = total_changes(); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(TOTAL_CHANGES())"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "TOTAL_CHANGES()"; + } + value = serialize(expression, context); + } + SECTION("CHANGES") { + auto expression = changes(); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(CHANGES())"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "CHANGES()"; + } + value = serialize(expression, context); + } + SECTION("TRIM(X)") { + auto expression = trim("hey"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(TRIM('hey'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "TRIM('hey')"; + } + value = serialize(expression, context); + } + SECTION("TRIM(X,Y)") { + auto expression = trim("hey", "h"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(TRIM('hey', 'h'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "TRIM('hey', 'h')"; + } + value = serialize(expression, context); + } + SECTION("LTRIM(X)") { + auto expression = ltrim("hey"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(LTRIM('hey'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "LTRIM('hey')"; + } + value = serialize(expression, context); + } + SECTION("LTRIM(X,Y)") { + auto expression = ltrim("hey", "h"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(LTRIM('hey', 'h'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "LTRIM('hey', 'h')"; + } + value = serialize(expression, context); + } + SECTION("RTRIM(X)") { + auto expression = rtrim("hey"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(RTRIM('hey'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "RTRIM('hey')"; + } + value = serialize(expression, context); + } + SECTION("RTRIM(X,Y)") { + auto expression = rtrim("hey", "h"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(RTRIM('hey', 'h'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "RTRIM('hey', 'h')"; + } + value = serialize(expression, context); + } + SECTION("HEX") { + auto expression = hex("love"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(HEX('love'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "HEX('love')"; + } + value = serialize(expression, context); + } + SECTION("QUOTE") { + auto expression = quote("one"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(QUOTE('one'))"; + ; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "QUOTE('one')"; + } + value = serialize(expression, context); + } + SECTION("RANDOMBLOB") { + auto expression = randomblob(5); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(RANDOMBLOB(5))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "RANDOMBLOB(5)"; + } + value = serialize(expression, context); + } + SECTION("INSTR") { + auto expression = instr("hi", "i"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(INSTR('hi', 'i'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "INSTR('hi', 'i')"; + } + value = serialize(expression, context); + } + SECTION("REPLACE") { + auto expression = replace("contigo", "o", "a"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(REPLACE('contigo', 'o', 'a'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "REPLACE('contigo', 'o', 'a')"; + } + value = serialize(expression, context); + } + SECTION("ROUND(X)") { + auto expression = sqlite_orm::round(10.5); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(ROUND(10.5))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "ROUND(10.5)"; + } + value = serialize(expression, context); + } + SECTION("ROUND(X,Y)") { + auto expression = sqlite_orm::round(10.5, 0.5); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(ROUND(10.5, 0.5))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "ROUND(10.5, 0.5)"; + } + value = serialize(expression, context); + } +#if SQLITE_VERSION_NUMBER >= 3007016 + SECTION("CHAR") { + auto expression = char_(40, 45); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(CHAR(40, 45))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "CHAR(40, 45)"; + } + value = serialize(expression, context); + } + SECTION("RANDOM") { + auto expression = sqlite_orm::random(); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(RANDOM())"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "RANDOM()"; + } + value = serialize(expression, context); + } +#endif + SECTION("COALESCE") { + auto expression = coalesce(10, 15); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(COALESCE(10, 15))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "COALESCE(10, 15)"; + } + value = serialize(expression, context); + } + SECTION("DATE") { + auto expression = date("now"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(DATE('now'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "DATE('now')"; + } + value = serialize(expression, context); + } + SECTION("TIME") { + auto expression = time("12:00", "localtime"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(TIME('12:00', 'localtime'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "TIME('12:00', 'localtime')"; + } + value = serialize(expression, context); + } + SECTION("DATETIME") { + auto expression = datetime("now"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(DATETIME('now'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "DATETIME('now')"; + } + value = serialize(expression, context); + } + SECTION("JULIANDAY") { + auto expression = julianday("now"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(JULIANDAY('now'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "JULIANDAY('now')"; + } + value = serialize(expression, context); + } + SECTION("STRFTIME") { + auto expression = strftime("%s", "2014-10-07 02:34:56"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(STRFTIME('%s', '2014-10-07 02:34:56'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "STRFTIME('%s', '2014-10-07 02:34:56')"; + } + value = serialize(expression, context); + } + SECTION("ZEROBLOB") { + auto expression = zeroblob(5); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(ZEROBLOB(5))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "ZEROBLOB(5)"; + } + value = serialize(expression, context); + } + SECTION("SUBSTR(X,Y)") { + auto expression = substr("Zara", 2); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(SUBSTR('Zara', 2))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "SUBSTR('Zara', 2)"; + } + value = serialize(expression, context); + } + SECTION("SUBSTR(X,Y,Z)") { + auto expression = substr("Natasha", 3, 2); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(SUBSTR('Natasha', 3, 2))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "SUBSTR('Natasha', 3, 2)"; + } + value = serialize(expression, context); + } + SECTION("SOUNDEX") { +#ifdef SQLITE_SOUNDEX + auto expression = soundex("Vaso"); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = "(SOUNDEX('Vaso'))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = "SOUNDEX('Vaso')"; + } + value = serialize(expression, context); +#endif + } + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/foreign_key.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/foreign_key.cpp new file mode 100644 index 0000000..1a79ca0 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/foreign_key.cpp @@ -0,0 +1,384 @@ +#include +#include + +#include // std::is_same + +#include "../static_tests/static_tests_storage_traits.h" + +using namespace sqlite_orm; + +#if SQLITE_VERSION_NUMBER >= 3006019 + +TEST_CASE("statement_serializer foreign key") { + SECTION("one to one") { + struct User { + int id = 0; + std::string name; + }; + + struct Visit { + int id = 0; + decltype(User::id) userId; + long time = 0; + }; + + auto usersTable = make_table("users", + make_column("id", &User::id, primary_key().autoincrement()), + make_column("name", &User::name)); + + SECTION("simple") { + auto fk = foreign_key(&Visit::userId).references(&User::id); + + using ForeignKey = decltype(fk); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto visitsTable = make_table("visits", + make_column("id", &Visit::id, primary_key().autoincrement()), + make_column("user_id", &Visit::userId), + make_column("time", &Visit::time), + fk); + + using db_objects_t = internal::db_objects_tuple; + + db_objects_t dbObjects{usersTable, visitsTable}; + + using context_t = internal::serializer_context; + + context_t context{dbObjects}; + auto value = serialize(fk, context); + REQUIRE(value == R"(FOREIGN KEY("user_id") REFERENCES "users"("id"))"); + } + SECTION("on update") { + SECTION("no_action") { + auto fk = foreign_key(&Visit::userId).references(&User::id).on_update.no_action(); + + using ForeignKey = decltype(fk); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto visitsTable = make_table("visits", + make_column("id", &Visit::id, primary_key().autoincrement()), + make_column("user_id", &Visit::userId), + make_column("time", &Visit::time), + fk); + + using db_objects_t = internal::db_objects_tuple; + + db_objects_t dbObjects{usersTable, visitsTable}; + + using context_t = internal::serializer_context; + + context_t context{dbObjects}; + auto value = serialize(fk, context); + REQUIRE(value == R"(FOREIGN KEY("user_id") REFERENCES "users"("id") ON UPDATE NO ACTION)"); + } + SECTION("restrict_") { + auto fk = foreign_key(&Visit::userId).references(&User::id).on_update.restrict_(); + + using ForeignKey = decltype(fk); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto visitsTable = make_table("visits", + make_column("id", &Visit::id, primary_key().autoincrement()), + make_column("user_id", &Visit::userId), + make_column("time", &Visit::time), + fk); + + using db_objects_t = internal::db_objects_tuple; + + db_objects_t dbObjects{usersTable, visitsTable}; + + using context_t = internal::serializer_context; + + context_t context{dbObjects}; + auto value = serialize(fk, context); + REQUIRE(value == R"(FOREIGN KEY("user_id") REFERENCES "users"("id") ON UPDATE RESTRICT)"); + } + SECTION("set_null") { + auto fk = foreign_key(&Visit::userId).references(&User::id).on_update.set_null(); + + using ForeignKey = decltype(fk); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto visitsTable = make_table("visits", + make_column("id", &Visit::id, primary_key().autoincrement()), + make_column("user_id", &Visit::userId), + make_column("time", &Visit::time), + fk); + + using db_objects_t = internal::db_objects_tuple; + + db_objects_t dbObjects{usersTable, visitsTable}; + + using context_t = internal::serializer_context; + + context_t context{dbObjects}; + auto value = serialize(fk, context); + REQUIRE(value == R"(FOREIGN KEY("user_id") REFERENCES "users"("id") ON UPDATE SET NULL)"); + } + SECTION("set_default") { + auto fk = foreign_key(&Visit::userId).references(&User::id).on_update.set_default(); + + using ForeignKey = decltype(fk); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto visitsTable = make_table("visits", + make_column("id", &Visit::id, primary_key().autoincrement()), + make_column("user_id", &Visit::userId), + make_column("time", &Visit::time), + fk); + + using db_objects_t = internal::db_objects_tuple; + + db_objects_t dbObjects{usersTable, visitsTable}; + + using context_t = internal::serializer_context; + + context_t context{dbObjects}; + auto value = serialize(fk, context); + REQUIRE(value == R"(FOREIGN KEY("user_id") REFERENCES "users"("id") ON UPDATE SET DEFAULT)"); + } + SECTION("cascade") { + auto fk = foreign_key(&Visit::userId).references(&User::id).on_update.cascade(); + + using ForeignKey = decltype(fk); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto visitsTable = make_table("visits", + make_column("id", &Visit::id, primary_key().autoincrement()), + make_column("user_id", &Visit::userId), + make_column("time", &Visit::time), + fk); + + using db_objects_t = internal::db_objects_tuple; + + db_objects_t dbObjects{usersTable, visitsTable}; + + using context_t = internal::serializer_context; + + context_t context{dbObjects}; + auto value = serialize(fk, context); + REQUIRE(value == R"(FOREIGN KEY("user_id") REFERENCES "users"("id") ON UPDATE CASCADE)"); + } + } + SECTION("on delete") { + SECTION("no_action") { + auto fk = foreign_key(&Visit::userId).references(&User::id).on_delete.no_action(); + + using ForeignKey = decltype(fk); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto visitsTable = make_table("visits", + make_column("id", &Visit::id, primary_key().autoincrement()), + make_column("user_id", &Visit::userId), + make_column("time", &Visit::time), + fk); + + using db_objects_t = internal::db_objects_tuple; + + db_objects_t dbObjects{usersTable, visitsTable}; + + using context_t = internal::serializer_context; + + context_t context{dbObjects}; + auto value = serialize(fk, context); + REQUIRE(value == R"(FOREIGN KEY("user_id") REFERENCES "users"("id") ON DELETE NO ACTION)"); + } + SECTION("restrict_") { + auto fk = foreign_key(&Visit::userId).references(&User::id).on_delete.restrict_(); + + using ForeignKey = decltype(fk); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto visitsTable = make_table("visits", + make_column("id", &Visit::id, primary_key().autoincrement()), + make_column("user_id", &Visit::userId), + make_column("time", &Visit::time), + fk); + + using db_objects_t = internal::db_objects_tuple; + + db_objects_t dbObjects{usersTable, visitsTable}; + + using context_t = internal::serializer_context; + + context_t context{dbObjects}; + auto value = serialize(fk, context); + REQUIRE(value == R"(FOREIGN KEY("user_id") REFERENCES "users"("id") ON DELETE RESTRICT)"); + } + SECTION("set_null") { + auto fk = foreign_key(&Visit::userId).references(&User::id).on_delete.set_null(); + + using ForeignKey = decltype(fk); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto visitsTable = make_table("visits", + make_column("id", &Visit::id, primary_key().autoincrement()), + make_column("user_id", &Visit::userId), + make_column("time", &Visit::time), + fk); + + using db_objects_t = internal::db_objects_tuple; + + db_objects_t dbObjects{usersTable, visitsTable}; + + using context_t = internal::serializer_context; + + context_t context{dbObjects}; + auto value = serialize(fk, context); + REQUIRE(value == R"(FOREIGN KEY("user_id") REFERENCES "users"("id") ON DELETE SET NULL)"); + } + SECTION("set_default") { + auto fk = foreign_key(&Visit::userId).references(&User::id).on_delete.set_default(); + + using ForeignKey = decltype(fk); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto visitsTable = make_table("visits", + make_column("id", &Visit::id, primary_key().autoincrement()), + make_column("user_id", &Visit::userId), + make_column("time", &Visit::time), + fk); + + using db_objects_t = internal::db_objects_tuple; + + db_objects_t dbObjects{usersTable, visitsTable}; + + using context_t = internal::serializer_context; + + context_t context{dbObjects}; + auto value = serialize(fk, context); + REQUIRE(value == R"(FOREIGN KEY("user_id") REFERENCES "users"("id") ON DELETE SET DEFAULT)"); + } + SECTION("cascade") { + auto fk = foreign_key(&Visit::userId).references(&User::id).on_delete.cascade(); + + using ForeignKey = decltype(fk); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto visitsTable = make_table("visits", + make_column("id", &Visit::id, primary_key().autoincrement()), + make_column("user_id", &Visit::userId), + make_column("time", &Visit::time), + fk); + + using db_objects_t = internal::db_objects_tuple; + + db_objects_t dbObjects{usersTable, visitsTable}; + + using context_t = internal::serializer_context; + + context_t context{dbObjects}; + auto value = serialize(fk, context); + REQUIRE(value == R"(FOREIGN KEY("user_id") REFERENCES "users"("id") ON DELETE CASCADE)"); + } + } + } + SECTION("one to explicit one") { + struct Object { + int id = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Object() = default; + Object(int id) : id{id} {} +#endif + }; + + struct User : Object { + std::string name; + + User(decltype(id) id_, decltype(name) name_) : Object{id_}, name(std::move(name_)) {} + }; + + struct Token : Object { + std::string token; + int usedId = 0; + + Token(decltype(id) id_, decltype(token) token_, decltype(usedId) usedId_) : + Object{id_}, token(std::move(token_)), usedId(usedId_) {} + }; + auto fk = foreign_key(&Token::usedId).references(column(&User::id)); + + using ForeignKey = decltype(fk); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(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", + make_column("id", &Token::id, primary_key()), + make_column("token", &Token::token), + make_column("user_id", &Token::usedId), + fk); + + using db_objects_t = internal::db_objects_tuple; + + db_objects_t dbObjects{usersTable, tokensTable}; + + using context_t = internal::serializer_context; + + context_t context{dbObjects}; + auto value = serialize(fk, context); + REQUIRE(value == R"(FOREIGN KEY("user_id") REFERENCES "users"("id"))"); + } + SECTION("composite key") { + using namespace sqlite_orm::internal::storage_traits; + + struct User { + int id = 0; + std::string firstName; + std::string lastName; + }; + + struct UserVisit { + int userId = 0; + std::string userFirstName; + time_t time = 0; + }; + + auto fk = foreign_key(&UserVisit::userId, &UserVisit::userFirstName).references(&User::id, &User::firstName); + + using ForeignKey = decltype(fk); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(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_REQUIRE(table_foreign_keys_count::value == 0); + STATIC_REQUIRE(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_REQUIRE(table_foreign_keys_count::value == 1); + STATIC_REQUIRE(table_foreign_keys_count::value == 0); + + using db_objects_t = internal::db_objects_tuple; + + db_objects_t dbObjects{usersTable, visitsTable}; + + using context_t = internal::serializer_context; + + context_t context{dbObjects}; + auto value = serialize(fk, context); + REQUIRE(value == R"(FOREIGN KEY("user_id", "user_first_name") REFERENCES "users"("id", "first_name"))"); + } +} + +#endif // SQLITE_VERSION_NUMBER diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/indexed_column.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/indexed_column.cpp new file mode 100644 index 0000000..2c49550 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/indexed_column.cpp @@ -0,0 +1,46 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer indexed_column") { + struct User { + int id = 0; + std::string name; + }; + auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + { + auto column = indexed_column(&User::id); + auto value = internal::serialize(column, context); + REQUIRE(value == R"("id")"); + } + { + auto column = indexed_column(&User::id).asc(); + auto value = internal::serialize(column, context); + REQUIRE(value == R"("id" ASC)"); + } + { + auto column = indexed_column(&User::id).desc(); + auto value = internal::serialize(column, context); + REQUIRE(value == R"("id" DESC)"); + } + { + auto column = indexed_column(&User::id).collate("BINARY"); + auto value = internal::serialize(column, context); + REQUIRE(value == R"("id" COLLATE BINARY)"); + } + { + auto column = indexed_column(&User::name).collate("BINARY").asc(); + auto value = internal::serialize(column, context); + REQUIRE(value == R"("name" COLLATE BINARY ASC)"); + } + { + auto column = indexed_column(&User::name).collate("OTHER").desc(); + auto value = internal::serialize(column, context); + REQUIRE(value == R"("name" COLLATE OTHER DESC)"); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/logical_operators.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/logical_operators.cpp new file mode 100644 index 0000000..679252b --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/logical_operators.cpp @@ -0,0 +1,89 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer 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 db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + 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 = R"((("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 = R"((("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 = R"("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 = R"("id" NOT IN (1, 2, 3))"; + } + SECTION("dynamic in") { + auto inValue = in(&User::id, {1, 2, 3}); + stringValue = internal::serialize(inValue, context); + expected = R"("id" IN (1, 2, 3))"; + } + SECTION("dynamic not in") { + auto inValue = not_in(&User::id, {1, 2, 3}); + stringValue = internal::serialize(inValue, context); + expected = R"("id" NOT IN (1, 2, 3))"; + } + } + REQUIRE(stringValue == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/rowid.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/rowid.cpp new file mode 100644 index 0000000..8d455cf --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/rowid.cpp @@ -0,0 +1,13 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer rowid") { + { + internal::db_objects_tuple<> storage; + internal::serializer_context> context{storage}; + auto value = serialize(rowid(), context); + REQUIRE(value == "rowid"); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/schema/index.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/schema/index.cpp new file mode 100644 index 0000000..4e28b56 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/schema/index.cpp @@ -0,0 +1,67 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer index") { + struct User { + int id = 0; + std::string name; + }; + auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + std::string value; + decltype(value) expected; + SECTION("simple") { + auto index = make_index("id_index", &User::id); + value = internal::serialize(index, context); + expected = R"(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 = R"(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 = R"(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 = R"(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 = R"(CREATE INDEX IF NOT EXISTS "my_index" ON "users" ("id" COLLATE compare ASC))"; + } + SECTION("ifnull") { + auto index = make_index("idx", &User::id, ifnull(&User::name, "")); + value = internal::serialize(index, context); + expected = R"(CREATE INDEX IF NOT EXISTS "idx" ON "users" ("id", IFNULL("name", '')))"; + } + SECTION("where") { + auto index = make_index("idx", &User::id, where(is_not_null(&User::id))); + value = internal::serialize(index, context); + expected = R"(CREATE INDEX IF NOT EXISTS "idx" ON "users" ("id") WHERE ("id" IS NOT NULL))"; + } +#ifdef SQLITE_ENABLE_JSON1 + SECTION("json") { + SECTION("implicit") { + auto index = make_index("idx", json_extract(&User::name, "$.field")); + value = internal::serialize(index, context); + } + SECTION("explicit") { + auto index = make_index("idx", indexed_column(json_extract(&User::name, "$.field"))); + value = internal::serialize(index, context); + } + expected = "CREATE INDEX IF NOT EXISTS \"idx\" ON \"users\" (JSON_EXTRACT(\"name\", '$.field'))"; + } +#endif // SQLITE_ENABLE_JSON1 + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/schema/new_old.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/schema/new_old.cpp new file mode 100644 index 0000000..fcdf071 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/schema/new_old.cpp @@ -0,0 +1,30 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer new/old") { + 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 db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + std::string value; + decltype(value) expected; + SECTION("new") { + auto expression = new_(&User::id); + value = serialize(expression, context); + expected = R"(NEW."id")"; + } + SECTION("old") { + auto expression = old(&User::id); + value = serialize(expression, context); + expected = R"(OLD."id")"; + } + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/schema/raise.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/schema/raise.cpp new file mode 100644 index 0000000..15430e7 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/schema/raise.cpp @@ -0,0 +1,35 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer raise") { + using internal::serialize; + + std::string value; + decltype(value) expected; + + internal::db_objects_tuple<> storage; + internal::serializer_context> context{storage}; + SECTION("ignore") { + auto expression = raise_ignore(); + value = serialize(expression, context); + expected = "RAISE(IGNORE)"; + } + SECTION("rollback") { + auto expression = raise_rollback("no rap"); + value = serialize(expression, context); + expected = "RAISE(ROLLBACK, 'no rap')"; + } + SECTION("abort") { + auto expression = raise_abort("no rap"); + value = serialize(expression, context); + expected = "RAISE(ABORT, 'no rap')"; + } + SECTION("fail") { + auto expression = raise_fail("no rap"); + value = serialize(expression, context); + expected = "RAISE(FAIL, 'no rap')"; + } + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/schema/trigger.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/schema/trigger.cpp new file mode 100644 index 0000000..28c9d00 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/schema/trigger.cpp @@ -0,0 +1,43 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer trigger") { + using internal::serialize; + struct Lead { + int id = 0; + std::string firstName; + std::string lastName; + std::string email; + std::string phone; + }; + auto table = make_table("leads", + make_column("id", &Lead::id, primary_key()), + make_column("first_name", &Lead::firstName), + make_column("last_name", &Lead::lastName), + make_column("email", &Lead::email), + make_column("phone", &Lead::phone)); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + std::string value; + decltype(value) expected; + SECTION("without for each row") { + auto expression = make_trigger("validate_email_before_insert_leads", + before() + .insert() + .on() + .begin(select(case_() + .when(not like(new_(&Lead::email), "%_@__%.__%"), + then(raise_abort("Invalid email address"))) + .end())) + .end()); + value = serialize(expression, context); + expected = + R"(CREATE TRIGGER IF NOT EXISTS "validate_email_before_insert_leads" BEFORE INSERT ON "leads" BEGIN SELECT )" + R"(CASE WHEN NOT (NEW."email" LIKE '%_@__%.__%' ) THEN RAISE(ABORT, 'Invalid email address') END; END)"; + } + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/select_constraints.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/select_constraints.cpp new file mode 100644 index 0000000..99fafce --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/select_constraints.cpp @@ -0,0 +1,142 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer 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 db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + internal::serializer_context context{dbObjects}; + + std::string value; + decltype(value) expected; + SECTION("columns") { + auto expression = columns(&User::id, &User::name); + SECTION("use_parentheses") { + context.use_parentheses = true; + expected = R"(("id", "name"))"; + } + SECTION("!use_parentheses") { + context.use_parentheses = false; + expected = R"("id", "name")"; + } + value = serialize(expression, context); + } + SECTION("into") { + auto expression = into(); + value = serialize(expression, context); + expected = R"(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 = R"(FROM "users")"; + } + SECTION("with alias") { + auto expression = from>(); + value = serialize(expression, context); + expected = R"(FROM "users" "u")"; + } + } + // tests whether the statement serializer for a select with joins + // properly deduplicates the table names when no explicit from is used + SECTION("deduplicated table names") { + struct UserProps { + int id = 0; + std::string name; + }; + auto table2 = + make_table("user_props", make_column("id", &UserProps::id), make_column("name", &UserProps::name)); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table, table2}; + internal::serializer_context context{dbObjects}; + context.use_parentheses = false; + + SECTION("left join") { + auto expression = select(asterisk(), left_join(using_(&UserProps::id))); + value = serialize(expression, context); + expected = R"(SELECT "users".* FROM "users" LEFT JOIN "user_props" USING ("id"))"; + } + SECTION("join") { + auto expression = select(asterisk(), join(using_(&UserProps::id))); + value = serialize(expression, context); + expected = R"(SELECT "users".* FROM "users" JOIN "user_props" USING ("id"))"; + } + SECTION("left outer join") { + auto expression = select(asterisk(), left_outer_join(using_(&UserProps::id))); + value = serialize(expression, context); + expected = R"(SELECT "users".* FROM "users" LEFT OUTER JOIN "user_props" USING ("id"))"; + } + SECTION("inner join") { + auto expression = select(asterisk(), inner_join(using_(&UserProps::id))); + value = serialize(expression, context); + expected = R"(SELECT "users".* FROM "users" INNER JOIN "user_props" USING ("id"))"; + } + SECTION("cross join") { + auto expression = select(asterisk(), cross_join()); + value = serialize(expression, context); + expected = R"(SELECT "users".* FROM "users" CROSS JOIN "user_props")"; + } + SECTION("natural join") { + auto expression = select(asterisk(), natural_join()); + value = serialize(expression, context); + expected = R"(SELECT "users".* FROM "users" NATURAL JOIN "user_props")"; + } + } + 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 = R"(EVEN("id"))"; + } + SECTION("exists") { + // EXISTS must use parentheses in a new context + context.use_parentheses = false; + auto expression = exists(select(1)); + value = serialize(expression, context); + expected = "EXISTS (SELECT 1)"; + } + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/insert_replace.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/insert_replace.cpp new file mode 100644 index 0000000..e30be74 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/insert_replace.cpp @@ -0,0 +1,409 @@ +#include +#include + +using namespace sqlite_orm; + +template +static void assert_same(const E1&, const E2&) { + STATIC_REQUIRE(std::is_same::value); +} + +TEST_CASE("statement_serializer insert/replace") { + using internal::serialize; + struct User { + int id = 0; + std::string name; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User() = default; + User(int id, std::string name) : id{id}, name{std::move(name)} {} +#endif + }; + struct UserBackup { + int id = 0; + std::string name; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + UserBackup() = default; + UserBackup(int id, std::string name) : id{id}, name{std::move(name)} {} +#endif + }; + 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 db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table, table2}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + std::string value; + decltype(value) expected; + SECTION("replace") { + SECTION("object") { + User user{5, "Gambit"}; + auto statement = replace(user); + SECTION("question marks") { + context.replace_bindable_with_question = true; + expected = R"(REPLACE INTO "users" ("id", "name") VALUES (?, ?))"; + } + SECTION("no question marks") { + context.replace_bindable_with_question = false; + expected = R"(REPLACE INTO "users" ("id", "name") VALUES (5, 'Gambit'))"; + } + value = serialize(statement, context); + } + SECTION("raw") { + 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 = R"(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 = R"(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 = R"(REPLACE INTO "users" DEFAULT VALUES)"; + } + SECTION("select") { + auto statement = replace(into(), select(columns(&UserBackup::id, &UserBackup::name))); + value = serialize(statement, context); + expected = + R"(REPLACE INTO "users" SELECT "users_backup"."id", "users_backup"."name" FROM "users_backup")"; + } + } + SECTION("range") { + context.replace_bindable_with_question = false; + + std::vector users(1); + SECTION("objects") { + auto expression = replace_range(users.begin(), users.end()); + // deduced object type + assert_same(replace_range(users.begin(), users.end()), expression); + // deduced object type + assert_same(replace_range(users.begin(), users.end(), polyfill::identity{}), expression); + assert_same(replace_range(users.begin(), users.end(), polyfill::identity{}), expression); + value = serialize(expression, context); + expected = R"(REPLACE INTO "users" ("id", "name") VALUES (?, ?))"; + } + SECTION("indirected") { + std::vector> userPtrs; + userPtrs.push_back(std::make_unique(users.front())); + auto expression = + replace_range(userPtrs.begin(), userPtrs.end(), &std::unique_ptr::operator*); + // deduced object type + assert_same(replace_range(userPtrs.begin(), userPtrs.end(), &std::unique_ptr::operator*), + expression); + value = serialize(expression, context); + expected = R"(REPLACE INTO "users" ("id", "name") VALUES (?, ?))"; + } + SECTION("wrapper") { + std::vector> userRefs{std::ref(users.front())}; + SECTION("identity") { + auto expression = replace_range(userRefs.begin(), userRefs.end()); + assert_same(replace_range(userRefs.begin(), userRefs.end(), polyfill::identity{}), + expression); + value = serialize(expression, context); + expected = R"(REPLACE INTO "users" ("id", "name") VALUES (?, ?))"; + } + SECTION("projected") { + auto expression = + replace_range(userRefs.begin(), userRefs.end(), &std::reference_wrapper::get); +#ifdef _MSC_VER /* `&std::reference_wrapper::get` is only invocable with Microsoft STL */ + // deduced object type + assert_same(replace_range(userRefs.begin(), userRefs.end(), &std::reference_wrapper::get), + expression); +#endif + value = serialize(expression, context); + expected = R"(REPLACE INTO "users" ("id", "name") VALUES (?, ?))"; + } + } + } + } + SECTION("insert") { + User user{5, "Gambit"}; + SECTION("crud") { + auto statement = insert(user); + SECTION("question marks") { + context.replace_bindable_with_question = true; + expected = R"(INSERT INTO "users" ("id", "name") VALUES (?, ?))"; + } + SECTION("no question marks") { + context.replace_bindable_with_question = false; + expected = R"(INSERT INTO "users" ("id", "name") VALUES (5, 'Gambit'))"; + } + value = serialize(statement, context); + } + SECTION("explicit") { + SECTION("one column") { + auto statement = insert(user, columns(&User::id)); + SECTION("question marks") { + context.replace_bindable_with_question = true; + expected = R"(INSERT INTO "users" ("id") VALUES (?))"; + } + SECTION("no question marks") { + context.replace_bindable_with_question = false; + expected = R"(INSERT INTO "users" ("id") VALUES (5))"; + } + value = serialize(statement, context); + } + SECTION("two columns") { + auto statement = insert(user, columns(&User::id, &User::name)); + SECTION("question marks") { + context.replace_bindable_with_question = true; + expected = R"(INSERT INTO "users" ("id", "name") VALUES (?, ?))"; + } + SECTION("no question marks") { + context.replace_bindable_with_question = false; + expected = R"(INSERT INTO "users" ("id", "name") VALUES (5, 'Gambit'))"; + } + value = serialize(statement, context); + } + } + SECTION("raw") { + 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 = R"(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 = R"(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 = R"(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 = R"(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 = R"(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 = R"(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 = R"(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 = + R"(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 = + R"(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 = + R"(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 = + R"(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 = + R"(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 = R"(INSERT INTO "users" DEFAULT VALUES)"; + } + SECTION("or abort") { + auto statement = insert(or_abort(), into(), default_values()); + value = serialize(statement, context); + expected = R"(INSERT OR ABORT INTO "users" DEFAULT VALUES)"; + } + SECTION("or fail") { + auto statement = insert(or_fail(), into(), default_values()); + value = serialize(statement, context); + expected = R"(INSERT OR FAIL INTO "users" DEFAULT VALUES)"; + } + SECTION("or ignore") { + auto statement = insert(or_ignore(), into(), default_values()); + value = serialize(statement, context); + expected = R"(INSERT OR IGNORE INTO "users" DEFAULT VALUES)"; + } + SECTION("or replace") { + auto statement = insert(or_replace(), into(), default_values()); + value = serialize(statement, context); + expected = R"(INSERT OR REPLACE INTO "users" DEFAULT VALUES)"; + } + SECTION("or rollback") { + auto statement = insert(or_rollback(), into(), default_values()); + value = serialize(statement, context); + expected = R"(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 = + R"(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 = + R"(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 = + R"(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 = + R"(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 = + R"(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 = + R"(INSERT OR ROLLBACK INTO "users" SELECT "users_backup"."id", "users_backup"."name" FROM "users_backup")"; + } + } + } + SECTION("range") { + context.replace_bindable_with_question = false; + + std::vector users(1); + SECTION("objects") { + auto expression = insert_range(users.begin(), users.end()); + // deduced object type + assert_same(insert_range(users.begin(), users.end()), expression); + // deduced object type + assert_same(insert_range(users.begin(), users.end(), polyfill::identity{}), expression); + assert_same(insert_range(users.begin(), users.end(), polyfill::identity{}), expression); + value = serialize(expression, context); + expected = R"(INSERT INTO "users" ("id", "name") VALUES (?, ?))"; + } + SECTION("indirected") { + std::vector> userPtrs; + userPtrs.push_back(std::make_unique(users.front())); + auto expression = + insert_range(userPtrs.begin(), userPtrs.end(), &std::unique_ptr::operator*); + // deduced object type + assert_same(insert_range(userPtrs.begin(), userPtrs.end(), &std::unique_ptr::operator*), + expression); + value = serialize(expression, context); + expected = R"(INSERT INTO "users" ("id", "name") VALUES (?, ?))"; + } + SECTION("wrapper") { + std::vector> userRefs{std::ref(users.front())}; + SECTION("identity") { + auto expression = insert_range(userRefs.begin(), userRefs.end()); + // deduced object type + assert_same(insert_range(userRefs.begin(), userRefs.end(), polyfill::identity{}), expression); + value = serialize(expression, context); + expected = R"(INSERT INTO "users" ("id", "name") VALUES (?, ?))"; + } + SECTION("projected") { + auto expression = + insert_range(userRefs.begin(), userRefs.end(), &std::reference_wrapper::get); +#ifdef _MSC_VER /* `&std::reference_wrapper::get` is only invocable with Microsoft STL */ + // deduced object type + assert_same(insert_range(userRefs.begin(), userRefs.end(), &std::reference_wrapper::get), + expression); +#endif + value = serialize(expression, context); + expected = R"(INSERT INTO "users" ("id", "name") VALUES (?, ?))"; + } + } + } + } + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/remove.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/remove.cpp new file mode 100644 index 0000000..06a1f66 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/remove.cpp @@ -0,0 +1,33 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer remove") { + using internal::serialize; + struct User { + int id = 0; + std::string name; + }; + auto table = make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)); + using db_objects_t = internal::db_objects_tuple; + db_objects_t dbObjects{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + + std::string value; + decltype(value) expected; + + auto statement = remove(5); + SECTION("with question marks") { + context.replace_bindable_with_question = true; + expected = R"(DELETE FROM "users" WHERE "id" = ?)"; + } + SECTION("without question marks") { + context.replace_bindable_with_question = false; + expected = R"(DELETE FROM "users" WHERE "id" = 5)"; + } + value = serialize(statement, context); + + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/remove_all.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/remove_all.cpp new file mode 100644 index 0000000..aeb2ba4 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/remove_all.cpp @@ -0,0 +1,37 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer remove_all") { + using internal::serialize; + struct User { + int id = 0; + std::string name; + }; + auto table = make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)); + using db_objects_t = internal::db_objects_tuple; + db_objects_t dbObjects{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + + std::string value; + std::string expected; + + SECTION("all") { + auto statement = remove_all(); + value = serialize(statement, context); + expected = R"(DELETE FROM "users")"; + } + SECTION("where") { + auto statement = remove_all(where(&User::id == c(1))); + value = serialize(statement, context); + expected = R"(DELETE FROM "users" WHERE (("id" = 1)))"; + } + SECTION("conditions") { + auto statement = remove_all(where(&User::id == c(1)), limit(1)); + value = serialize(statement, context); + expected = R"(DELETE FROM "users" WHERE (("id" = 1)) LIMIT 1)"; + } + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/select.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/select.cpp new file mode 100644 index 0000000..bdd8f1b --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/select.cpp @@ -0,0 +1,203 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer 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 db_objects_t = internal::db_objects_tuple; + db_objects_t dbObjects{table}; + internal::serializer_context context{dbObjects}; + std::string stringValue; + decltype(stringValue) expected; + SECTION("simple") { + SECTION("1") { + 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("null") { + auto statement = select(nullptr); + SECTION("!highest_level") { + statement.highest_level = false; + stringValue = serialize(statement, context); + expected = "(SELECT null)"; + } + SECTION("highest_level") { + statement.highest_level = true; + stringValue = serialize(statement, context); + expected = "SELECT null"; + } + } + } + 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 = R"((SELECT "users"."id" FROM "users"))"; + } + SECTION("highest_level") { + statement.highest_level = true; + stringValue = serialize(statement, context); + expected = R"(SELECT "users"."id" FROM "users")"; + } + } + SECTION("null") { + auto statement = select(columns(nullptr)); + SECTION("!highest_level") { + statement.highest_level = false; + stringValue = serialize(statement, context); + expected = "(SELECT null)"; + } + SECTION("highest_level") { + statement.highest_level = true; + stringValue = serialize(statement, context); + expected = "SELECT null"; + } + } + SECTION("asterisk") { + SECTION("mapped") { + auto expression = select(asterisk()); + expression.highest_level = true; + stringValue = serialize(expression, context); + expected = R"(SELECT "users".* FROM "users")"; + } + SECTION("mapped, implicit select order") { + auto expression = select(asterisk(false)); + expression.highest_level = true; + stringValue = serialize(expression, context); + expected = R"(SELECT "users".* FROM "users")"; + } + SECTION("mapped, defined select order") { + auto expression = select(asterisk(true)); + expression.highest_level = true; + stringValue = serialize(expression, context); + expected = R"(SELECT "users"."id", "users"."name" FROM "users")"; + } + SECTION("alias, implicit select order") { + using als_u = alias_u; + + auto expression = select(asterisk()); + expression.highest_level = true; + stringValue = serialize(expression, context); + expected = R"(SELECT "u".* FROM "users" "u")"; + } + SECTION("alias, defined select order") { + using als_u = alias_u; + + auto expression = select(asterisk(true)); + expression.highest_level = true; + stringValue = serialize(expression, context); + expected = R"(SELECT "u"."id", "u"."name" FROM "users" "u")"; + } + SECTION("object") { + auto expression = select(object()); + expression.highest_level = true; + stringValue = serialize(expression, context); + expected = R"(SELECT "users".* FROM "users")"; + } + SECTION("object, implicit select order") { + auto expression = select(object(false)); + expression.highest_level = true; + stringValue = serialize(expression, context); + expected = R"(SELECT "users".* FROM "users")"; + } + SECTION("object, defined select order") { + auto expression = select(object(true)); + expression.highest_level = true; + stringValue = serialize(expression, context); + expected = R"(SELECT "users"."id", "users"."name" FROM "users")"; + } + // issue #1106 + SECTION("multi") { + auto expression = columns(asterisk(), asterisk(true)); + context.skip_table_name = false; + context.use_parentheses = false; + stringValue = serialize(expression, context); + expected = R"("users".*, "users"."id", "users"."name")"; + } + SECTION("issue #945") { + struct Employee { + int m_empno; + int m_deptno; + }; + + struct Department { + int m_deptno; + std::string m_deptname; + }; + + auto t1 = make_table("Emp", + make_column("empno", &Employee::m_empno, primary_key().autoincrement()), + make_column("deptno", &Employee::m_deptno), + foreign_key(&Employee::m_deptno).references(&Department::m_deptno)); + auto t2 = + make_table("Dept", make_column("deptno", &Department::m_deptno, primary_key().autoincrement())); + + using db_objects_t = internal::db_objects_tuple; + db_objects_t storage{t1, t2}; + + using als_d = alias_d; + using als_e = alias_e; + + auto expression = select(asterisk(), + left_join(on(c(alias_column(&Department::m_deptno)) == + alias_column(&Employee::m_deptno))), + where(is_null(alias_column(&Employee::m_deptno)))); + expression.highest_level = true; + internal::serializer_context context{storage}; + context.skip_table_name = false; + stringValue = serialize(expression, context); + expected = + R"(SELECT "d".* FROM "Dept" "d" LEFT JOIN "Emp" "e" ON ("d"."deptno" = "e"."deptno") WHERE ("e"."deptno" IS NULL))"; + } + } + } + REQUIRE(stringValue == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/update.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/update.cpp new file mode 100644 index 0000000..06f6164 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/update.cpp @@ -0,0 +1,144 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer update") { + using internal::serialize; + struct A { + int address = 0; + int type = 0; + int index = 0; + double value = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + A() = default; + A(int address, int type, int index, double value) : address{address}, type{type}, index{index}, value{value} {} +#endif + }; + std::string value; + decltype(value) expected; + SECTION("primary key") { + SECTION("column") { + auto table = make_table("table", + make_column("address", &A::address, primary_key()), + make_column("type", &A::type), + make_column("idx", &A::index), + make_column("value", &A::value)); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + SECTION("update") { + SECTION("with question marks") { + context.replace_bindable_with_question = true; + expected = R"(UPDATE "table" SET "type" = ?, "idx" = ?, "value" = ? WHERE "address" = ?)"; + } + SECTION("without question marks") { + context.replace_bindable_with_question = false; + expected = R"(UPDATE "table" SET "type" = 2, "idx" = 3, "value" = 4 WHERE "address" = 1)"; + } + + A object{1, 2, 3, 4}; + auto expression = update(object); + value = serialize(expression, context); + } + SECTION("update_all") { + SECTION("static set") { + auto expression = update_all(set(assign(&A::value, 5)), where(is_equal(&A::address, 1))); + SECTION("with question marks") { + context.replace_bindable_with_question = true; + expected = R"(UPDATE "table" SET "value" = ? WHERE (("address" = ?)))"; + } + SECTION("without question marks") { + context.replace_bindable_with_question = false; + expected = R"(UPDATE "table" SET "value" = 5 WHERE (("address" = 1)))"; + } + value = serialize(expression, context); + } + SECTION("dynamic set") { + auto storage = make_storage({}, table); + auto dynamicSet = dynamic_set(storage); + dynamicSet.push_back(assign(&A::value, 5)); + auto expression = update_all(dynamicSet, where(is_equal(&A::address, 1))); + context.replace_bindable_with_question = false; + expected = R"(UPDATE "table" SET "value" = 5 WHERE (("address" = 1)))"; + value = serialize(expression, context); + } + } + } + SECTION("table") { + auto table = make_table("table", + make_column("address", &A::address), + make_column("type", &A::type), + make_column("idx", &A::index), + make_column("value", &A::value), + primary_key(&A::address)); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + SECTION("with question marks") { + context.replace_bindable_with_question = true; + expected = R"(UPDATE "table" SET "type" = ?, "idx" = ?, "value" = ? WHERE "address" = ?)"; + } + SECTION("without question marks") { + context.replace_bindable_with_question = false; + expected = R"(UPDATE "table" SET "type" = 2, "idx" = 3, "value" = 4 WHERE "address" = 1)"; + } + + A object{1, 2, 3, 4}; + auto expression = update(object); + value = serialize(expression, context); + } + } + SECTION("composite key 2") { + auto table = make_table("table", + make_column("address", &A::address), + make_column("type", &A::type), + make_column("idx", &A::index), + make_column("value", &A::value), + primary_key(&A::address, &A::type)); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + SECTION("with question marks") { + context.replace_bindable_with_question = true; + expected = R"(UPDATE "table" SET "idx" = ?, "value" = ? WHERE "address" = ? AND "type" = ?)"; + } + SECTION("without question marks") { + context.replace_bindable_with_question = false; + expected = R"(UPDATE "table" SET "idx" = 3, "value" = 4 WHERE "address" = 1 AND "type" = 2)"; + } + + A object{1, 2, 3, 4}; + auto expression = update(object); + value = serialize(expression, context); + } + SECTION("composite key 3") { + auto table = make_table("table", + make_column("address", &A::address), + make_column("type", &A::type), + make_column("idx", &A::index), + make_column("value", &A::value), + primary_key(&A::address, &A::type, &A::index)); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + SECTION("question marks") { + context.replace_bindable_with_question = true; + expected = R"(UPDATE "table" SET "value" = ? WHERE "address" = ? AND "type" = ? AND "idx" = ?)"; + } + SECTION("no question marks") { + context.replace_bindable_with_question = false; + expected = R"(UPDATE "table" SET "value" = 4 WHERE "address" = 1 AND "type" = 2 AND "idx" = 3)"; + } + + A object{1, 2, 3, 4}; + auto expression = update(object); + value = serialize(expression, context); + } + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/update_all.cpp b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/update_all.cpp new file mode 100644 index 0000000..1f51b14 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/statement_serializer_tests/statements/update_all.cpp @@ -0,0 +1,60 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer update_all") { + using internal::serialize; + struct Contact { + int id = 0; + std::string firstName; + std::string lastName; + std::string phone; + }; + + 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 contactsTable = 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)); + auto customersTable = 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)); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{contactsTable, customersTable}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + + auto statement = + update_all(set(c(&Contact::phone) = select(&Customer::phone, from(), where(c(&Customer::id) == 1)))); + auto value = serialize(statement, context); + decltype(value) expected = + R"(UPDATE "contacts" SET "phone" = (SELECT "customers"."Phone" FROM "customers" WHERE (("customers"."CustomerId" = 1))))"; + REQUIRE(value == expected); +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/aggregate_function_return_types.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/aggregate_function_return_types.cpp new file mode 100644 index 0000000..ff5ed92 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/aggregate_function_return_types.cpp @@ -0,0 +1,119 @@ +#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_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + STATIC_REQUIRE( + std::is_same::value); + STATIC_REQUIRE( + std::is_same::value); + STATIC_REQUIRE( + std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE( + std::is_same::value); + + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + STATIC_REQUIRE( + std::is_same::value); + STATIC_REQUIRE( + std::is_same::value); + STATIC_REQUIRE( + std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE( + std::is_same::value); + + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + STATIC_REQUIRE( + std::is_same::value); + STATIC_REQUIRE( + std::is_same::value); + STATIC_REQUIRE( + std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE( + std::is_same::value); +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/alias.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/alias.cpp new file mode 100644 index 0000000..b0eaff9 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/alias.cpp @@ -0,0 +1,35 @@ +#include +#include // std::is_same +#include + +using namespace sqlite_orm; +using internal::alias_column_t; +using internal::alias_holder; +using internal::as_t; +using internal::column_alias; +using internal::column_pointer; +using internal::recordset_alias; + +template +void do_assert() { + STATIC_REQUIRE(std::is_same::value); +} + +template +void runTest(ColAlias /*colRef*/) { + do_assert(); +} + +TEST_CASE("aliases") { + struct User { + int id; + }; + + SECTION("column alias expressions") { + runTest>>(get()); + runTest, int User::*>>(as(&User::id)); + runTest, int User::*>>(alias_column>(&User::id)); + runTest, column_pointer>>( + alias_column>(column(&User::id))); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/arithmetic_operators_result_type.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/arithmetic_operators_result_type.cpp new file mode 100644 index 0000000..4fc2467 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/arithmetic_operators_result_type.cpp @@ -0,0 +1,16 @@ +#include +#include +#include // std::is_same + +using namespace sqlite_orm; + +TEST_CASE("Arithmetic operators result type") { + STATIC_REQUIRE( + std::is_same, decltype(add(1, 2))>::type, double>::value); + STATIC_REQUIRE( + std::is_same, decltype(sub(2, 1))>::type, double>::value); + STATIC_REQUIRE( + std::is_same, decltype(mul(2, 3))>::type, double>::value); + STATIC_REQUIRE(std::is_same, decltype(sqlite_orm::div(2, 3))>::type, + double>::value); +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/bindable_filter.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/bindable_filter.cpp new file mode 100644 index 0000000..afaff9f --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/bindable_filter.cpp @@ -0,0 +1,88 @@ +#include +#include +#include // std::is_same +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include +#endif + +using namespace sqlite_orm; + +namespace { + struct Custom {}; + template + class StringVeneer : public std::basic_string {}; +} + +namespace sqlite_orm { + template<> + struct statement_binder {}; +} + +TEST_CASE("bindable_filter") { + struct User { + int id = 0; + std::string name; + }; + + using internal::bindable_filter_t; + using std::is_same; + { + using Tuple = std::tuple, +#ifndef SQLITE_ORM_OMITS_CODECVT + const wchar_t*, + std::wstring, + StringVeneer, +#endif + std::vector, + std::nullptr_t, +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + std::nullopt_t, + std::optional, + std::optional, +#endif +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::string_view, +#ifndef SQLITE_ORM_OMITS_CODECVT + std::wstring_view, +#endif +#endif + std::unique_ptr, + std::shared_ptr, +#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED + static_pointer_binding, +#endif + Custom, + std::unique_ptr>; + using Res = bindable_filter_t; + STATIC_REQUIRE(is_same::value); + } + { + using Tuple = std::tuple; + using Res = bindable_filter_t; + using Expected = std::tuple; + STATIC_REQUIRE(is_same::value); + } + { + using Tuple = std::tuple; + using Res = bindable_filter_t; + using Expected = std::tuple; + STATIC_REQUIRE(is_same::value); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/column.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/column.cpp new file mode 100644 index 0000000..40de696 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/column.cpp @@ -0,0 +1,76 @@ +#include +#include + +#include "static_tests_common.h" + +using namespace sqlite_orm; + +TEST_CASE("Column") { + { + using column_type = decltype(make_column("id", &User::id)); + STATIC_REQUIRE(std::tuple_size::value == 0); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + } + { + using column_type = decltype(make_column("id", &User::getIdByRefConst, &User::setIdByVal)); + STATIC_REQUIRE(std::tuple_size::value == 0); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + } + { + using column_type = decltype(make_column("id", &User::setIdByVal, &User::getIdByRefConst)); + STATIC_REQUIRE(std::tuple_size::value == 0); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + } + { + using column_type = decltype(make_column("id", &User::getIdByRef, &User::setIdByConstRef)); + STATIC_REQUIRE(std::tuple_size::value == 0); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + } + { + using column_type = decltype(make_column("id", &User::setIdByConstRef, &User::getIdByRef)); + STATIC_REQUIRE(std::tuple_size::value == 0); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + } + { + using column_type = decltype(make_column("id", &User::getIdByValConst, &User::setIdByRef)); + STATIC_REQUIRE(std::tuple_size::value == 0); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + } + { + using column_type = decltype(make_column("id", &User::setIdByRef, &User::getIdByValConst)); + STATIC_REQUIRE(std::tuple_size::value == 0); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + } + { + using column_type = decltype(column(&Token::id)); + STATIC_REQUIRE(std::is_same::value); + using field_type = column_type::field_type; + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::type, Token>::value); + STATIC_REQUIRE(std::is_same::type, Object>::value); + STATIC_REQUIRE(std::is_same, field_type>::type, int>::value); + STATIC_REQUIRE(std::is_member_pointer::value); + STATIC_REQUIRE(!std::is_member_function_pointer::value); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/column_result_t.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/column_result_t.cpp new file mode 100644 index 0000000..dbbe963 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/column_result_t.cpp @@ -0,0 +1,118 @@ +#include +#include +#include // std::is_same + +using namespace sqlite_orm; + +template +void do_assert() { + STATIC_REQUIRE(std::is_same::value); +} + +template +void runTest(V /*value*/) { + do_assert, E>(); +} + +TEST_CASE("column_result_of_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 = std::move(comment); + } + + const std::string& getComment() const { + return this->comment; + } + + private: + int id = 0; + std::string comment; + }; + auto dbObjects = + std::make_tuple(make_table("users", make_column("id", &User::id), make_column("name", &User::name))); + using db_objects_t = decltype(dbObjects); + + runTest(&User::id); + runTest(&User::name); + runTest(in(&User::id, {1, 2, 3})); + { + std::vector vector; + vector.insert(vector.cend(), {1, 2, 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, 4)); + runTest>(min(&User::id, 4)); + runTest>(max(&User::id)); + runTest>(max(&User::name)); + runTest>(min(&User::id)); + runTest>(min(&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_()); + runTest>(columns(&User::id, &User::name)); + runTest>(asterisk()); + runTest>(asterisk>()); + runTest>(columns(asterisk(), asterisk())); + runTest(column(&User::id)); + runTest(alias_column>(&User::id)); + runTest(object()); +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/compound_operators.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/compound_operators.cpp new file mode 100644 index 0000000..164502e --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/compound_operators.cpp @@ -0,0 +1,17 @@ +#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_REQUIRE(internal::is_base_of_template::value); + auto unionAllValue = union_all(select(&User::id), select(&Token::id)); + STATIC_REQUIRE(internal::is_base_of_template::value); + auto exceptValue = except(select(&User::id), select(&Token::id)); + STATIC_REQUIRE(internal::is_base_of_template::value); + auto intersectValue = intersect(select(&User::id), select(&Token::id)); + STATIC_REQUIRE(internal::is_base_of_template::value); +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/core_function_return_types.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/core_function_return_types.cpp new file mode 100644 index 0000000..333bc30 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/core_function_return_types.cpp @@ -0,0 +1,52 @@ +#include +#include +#include // std::is_same +#include // nullptr_t + +using namespace sqlite_orm; +using std::is_same; +using std::nullptr_t; +#if __cpp_lib_type_trait_variable_templates >= 201510L +using std::is_same_v; +#endif + +TEST_CASE("Builtin function return types") { + struct User { + int64 id; + bool flag; + + bool getFlag() const { + return this->flag; + } + }; + + STATIC_REQUIRE(is_same::value); + STATIC_REQUIRE(is_same::value); + STATIC_REQUIRE(is_same::value); + STATIC_REQUIRE(is_same::value); + STATIC_REQUIRE(is_same(&User::flag, false))::return_type, int>::value); + // note: return type nullptr_t doesn't make sense but works for unit tests to assert intention + STATIC_REQUIRE(is_same(&User::flag, nullptr))::return_type, nullptr_t>::value); + + STATIC_REQUIRE(is_same::value); + STATIC_REQUIRE(is_same::value); + STATIC_REQUIRE(is_same::value); + STATIC_REQUIRE(is_same::value); + STATIC_REQUIRE(is_same(&User::flag, false))::return_type, int>::value); + // note: return type nullptr_t doesn't make sense but works for unit tests to assert intention + STATIC_REQUIRE(is_same(&User::flag, nullptr))::return_type, nullptr_t>::value); + +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + STATIC_REQUIRE(is_same_v>); + STATIC_REQUIRE(is_same_v>); + STATIC_REQUIRE(is_same_v>); + STATIC_REQUIRE(is_same_v>); +#endif + // note: return type nullptr_t doesn't make sense but works for unit tests to assert intention + { + STATIC_REQUIRE(is_same(&User::id, 0))::return_type, nullptr_t>::value); + STATIC_REQUIRE(is_same(&User::flag, false))::return_type, nullptr_t>::value); + STATIC_REQUIRE(is_same(&User::getFlag, false))::return_type, nullptr_t>::value); + STATIC_REQUIRE(is_same(&User::flag, 0))::return_type, nullptr_t>::value); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/foreign_key.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/foreign_key.cpp new file mode 100644 index 0000000..60ed3fe --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/foreign_key.cpp @@ -0,0 +1,137 @@ +#include +#include + +#include // std::is_same +#include // std::string +#include // std::is_same + +#include "static_tests_storage_traits.h" + +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_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto cppInclusionIncludeePathFk = foreign_key(&CppInclusion::includee_path).references(&File::path); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto functionDeclFilePathFk = foreign_key(&FunctionDecl::file_path).references(&File::path); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto varDeclFilePathFk = foreign_key(&VarDecl::file_path).references(&File::path); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto functionDefFilePathFk = foreign_key(&FunctionDef::file_path).references(&File::path); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto functionCallParentFunctionName = + foreign_key(&FunctionCall::parent_function_name).references(&FunctionDef::function_name); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(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, primary_key().autoincrement()), + 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_REQUIRE(std::is_same::value); + } + { + using FkTuple = storage_fk_references::type; + using Expected = std::tuple; + STATIC_REQUIRE(std::is_same::value); + } + { + using FkTuple = storage_fk_references::type; + using Expected = std::tuple<>; + STATIC_REQUIRE(std::is_same::value); + } + { + using FkTuple = storage_fk_references::type; + using Expected = std::tuple<>; + STATIC_REQUIRE(std::is_same::value); + } + { + using FkTuple = storage_fk_references::type; + using Expected = std::tuple<>; + STATIC_REQUIRE(std::is_same::value); + } + { + using FkTuple = storage_fk_references::type; + using Expected = std::tuple; + STATIC_REQUIRE(std::is_same::value); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/function_static_tests.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/function_static_tests.cpp new file mode 100644 index 0000000..9217d3a --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/function_static_tests.cpp @@ -0,0 +1,224 @@ +#include +#include +#include // std::is_same + +using namespace sqlite_orm; +using internal::is_aggregate_function_v; +using internal::is_scalar_function_v; + +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_REQUIRE(is_scalar_function_v); + STATIC_REQUIRE(!is_aggregate_function_v); + + using RunMemberFunctionPointer = internal::scalar_call_function_t; + using ExpectedType = double (Function::*)(double) const; + STATIC_REQUIRE(std::is_same::value); + + using ArgumentsTuple = internal::member_function_arguments::tuple_type; + using ExpectedArgumentsTuple = std::tuple; + STATIC_REQUIRE(std::is_same::value); + + STATIC_REQUIRE(std::is_same::return_type, double>::value); + STATIC_REQUIRE( + std::is_same::args_tuple, std::tuple>::value); + } + SECTION("double(double)") { + struct Function { + double operator()(double arg) { + return std::sqrt(arg); + } + }; + + STATIC_REQUIRE(is_scalar_function_v); + STATIC_REQUIRE(!is_aggregate_function_v); + + using RunMemberFunctionPointer = internal::scalar_call_function_t; + using ExpectedType = double (Function::*)(double); + STATIC_REQUIRE(std::is_same::value); + + using ArgumentsTuple = internal::member_function_arguments::tuple_type; + using ExpectedArgumentsTuple = std::tuple; + STATIC_REQUIRE(std::is_same::value); + + STATIC_REQUIRE(std::is_same::return_type, double>::value); + STATIC_REQUIRE( + 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_REQUIRE(is_scalar_function_v); + STATIC_REQUIRE(!is_aggregate_function_v); + + using RunMemberFunctionPointer = internal::scalar_call_function_t; + using ExpectedType = int (Function::*)(std::string) const; + STATIC_REQUIRE(std::is_same::value); + + using ArgumentsTuple = internal::member_function_arguments::tuple_type; + using ExpectedArgumentsTuple = std::tuple; + STATIC_REQUIRE(std::is_same::value); + + STATIC_REQUIRE(std::is_same::return_type, int>::value); + STATIC_REQUIRE( + std::is_same::args_tuple, std::tuple>::value); + } + SECTION("int(std::string)") { + struct Function { + int operator()(std::string arg) { + return int(arg.length()); + } + }; + + STATIC_REQUIRE(is_scalar_function_v); + STATIC_REQUIRE(!is_aggregate_function_v); + + using RunMemberFunctionPointer = internal::scalar_call_function_t; + using ExpectedType = int (Function::*)(std::string); + STATIC_REQUIRE(std::is_same::value); + + using ArgumentsTuple = internal::member_function_arguments::tuple_type; + using ExpectedArgumentsTuple = std::tuple; + STATIC_REQUIRE(std::is_same::value); + + STATIC_REQUIRE(std::is_same::return_type, int>::value); + STATIC_REQUIRE( + 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_REQUIRE(is_scalar_function_v); + STATIC_REQUIRE(!is_aggregate_function_v); + + using RunMemberFunctionPointer = internal::scalar_call_function_t; + using ExpectedType = std::string (Function::*)(const std::string&, const std::string&) const; + STATIC_REQUIRE(std::is_same::value); + + using ArgumentsTuple = internal::member_function_arguments::tuple_type; + using ExpectedArgumentsTuple = std::tuple; + STATIC_REQUIRE(std::is_same::value); + + STATIC_REQUIRE(std::is_same::return_type, std::string>::value); + STATIC_REQUIRE(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_REQUIRE(is_scalar_function_v); + STATIC_REQUIRE(!is_aggregate_function_v); + + using RunMemberFunctionPointer = internal::scalar_call_function_t; + using ExpectedType = std::string (Function::*)(const std::string&, const std::string&); + STATIC_REQUIRE(std::is_same::value); + + using ArgumentsTuple = internal::member_function_arguments::tuple_type; + using ExpectedArgumentsTuple = std::tuple; + STATIC_REQUIRE(std::is_same::value); + + STATIC_REQUIRE(std::is_same::return_type, std::string>::value); + STATIC_REQUIRE(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_REQUIRE(is_aggregate_function_v); + STATIC_REQUIRE(!is_scalar_function_v); + + using StepMemberFunctionPointer = internal::aggregate_step_function_t; + using ExpectedStepType = void (Function::*)(int); + STATIC_REQUIRE(std::is_same::value); + + using FinMemberFunctionPointer = internal::aggregate_fin_function_t; + using ExpectedFinType = int (Function::*)() const; + STATIC_REQUIRE(std::is_same::value); + + STATIC_REQUIRE(std::is_same::return_type, int>::value); + STATIC_REQUIRE(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 std::move(result); + } + }; + + STATIC_REQUIRE(is_aggregate_function_v); + STATIC_REQUIRE(!is_scalar_function_v); + + using StepMemberFunctionPointer = internal::aggregate_step_function_t; + using ExpectedStepType = void (Function::*)(std::string) const; + STATIC_REQUIRE(std::is_same::value); + + using FinMemberFunctionPointer = internal::aggregate_fin_function_t; + using ExpectedFinType = std::string (Function::*)(); + STATIC_REQUIRE(std::is_same::value); + + STATIC_REQUIRE(std::is_same::return_type, std::string>::value); + STATIC_REQUIRE( + std::is_same::args_tuple, std::tuple>::value); + } + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/functional/mpl.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/functional/mpl.cpp new file mode 100644 index 0000000..9282385 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/functional/mpl.cpp @@ -0,0 +1,35 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("mpl") { + using mpl_is_same = mpl::quote_fn; + + STATIC_REQUIRE(!mpl::is_metafunction_class_v); + STATIC_REQUIRE(mpl::is_metafunction_class_v); + STATIC_REQUIRE(mpl::invoke_fn_t::value); + STATIC_REQUIRE(mpl::invoke_t::value); + STATIC_REQUIRE(!mpl::invoke_t, int, int>::value); + STATIC_REQUIRE(mpl::invoke_t>, size_t, int>::value); + STATIC_REQUIRE(mpl::invoke_t, std::vector>::value); + STATIC_REQUIRE(mpl::invoke_t>::value); + STATIC_REQUIRE(!mpl::invoke_t>::value); + STATIC_REQUIRE(mpl::invoke_t, int, int>::value); + STATIC_REQUIRE(!mpl::invoke_t>, int, int>::value); + STATIC_REQUIRE(mpl::invoke_t::value); + STATIC_REQUIRE(mpl::invoke_t>::value); + STATIC_REQUIRE(std::is_same, std::is_void>, + mpl::quote_fn>::value); + STATIC_REQUIRE(mpl::invoke_t, + mpl::quote_fn, + mpl::quote_fn>::value); + using check_if_same_type = mpl::bind_front_fn; + STATIC_REQUIRE(mpl::invoke_t::value); + using check_if_same_template = + mpl::pass_extracted_fn_to>>; + STATIC_REQUIRE(mpl::invoke_t>::value); + using check_if_has_type_t = mpl::bind_front_higherorder_fn; + STATIC_REQUIRE(!mpl::invoke_t::value); + STATIC_REQUIRE(mpl::invoke_t::value); +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/functional/same_or_void.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/functional/same_or_void.cpp new file mode 100644 index 0000000..1f58d4a --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/functional/same_or_void.cpp @@ -0,0 +1,34 @@ +#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_REQUIRE(std::is_same::type, int>::value); + STATIC_REQUIRE(std::is_same::type, std::string>::value); + STATIC_REQUIRE(std::is_same::type, long>::value); + + // two arguments + STATIC_REQUIRE(std::is_same::type, int>::value); + STATIC_REQUIRE(std::is_same::type, void>::value); + STATIC_REQUIRE(std::is_same::type, std::string>::value); + STATIC_REQUIRE(std::is_same::type, void>::value); + + // three arguments + STATIC_REQUIRE(std::is_same::type, int>::value); + STATIC_REQUIRE(std::is_same::type, long>::value); + STATIC_REQUIRE(std::is_same::type, void>::value); + STATIC_REQUIRE(std::is_same::type, void>::value); + STATIC_REQUIRE(std::is_same::type, void>::value); + + // four arguments + STATIC_REQUIRE(std::is_same::type, int>::value); + STATIC_REQUIRE(std::is_same::type, long>::value); + STATIC_REQUIRE(std::is_same::type, void>::value); +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/functional/static_if_tests.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/functional/static_if_tests.cpp new file mode 100644 index 0000000..e915733 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/functional/static_if_tests.cpp @@ -0,0 +1,85 @@ +#include +#include + +using namespace sqlite_orm; +using internal::call_if_constexpr; +using internal::static_if; + +TEST_CASE("static_if") { + using called_pair = std::pair; + called_pair called; + { // simple true + called = {}; + static_if( + [&called] { + ++called.first; + }, + [&called] { + ++called.second; + })(); + REQUIRE(called == called_pair{1, 0}); + } + { // simple false + called = {}; + static_if( + [&called] { + ++called.first; + }, + [&called] { + ++called.second; + })(); + REQUIRE(called == called_pair{0, 1}); + } + { // tuple is empty + called = {}; + static_if>::value>( + [&called] { + ++called.first; + }, + [&called] { + ++called.second; + })(); + REQUIRE(called == called_pair{1, 0}); + } + { // tuple is not empty + called = {}; + static_if>>>( + [&called] { + ++called.first; + }, + [&called] { + ++called.second; + })(); + REQUIRE(called == called_pair{0, 1}); + } + { + struct User { + std::string name; + }; + auto ch = check(length(&User::name) > 5); + STATIC_REQUIRE(!internal::is_column_v); + called = {}; + static_if>( + [&called] { + ++called.first; + }, + [&called] { + ++called.second; + })(); + REQUIRE(called == called_pair{0, 1}); + } + { // simple true + called = {}; + call_if_constexpr([&called] { + ++called.first; + }); + REQUIRE(called == called_pair{1, 0}); + } + { // simple false + called = {}; + call_if_constexpr([&called] { + ++called.first; + }); + REQUIRE(called == called_pair{0, 0}); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/functional/tuple_conc.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/functional/tuple_conc.cpp new file mode 100644 index 0000000..7291c6d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/functional/tuple_conc.cpp @@ -0,0 +1,59 @@ +#include +#include +#include // std::string +#include // std::is_same + +using namespace sqlite_orm; + +TEST_CASE("Tuple conc") { + using namespace internal; + { + using TupleL = std::tuple; + using TupleR = std::tuple; + using IntStringTuple = tuple_cat_t; + STATIC_REQUIRE(std::is_same>::value); + } + { + using TupleL = std::tuple; + using TupleR = std::tuple; + using IntFloatTuple = tuple_cat_t; + STATIC_REQUIRE(std::is_same>::value); + } + { + using TupleL = std::tuple<>; + using TupleR = std::tuple; + using NoneFloatTuple = tuple_cat_t; + STATIC_REQUIRE(std::is_same>::value); + } + { + using TupleL = std::tuple<>; + using TupleR = std::tuple<>; + using NoneNoneTuple = tuple_cat_t; + STATIC_REQUIRE(std::is_same>::value); + } + { + using TupleL = std::tuple; + using TupleR = std::tuple; + using IntFloatDoubleTuple = tuple_cat_t; + STATIC_REQUIRE(std::is_same>::value); + } + { + using Arg = std::tuple; + using SingleArgTuple = tuple_cat_t; + STATIC_REQUIRE(std::is_same::value); + } + { + using Arg1 = std::tuple; + using Arg2 = std::tuple; + using Arg3 = std::tuple; + using IntFloatStringTuple = tuple_cat_t; + STATIC_REQUIRE(std::is_same>::value); + } + { + using Arg1 = std::tuple; + using Arg2 = std::tuple; + using Arg3 = std::tuple<>; + using IntFloatEmptyTuple = tuple_cat_t; + STATIC_REQUIRE(std::is_same>::value); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/functional/tuple_filter.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/functional/tuple_filter.cpp new file mode 100644 index 0000000..16c1610 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/functional/tuple_filter.cpp @@ -0,0 +1,78 @@ +#include +#include +#include // std::is_same + +using namespace sqlite_orm; +using std::make_tuple; + +namespace { + struct User { + int id = 0; + std::string name; + }; +} + +TEST_CASE("tuple_filter") { + using internal::filter_tuple_t; + SECTION("is_bindable") { + using Arg = std::tuple, internal::order_by_t>; + using Expected = std::tuple; + using ResultType = filter_tuple_t; + STATIC_REQUIRE(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 = filter_tuple_t; + STATIC_REQUIRE(std::is_same::value); + } +} + +TEST_CASE("count_tuple") { + using internal::count_tuple; + { + auto t = make_tuple(where(is_equal(&User::id, 5)), limit(5), order_by(&User::name)); + using T = decltype(t); + STATIC_REQUIRE(count_tuple::value == 1); + STATIC_REQUIRE(count_tuple::value == 0); + STATIC_REQUIRE(count_tuple::value == 1); + STATIC_REQUIRE(count_tuple::value == 1); + } + { + auto t = + make_tuple(where(lesser_than(&User::id, 10)), where(greater_than(&User::id, 5)), group_by(&User::name)); + using T = decltype(t); + STATIC_REQUIRE(count_tuple::value == 2); + STATIC_REQUIRE(count_tuple::value == 1); + STATIC_REQUIRE(count_tuple::value == 0); + STATIC_REQUIRE(count_tuple::value == 0); + } + { + auto t = make_tuple(group_by(&User::name), limit(10, offset(5))); + using T = decltype(t); + STATIC_REQUIRE(count_tuple::value == 0); + STATIC_REQUIRE(count_tuple::value == 1); + STATIC_REQUIRE(count_tuple::value == 0); + STATIC_REQUIRE(count_tuple::value == 1); + } + { + auto t = make_tuple(where(is_null(&User::name)), order_by(&User::id), multi_order_by(order_by(&User::name))); + using T = decltype(t); + STATIC_REQUIRE(count_tuple::value == 1); + STATIC_REQUIRE(count_tuple::value == 0); + STATIC_REQUIRE(count_tuple::value == 2); + STATIC_REQUIRE(count_tuple::value == 0); + } + { + auto t = make_tuple(dynamic_order_by(make_storage(""))); + using T = decltype(t); + STATIC_REQUIRE(count_tuple::value == 0); + STATIC_REQUIRE(count_tuple::value == 0); + STATIC_REQUIRE(count_tuple::value == 1); + STATIC_REQUIRE(count_tuple::value == 0); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/functional/tuple_traits.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/functional/tuple_traits.cpp new file mode 100644 index 0000000..d11d43a --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/functional/tuple_traits.cpp @@ -0,0 +1,21 @@ +#include +#include + +using namespace sqlite_orm; +using internal::check_if_tuple_has; +using internal::check_if_tuple_has_template; +using internal::check_if_tuple_has_type; +using internal::default_t; +using internal::is_primary_key; +using internal::primary_key_t; + +TEST_CASE("tuple traits") { + using empty_tuple_type = std::tuple<>; + using tuple_type = std::tuple, primary_key_t<>, std::string>; + + STATIC_REQUIRE(mpl::invoke_t, tuple_type>::value); + STATIC_REQUIRE(mpl::invoke_t, tuple_type>::value); + STATIC_REQUIRE(mpl::invoke_t, tuple_type>::value); + STATIC_REQUIRE(!mpl::invoke_t, tuple_type>::value); + STATIC_REQUIRE(!mpl::invoke_t, empty_tuple_type>::value); +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/functional/tuple_transform.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/functional/tuple_transform.cpp new file mode 100644 index 0000000..d2f6178 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/functional/tuple_transform.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::transform_tuple_t; + using Expected = std::tuple; + STATIC_REQUIRE(std::is_same::value); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/is_bindable.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/is_bindable.cpp new file mode 100644 index 0000000..d9d054e --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/is_bindable.cpp @@ -0,0 +1,121 @@ +#include +#include +#include // std::unique_ptr, std::shared_ptr +#include // std::string +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include +#endif + +using namespace sqlite_orm; +using internal::is_bindable_v; + +namespace { + struct Custom {}; + template + class StringVeneer : public std::basic_string {}; + + struct User { + int id; + }; +} + +namespace sqlite_orm { + template<> + struct statement_binder {}; + template<> + struct field_printer {}; +} + +TEST_CASE("is_bindable") { + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v>); +#ifndef SQLITE_ORM_OMITS_CODECVT + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v>); +#endif + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v>); + STATIC_REQUIRE(is_bindable_v>); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + STATIC_REQUIRE(is_bindable_v); +#ifndef SQLITE_ORM_OMITS_CODECVT + STATIC_REQUIRE(is_bindable_v); +#endif +#endif +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v>); + STATIC_REQUIRE(is_bindable_v>); + STATIC_REQUIRE(!is_bindable_v>); +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED + STATIC_REQUIRE(is_bindable_v>); +#endif + + STATIC_REQUIRE(is_bindable_v); + STATIC_REQUIRE(is_bindable_v>); + + STATIC_REQUIRE(!is_bindable_v); + STATIC_REQUIRE(!is_bindable_v>); + STATIC_REQUIRE(!is_bindable_v); + STATIC_REQUIRE(!is_bindable_v>); + { + auto isEqual = is_equal(&User::id, 5); + STATIC_REQUIRE(!is_bindable_v); + } + { + auto notEqual = is_not_equal(&User::id, 10); + STATIC_REQUIRE(!is_bindable_v); + } + { + auto lesserThan = lesser_than(&User::id, 10); + STATIC_REQUIRE(!is_bindable_v); + } + { + auto lesserOrEqual = lesser_or_equal(&User::id, 5); + STATIC_REQUIRE(!is_bindable_v); + } + { + auto greaterThan = greater_than(&User::id, 5); + STATIC_REQUIRE(!is_bindable_v); + } + { + auto greaterOrEqual = greater_or_equal(&User::id, 5); + STATIC_REQUIRE(!is_bindable_v); + } + { + auto func = datetime("now"); + STATIC_REQUIRE(!is_bindable_v); + bool trueCalled = false; + bool falseCalled = false; + auto dummy = 5; // for gcc compilation + internal::static_if>( + [&trueCalled](int&) { + trueCalled = true; + }, + [&falseCalled](int&) { + falseCalled = true; + })(dummy); + REQUIRE(!trueCalled); + REQUIRE(falseCalled); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/is_column_with_insertable_primary_key.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/is_column_with_insertable_primary_key.cpp new file mode 100644 index 0000000..4be302d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/is_column_with_insertable_primary_key.cpp @@ -0,0 +1,44 @@ +#include +#include + +using namespace sqlite_orm; +using namespace internal; + +template +using insertable_index_sequence = filter_tuple_sequence_t>; +template +using noninsertable_index_sequence = filter_tuple_sequence_t::template fn, + polyfill::type_identity_t, + col_index_sequence_with>; + +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 + ); + + STATIC_REQUIRE(insertable_index_sequence::size() == 4); + STATIC_REQUIRE(noninsertable_index_sequence::size() == 2); + STATIC_REQUIRE(noninsertable_index_sequence::size() == 0); +} \ No newline at end of file diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/is_primary_key_insertable.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/is_primary_key_insertable.cpp new file mode 100644 index 0000000..274b0a4 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/is_primary_key_insertable.cpp @@ -0,0 +1,32 @@ +#include +#include +#include // std::std::decay + +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_REQUIRE(internal::is_primary_key_insertable>::value); + }); + + iterate_tuple(noninsertable, [](auto& v) { + STATIC_REQUIRE(!internal::is_primary_key_insertable>::value); + }); +} \ No newline at end of file diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/is_printable.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/is_printable.cpp new file mode 100644 index 0000000..86d7ef9 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/is_printable.cpp @@ -0,0 +1,76 @@ +#include +#include +#include // std::unique_ptr, std::shared_ptr +#include // std::string +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include +#endif + +using namespace sqlite_orm; +using internal::is_printable_v; + +namespace { + struct Custom {}; + template + class StringVeneer : public std::basic_string {}; + + struct User { + int id; + }; +} + +namespace sqlite_orm { + template<> + struct field_printer {}; +} + +TEST_CASE("is_printable") { + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(!is_printable_v); + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v>); +#ifndef SQLITE_ORM_OMITS_CODECVT + STATIC_REQUIRE(!is_printable_v); + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v>); +#endif + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v>); + STATIC_REQUIRE(is_printable_v>); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + STATIC_REQUIRE(!is_printable_v); + STATIC_REQUIRE(!is_printable_v); +#endif +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v>); + STATIC_REQUIRE(is_printable_v>); + STATIC_REQUIRE(!is_printable_v>); +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED + STATIC_REQUIRE(!is_printable_v>); +#endif + + STATIC_REQUIRE(is_printable_v); + STATIC_REQUIRE(is_printable_v>); + + STATIC_REQUIRE(!is_printable_v); + STATIC_REQUIRE(!is_printable_v); + STATIC_REQUIRE(!is_printable_v>); +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/iterator_t.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/iterator_t.cpp new file mode 100644 index 0000000..39c424d --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/iterator_t.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +using namespace sqlite_orm; + +namespace { + 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_REQUIRE(std::is_default_constructible::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same()), iter&>::value); + using check = decltype(std::declval()++); + + // indirectly_readable + STATIC_REQUIRE(std::is_same()), const User&>::value); + + // input_iterator + STATIC_REQUIRE(std::is_same::value); + + // sentinel (equality comparable) + STATIC_REQUIRE(std::is_same() == std::declval()), bool>::value); +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/member_traits_tests.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/member_traits_tests.cpp new file mode 100644 index 0000000..be1ed63 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/member_traits_tests.cpp @@ -0,0 +1,294 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("member_traits_tests") { + using internal::getter_field_type_t; + using internal::is_getter; + using internal::is_setter; + using internal::member_field_type_t; + using internal::member_object_type_t; + using internal::object_field_type_t; + using internal::setter_field_type_t; + using std::is_same; +#if __cpp_lib_type_trait_variable_templates >= 201510L + using std::is_same_v; +#endif + + struct User { + mutable int id = 0; + + // getter_by_value + int getIdByValConst() const { + return this->id; + } + + // getter_by_ref_const + int& getIdByRefConst() const { + return this->id; + } + + // 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; + } + + // setter_by_ref + void setIdByRef(int& id) { + this->id = 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_REQUIRE(!is_getter::value); + STATIC_REQUIRE(is_getter::value); + STATIC_REQUIRE(is_getter::value); + STATIC_REQUIRE(is_getter::value); + STATIC_REQUIRE(is_getter::value); + STATIC_REQUIRE(is_getter::value); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_getter::value); + STATIC_REQUIRE(is_getter::value); + STATIC_REQUIRE(is_getter::value); + STATIC_REQUIRE(is_getter::value); + STATIC_REQUIRE(is_getter::value); + STATIC_REQUIRE(is_getter::value); +#endif + STATIC_REQUIRE(!is_getter::value); + STATIC_REQUIRE(!is_getter::value); + STATIC_REQUIRE(!is_getter::value); + STATIC_REQUIRE(!is_getter::value); + STATIC_REQUIRE(!is_getter::value); + STATIC_REQUIRE(!is_getter::value); + + STATIC_REQUIRE(!is_setter::value); + STATIC_REQUIRE(!is_setter::value); + STATIC_REQUIRE(!is_setter::value); + STATIC_REQUIRE(!is_setter::value); + STATIC_REQUIRE(!is_setter::value); + STATIC_REQUIRE(!is_setter::value); + STATIC_REQUIRE(!is_setter::value); + STATIC_REQUIRE(!is_setter::value); + STATIC_REQUIRE(!is_setter::value); + STATIC_REQUIRE(!is_setter::value); + STATIC_REQUIRE(!is_setter::value); + STATIC_REQUIRE(!is_setter::value); + STATIC_REQUIRE(is_setter::value); + STATIC_REQUIRE(is_setter::value); + STATIC_REQUIRE(is_setter::value); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_setter::value); + STATIC_REQUIRE(is_setter::value); + STATIC_REQUIRE(is_setter::value); +#endif + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); + + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); + + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); + + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); + + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); + + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); +#endif + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); + + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); + + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); +#endif + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); + + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); + + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); + + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); + + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); +#endif + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); + + STATIC_REQUIRE(is_same, User>::value); + STATIC_REQUIRE(is_same, int>::value); + STATIC_REQUIRE(is_same, int>::value); +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); + + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); + + STATIC_REQUIRE(is_same_v, User>); + STATIC_REQUIRE(is_same_v, int>); + STATIC_REQUIRE(is_same_v, int>); +#endif +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/node_tuple.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/node_tuple.cpp new file mode 100644 index 0000000..5d28c4c --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/node_tuple.cpp @@ -0,0 +1,944 @@ +#include +#include +#include // std::is_same + +using namespace sqlite_orm; +using internal::alias_holder; +using internal::column_alias; +using internal::column_pointer; + +template +struct is_pair : std::false_type {}; + +template +struct is_pair> : std::true_type {}; + +template +struct is_tuple : std::false_type {}; + +template +struct is_tuple> : std::true_type {}; + +TEST_CASE("Node tuple") { + using internal::bindable_filter_t; + using internal::node_tuple; + using internal::node_tuple_t; + using std::is_same; + using std::tuple; + + struct User { + int id = 0; + std::string name; + }; + + SECTION("bindables") { + SECTION("int") { + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "bindable int"); + STATIC_REQUIRE(is_same, tuple>::value); + } + SECTION("float") { + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "bindable float"); + STATIC_REQUIRE(is_same, tuple>::value); + } + } + SECTION("non-bindable literals") { + using namespace internal; + using Tuple = node_tuple_t>; + using Expected = tuple<>; + static_assert(is_same::value, "literal int"); + STATIC_REQUIRE(is_same, tuple<>>::value); + } + SECTION("binary_condition") { + using namespace internal; + SECTION("5 < 6.0f") { + auto c = lesser_than(5, 6.0f); + using C = decltype(c); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "lesser_than_t"); + STATIC_REQUIRE(is_same, tuple>::value); + } + SECTION("id < 10") { + auto c = lesser_than(&User::id, 10); + using C = decltype(c); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "lesser_than_t"); + STATIC_REQUIRE(is_same, tuple>::value); + } + SECTION("5 <= 6.0f") { + auto c = lesser_or_equal(5, 6.0f); + using C = decltype(c); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "lesser_or_equal_t"); + STATIC_REQUIRE(is_same, tuple>::value); + } + SECTION("id <= 10.0") { + auto c = lesser_or_equal(&User::id, 10.0); + using C = decltype(c); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "lesser_or_equal_t"); + STATIC_REQUIRE(is_same, tuple>::value); + } + SECTION("5 > 6.0f") { + auto c = greater_than(5, 6.0f); + using C = decltype(c); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "greater_than_t"); + STATIC_REQUIRE(is_same, tuple>::value); + } + SECTION("id > 20") { + auto c = greater_than(&User::id, 20); + using C = decltype(c); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "greater_than_t"); + STATIC_REQUIRE(is_same, tuple>::value); + } + SECTION("5 >= 6.0f") { + auto c = greater_or_equal(5, 6.0f); + using C = decltype(c); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "greater_or_equal_t"); + STATIC_REQUIRE(is_same, tuple>::value); + } + SECTION("5 >= id") { + auto c = greater_or_equal(5, &User::id); + using C = decltype(c); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "greater_or_equal_t"); + STATIC_REQUIRE(is_same, tuple>::value); + } + SECTION("5 == 6.0f") { + auto c = is_equal(5, 6.0f); + using C = decltype(c); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "is_equal_t"); + STATIC_REQUIRE(is_same, tuple>::value); + } + SECTION("'ototo' == name") { + auto c = is_equal("ototo", &User::name); + using C = decltype(c); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "is_equal_t"); + STATIC_REQUIRE(is_same, tuple>::value); + } + SECTION("5 != 6.0f") { + auto c = is_not_equal(5, 6.0f); + using C = decltype(c); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "is_not_equal_t"); + STATIC_REQUIRE(is_same, tuple>::value); + } + SECTION("name != std::string('ototo')") { + auto c = is_not_equal(&User::name, std::string("ototo")); + using C = decltype(c); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "is_not_equal_t"); + STATIC_REQUIRE(is_same, tuple>::value); + } + SECTION("bool and int") { + using Tuple = node_tuple_t>; + using Expected = tuple; + static_assert(is_same::value, "and_condition_t"); + STATIC_REQUIRE(is_same, tuple>::value); + } + SECTION("bool or int") { + using Tuple = node_tuple_t>; + using Expected = tuple; + static_assert(is_same::value, "or_condition_t"); + STATIC_REQUIRE(is_same, tuple>::value); + } + } + SECTION("binary_operator") { + using namespace internal; + + using CondTuple = node_tuple_t>; + static_assert(is_same>::value, "conc_t"); + + using AddTuple = node_tuple_t>; + static_assert(is_same>::value, "add_t"); + + using SubTuple = node_tuple_t>; + static_assert(is_same>::value, "sub_t"); + + using MulTuple = node_tuple_t>; + static_assert(is_same>::value, "mul_t"); + + using DivTuple = node_tuple_t>; + static_assert(is_same>::value, "div_t"); + + using ModTuple = node_tuple_t>; + static_assert(is_same>::value, "mod_t"); + + using AssignTuple = node_tuple_t>; + static_assert(is_same>::value, "assign_t"); + } + SECTION("columns") { + auto cols = columns(&User::id, &User::name); + using Cols = decltype(cols); + using ColsTuple = node_tuple_t; + static_assert(is_same>::value, "columns_t"); + } + SECTION("in") { + auto inValue = in(&User::id, {1, 2, 3}); + using In = decltype(inValue); + using InTuple = node_tuple_t; + static_assert(is_same>>::value, "in_t"); + } + SECTION("exists(select(&User::name, where(in(&User::id, {6, 7, 9}))))") { + auto c = exists(select(&User::name, where(in(&User::id, {6, 7, 9})))); + using Con = decltype(c); + using Tuple = node_tuple_t; + using Expected = tuple>; + static_assert(is_same::value, "exists(select(&User::name, where(in(&User::id, {6, 7, 9}))))"); + } + SECTION("aggregate functions") { + SECTION("avg") { + auto node = avg(&User::id); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "avg"); + } + SECTION("avg filter") { + auto node = avg(&User::id).filter(where(length(&User::name) > 5)); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "avg filter"); + } + SECTION("count(*)") { + auto node = count(); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "count(*)"); + } + SECTION("count(*) filter") { + auto node = count().filter(where(length(&User::name) > 5)); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple()), decltype(&User::name), int>; + static_assert(is_same::value, "count(*) filter"); + } + SECTION("count(X)") { + auto node = count(&User::id); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "count(X)"); + } + SECTION("count(X) filter") { + auto node = count(&User::id).filter(where(length(&User::name) > 5)); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "count(X) filter"); + } + SECTION("group_concat(X)") { + auto node = group_concat(&User::id); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "group_concat(X)"); + } + SECTION("group_concat(X) filter") { + auto node = group_concat(&User::id).filter(where(length(&User::name) > 5)); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "group_concat(X) filter"); + } + SECTION("group_concat(X,Y)") { + auto node = group_concat(&User::id, std::string("-")); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "group_concat(X,Y)"); + } + SECTION("group_concat(X,Y) filter") { + auto node = group_concat(&User::id, std::string("-")).filter(where(length(&User::name) > 5)); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "group_concat(X,Y) filter"); + } + SECTION("max(X)") { + auto node = max(&User::id); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "max(X)"); + } + SECTION("max(X) filter") { + auto node = max(&User::id).filter(where(length(&User::name) > 5)); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "max(X) filter"); + } + SECTION("min(X)") { + auto node = min(&User::id); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "min(X)"); + } + SECTION("min(X) filter") { + auto node = min(&User::id).filter(where(length(&User::name) > 5)); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "min(X) filter"); + } + SECTION("sum(X)") { + auto node = sum(&User::id); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "sum(X)"); + } + SECTION("sum(X) filter") { + auto node = sum(&User::id).filter(where(length(&User::name) > 5)); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "sum(X) filter"); + } + SECTION("total(X)") { + auto node = total(&User::id); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "total(X)"); + } + SECTION("total(X) filter") { + auto node = total(&User::id).filter(where(length(&User::name) > 5)); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "total(X) filter"); + } + } + SECTION("scalar functions") { + SECTION("max(X,Y)") { + auto node = max(&User::id, 4); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "max(X,Y)"); + } + SECTION("min(X,Y)") { + auto node = min(&User::id, 4); + using Node = decltype(node); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "min(X,Y)"); + } + } + SECTION("compound operator") { + SECTION("union_(select(1), select(2))") { + auto un = union_(select(1), select(2)); + using Union = decltype(un); + using Tuple = node_tuple_t; + static_assert(is_same>::value, "union_(select(1), select(2))"); + } + SECTION("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_t; + using Expected = tuple; + static_assert(is_same::value, "union_all"); + } + SECTION("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_t; + using Expected = tuple; + static_assert(is_same::value, "except"); + } + SECTION("intersect") { + auto un = intersect(select(&User::name), select(&User::name, where(is_equal(&User::name, "Anny")))); + using Union = decltype(un); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "intersect"); + } + } + 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_t; + STATIC_REQUIRE(is_same>::value); + } + 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_t; + STATIC_REQUIRE(is_same>::value); + } + SECTION("tuple") { + auto expression = std::make_tuple(1, std::string("hi")); + using Expression = decltype(expression); + using Tuple = node_tuple_t; + STATIC_REQUIRE(is_same>::value); + } + SECTION("values") { + SECTION("int + string") { + auto expression = values(1, std::string("hi")); + using Expression = decltype(expression); + using Tuple = node_tuple_t; + STATIC_REQUIRE(is_same>::value); + } + SECTION("tuple") { + auto expression = values(std::make_tuple(1, std::string("hi"))); + using Expression = decltype(expression); + using Tuple = node_tuple_t; + STATIC_REQUIRE(is_same>::value); + } + } + SECTION("into") { + auto expression = into(); + using Expression = decltype(expression); + using Tuple = node_tuple_t; + STATIC_REQUIRE(is_same>::value); + } + SECTION("select") { + SECTION("select(&User::id)") { + auto sel = select(&User::id); + using Sel = decltype(sel); + using Tuple = node_tuple_t; + 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_t; + 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_t; + 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_t; + 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_t; + 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_t; + using ExpectedTuple = tuple; + STATIC_REQUIRE(std::is_same::value); + } + } + SECTION("get_all_t") { + SECTION("get_all()") { + auto getAll = get_all(); + using GetAll = decltype(getAll); + using Tuple = node_tuple_t; + 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_t; + 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_t; + static_assert(is_same>::value, + "get_all(where(is_equal(5.0, &User::id)))"); + } + } + SECTION("having_t") { + using namespace internal; + auto hav = having(greater_or_equal(&User::id, 10)); + using Having = decltype(hav); + using Tuple = node_tuple_t; + static_assert(is_same>::value, + "having(greater_or_equal(&User::id, 10))"); + } + SECTION("cast_t") { + auto sel = select(columns(cast(&User::id), cast(&User::name))); + using Select = decltype(sel); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "named_collate"); + } + 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_t; + using Expected = 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_t; + using Expected = 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_t; + using Expected = 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_t; + using Expected = 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_t; + using Expected = 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_t; + using Expected = 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_t; + using Expected = 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_t; + using Expected = 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_t; + using Expected = 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_t; + using Expected = 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_t; + using Expected = 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_t; + using Expected = tuple>; + static_assert(is_same::value, + "not exists(select(&User::name, where(in(&User::id, {6, 7, 9}))))"); + } + } + SECTION("core_function_t") { + SECTION("lower") { + auto f = lower(&User::name); + using Fun = decltype(f); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "lower"); + } + SECTION("upper") { + auto f = upper("hi"); + using Fun = decltype(f); + using Tuple = node_tuple_t; + using ArgType = std::tuple_element_t<0, Fun::args_type>; + static_assert(is_same::value, "upper arg[0]"); + using Expected = tuple; + static_assert(is_same::value, "upper"); + } + SECTION("total_changes") { + auto f = total_changes(); + using Fun = decltype(f); + using Tuple = node_tuple_t; + using Expected = tuple<>; + static_assert(is_same::value, "total_changes"); + } + SECTION("changes") { + auto f = changes(); + using Fun = decltype(f); + using Tuple = node_tuple_t; + using Expected = tuple<>; + static_assert(is_same::value, "changes"); + } + SECTION("trim(1)") { + auto f = trim(&User::name); + using Fun = decltype(f); + using Tuple = node_tuple_t; + using Expected = 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_t; + using Expected = tuple; + static_assert(is_same::value, "trim(2)"); + } + SECTION("ltrim(1)") { + auto f = ltrim(&User::id); + using Fun = decltype(f); + using Tuple = node_tuple_t; + using Expected = 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_t; + using Expected = tuple; + static_assert(is_same::value, "ltrim(2)"); + } + SECTION("rtrim(1)") { + auto f = rtrim(&User::name); + using Fun = decltype(f); + using Tuple = node_tuple_t; + using Expected = 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_t; + using Expected = 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_t; + using Expected = tuple; + static_assert(is_same::value, "char_"); + } +#endif + SECTION("coalesce") { + auto f = coalesce(10, 20); + using Fun = decltype(f); + using Tuple = node_tuple_t; + using Expected = 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_t; + using Expected = tuple; + static_assert(is_same::value, "date"); + } + SECTION("datetime") { + auto f = datetime("now"); + using Fun = decltype(f); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "datetime"); + } + SECTION("julianday") { + auto f = julianday("now"); + using Fun = decltype(f); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "julianday"); + } + SECTION("zeroblob") { + auto f = zeroblob(10); + using Fun = decltype(f); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "zeroblob"); + } + SECTION("substr(2)") { + auto f = substr(&User::name, 7); + using Fun = decltype(f); + using Tuple = node_tuple_t; + using Expected = 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_t; + using Expected = 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_t; + using Expected = tuple; + static_assert(is_same::value, "left_join"); + } + SECTION("join on") { + auto j = join(on(is_equal(&User::id, 2))); + using Join = decltype(j); + using Tuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "join on"); + } + SECTION("join using column") { + auto j = join(using_(&User::id)); + using Join = decltype(j); + using Tuple = node_tuple_t; + using Expected = tuple>; + static_assert(is_same::value, "join using"); + } + SECTION("join using explicit column") { + struct Derived : User {}; + auto j = join(using_(column(&User::id))); + using Join = decltype(j); + using Tuple = node_tuple_t; + using Expected = tuple>; + static_assert(is_same::value, "join using explicit column"); + } + SECTION("left_outer_join") { + auto j = left_outer_join(on(is_equal(&User::id, 2))); + using Join = decltype(j); + using Tuple = node_tuple_t; + using Expected = 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_t; + using Expected = tuple; + static_assert(is_same::value, "inner_join"); + } + } + SECTION("case") { + auto c = case_(&User::name).when("USA", then("Dosmetic")).else_("Foreign").end(); + using Case = decltype(c); + using CaseExpressionTuple = node_tuple::type; + STATIC_REQUIRE(is_same>::value); + + STATIC_REQUIRE(is_tuple>::value); + STATIC_REQUIRE(is_tuple>::value); + STATIC_REQUIRE(!is_tuple::value); + STATIC_REQUIRE(is_pair>::value); + STATIC_REQUIRE(!is_pair::value); + + using ArgsType = Case::args_type; + STATIC_REQUIRE(is_tuple::value); + STATIC_REQUIRE(std::tuple_size::value == 1); + + using Arg0 = std::tuple_element_t<0, ArgsType>; + STATIC_REQUIRE(is_pair::value); + using Arg0First = Arg0::first_type; + STATIC_REQUIRE(is_same::value); + using Arg0Second = Arg0::second_type; + STATIC_REQUIRE(is_same::value); + STATIC_REQUIRE(is_same>>::value); + + using ElseExpressionTuple = node_tuple::type; + STATIC_REQUIRE(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_t; + using ExpectedTuple = tuple; + STATIC_REQUIRE(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_t; + using ExpectedTuple = tuple; + STATIC_REQUIRE(std::is_same::value); + } + SECTION("excluded") { + auto statement = excluded(&User::id); + using Statement = decltype(statement); + using Tuple = node_tuple_t; + using ExpectedTuple = tuple; + STATIC_REQUIRE(std::is_same::value); + } +#if SQLITE_VERSION_NUMBER >= 3024000 + 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_t; + using ExpectedTuple = tuple; + STATIC_REQUIRE(std::is_same::value); + } +#endif + SECTION("group_by") { + auto statement = group_by(&User::id); + using Statement = decltype(statement); + using Tuple = node_tuple_t; + using ExpectedTuple = tuple; + STATIC_REQUIRE(std::is_same::value); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/operators_adl.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/operators_adl.cpp new file mode 100644 index 0000000..bca8f8e --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/operators_adl.cpp @@ -0,0 +1,64 @@ +#include +#include + +using sqlite_orm::c; +using sqlite_orm::internal::binary_operator; +using sqlite_orm::internal::greater_or_equal_t; +using sqlite_orm::internal::greater_than_t; +using sqlite_orm::internal::is_equal_t; +using sqlite_orm::internal::is_not_equal_t; +using sqlite_orm::internal::lesser_or_equal_t; +using sqlite_orm::internal::lesser_than_t; +using sqlite_orm::polyfill::is_specialization_of_v; + +TEST_CASE("ADL and expression operators") { + struct User { + int id; + }; + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v 42), greater_than_t>); + STATIC_REQUIRE(is_specialization_of_v c(&User::id)), greater_than_t>); + + STATIC_REQUIRE(is_specialization_of_v= 42), greater_or_equal_t>); + STATIC_REQUIRE(is_specialization_of_v= c(&User::id)), greater_or_equal_t>); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/select_return_type.cpp b/libs/sqlite_orm-1.8.2/tests/static_tests/select_return_type.cpp new file mode 100644 index 0000000..86d40e5 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/select_return_type.cpp @@ -0,0 +1,61 @@ +#include +#include + +#include "static_tests_common.h" +#include "static_tests_storage_traits.h" + +using namespace sqlite_orm; + +TEST_CASE("Select return types") { + auto storage = make_storage("", make_table("users", make_column("id", &User::id))); + // this call is important - it tests compilation in inner storage_t::serialize_column_schema function + storage.sync_schema(); + { + using SelectVectorInt = decltype(storage.select(&User::id)); + STATIC_REQUIRE(std::is_same>::value); + + using SelectVectorTuple = decltype(storage.select(columns(&User::id))); + auto ids = storage.select(columns(&User::id)); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same>>::value); + using IdsTuple = SelectVectorTuple::value_type; + STATIC_REQUIRE(std::tuple_size::value == 1); + } + // test storage traits + { + using namespace sqlite_orm::internal::storage_traits; + struct Visit { + int id = 0; + std::string date; + }; + + // test is_mapped + STATIC_REQUIRE(internal::is_mapped_v); + STATIC_REQUIRE(!internal::is_mapped_v); + + // test is_storage + STATIC_REQUIRE(internal::is_storage::value); + STATIC_REQUIRE(!internal::is_storage::value); + STATIC_REQUIRE(!internal::is_storage::value); + STATIC_REQUIRE(!internal::is_storage::value); + + auto storage2 = make_storage( + "", + make_table("visits", make_column("id", &Visit::id, primary_key()), make_column("date", &Visit::date))); + + // test storage_columns_count + STATIC_REQUIRE(storage_columns_count::value == 1); + STATIC_REQUIRE(storage_columns_count::value == 0); + STATIC_REQUIRE(storage_columns_count::value == 2); + + // test storage mapped columns + using MappedUserColumnsTypes = storage_mapped_columns::type; + STATIC_REQUIRE(std::is_same>::value); + + using MappedVisitColumnsEmpty = storage_mapped_columns::type; + STATIC_REQUIRE(std::is_same>::value); + + using MappedVisitColumnTypes = storage_mapped_columns::type; + STATIC_REQUIRE(std::is_same>::value); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/static_tests_common.h b/libs/sqlite_orm-1.8.2/tests/static_tests/static_tests_common.h new file mode 100644 index 0000000..366440f --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/static_tests_common.h @@ -0,0 +1,35 @@ +#pragma once + +struct User { + int id; + + const int& getIdByRefConst() const { + return this->id; + } + + const int& getIdByRef() { + return this->id; + } + + int getIdByValConst() const { + return this->id; + } + + void setIdByVal(int id_) { + this->id = id_; + } + + void setIdByConstRef(const int& id_) { + this->id = id_; + } + + void setIdByRef(int& id_) { + this->id = id_; + } +}; + +struct Object { + int id; +}; + +struct Token : Object {}; diff --git a/libs/sqlite_orm-1.8.2/tests/static_tests/static_tests_storage_traits.h b/libs/sqlite_orm-1.8.2/tests/static_tests/static_tests_storage_traits.h new file mode 100644 index 0000000..c354423 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/static_tests/static_tests_storage_traits.h @@ -0,0 +1,124 @@ +#pragma once +/* + * All symbols used to be in dev/storage_traits.h up to sqlite_orm 1.7. + * Because they were not used, they were moved to unit tests, + * and for simplicitly the namespace sqlite_orm::internal::storage_traits was kept. + */ + +#include // std::integral_constant + +#include + +namespace sqlite_orm { + + namespace internal { + + template + using source_type_t = typename T::source_type; + + namespace storage_traits { + + template + struct storage_columns_count_impl + : std::integral_constant>::value> {}; + + template<> + struct storage_columns_count_impl : std::integral_constant {}; + + /** + * S - storage_t + * Lookup - mapped or not mapped data type + */ + template + struct storage_columns_count + : storage_columns_count_impl> {}; + + /** + * Table A `table_t<>` + * Lookup is object type which is the reference target (e.g. foreign_key(&Visit::userId).references(&User::id) has Lookup = User) + */ + template + struct table_foreign_keys_count + : count_filtered_tuple, + check_if_is_type::template fn, + filter_tuple_sequence_t, is_foreign_key>, + target_type_t> {}; + + /** + * DBOs - db_objects_tuple + * Lookup - type mapped to storage + */ + template + struct storage_foreign_keys_count_impl : std::integral_constant {}; + +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + struct storage_foreign_keys_count_impl, Lookup> { + static constexpr int value = (table_foreign_keys_count::value + ...); + }; +#else + template + struct storage_foreign_keys_count_impl, Lookup> { + static constexpr int value = table_foreign_keys_count::value + + storage_foreign_keys_count_impl, Lookup>::value; + }; +#endif + + /** + * S - storage class + * Lookup - type mapped to storage + * This class tells how many types mapped to DBOs have foreign keys to Lookup + */ + template + struct storage_foreign_keys_count : storage_foreign_keys_count_impl {}; + + template + using table_foreign_keys_t = + filter_tuple_t, + check_if_is_type::template fn, + target_type_t, + filter_tuple_sequence_t, is_foreign_key>>; + + /* + * Implementation note: must be a struct instead of an template alias because the foreign keys tuple + * must be hoisted into a named alias, otherwise type replacement may fail for legacy compilers + * if an alias template has a dependent expression in it [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. + */ + template + struct table_fk_references { + using foreign_keys = table_foreign_keys_t; + + using type = transform_tuple_t; + }; + + /** + * DBOs - db_objects_tuple + * Lookup - type mapped to storage + */ + template + struct storage_fk_references_impl; + + template + struct storage_foreign_keys_impl; + + template + struct storage_fk_references_impl, Lookup> + : conc_tuple::type...> {}; + + template + struct storage_foreign_keys_impl, Lookup> + : conc_tuple...> {}; + + /** + * S - storage class + * Lookup - type mapped to storage + * type holds `std::tuple` with types that has references to Lookup as foreign keys + */ + template + struct storage_fk_references : storage_fk_references_impl {}; + + template + struct storage_foreign_keys : storage_foreign_keys_impl {}; + } + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/storage_non_crud_tests.cpp b/libs/sqlite_orm-1.8.2/tests/storage_non_crud_tests.cpp new file mode 100644 index 0000000..100bffd --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/storage_non_crud_tests.cpp @@ -0,0 +1,642 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("explicit from") { + struct User { + int id = 0; + std::string name; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User() = default; + User(int id, std::string name) : id{id}, name{std::move(name)} {} +#endif + }; + 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_all") { + struct Record { + int id = 0; + std::string name; + }; + auto storage = make_storage( + {}, + make_table("records", make_column("id", &Record::id, primary_key()), make_column("name", &Record::name))); + storage.sync_schema(); + auto vars = dynamic_set(storage); + vars.push_back(assign(&Record::name, "Bob")); + storage.update_all(vars, where(is_equal(&Record::id, 10))); +} + +TEST_CASE("update set null") { + + struct User { + int id = 0; + std::unique_ptr name; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User() = default; + User(int id, decltype(name) name) : id{id}, name{std::move(name)} {} +#endif + }; + + 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; + +#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED + Object() = default; + Object(int id, std::string name) : id{id}, name{std::move(name)} {} +#endif + }; + + struct ObjectWithoutRowid { + int id; + std::string name; + +#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED + ObjectWithoutRowid() = default; + ObjectWithoutRowid(int id, std::string name) : id{id}, name{std::move(name)} {} +#endif + }; + + 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(0, "Skillet")); + } + storage.insert_range(objects.begin(), objects.end(), &std::unique_ptr::operator*); + REQUIRE(storage.count() == 100); + + // test empty container + std::vector> emptyVector; + storage.insert_range(emptyVector.begin(), emptyVector.end(), &std::unique_ptr::operator*); + + // test insert_range without rowid + std::vector> objectsWR; + objectsWR.push_back(std::make_unique(10, "Life")); + objectsWR.push_back(std::make_unique(20, "Death")); + + REQUIRE(objectsWR.size() == 2); + storage.insert_range(objectsWR.begin(), objectsWR.end(), &std::unique_ptr::operator*); + 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); + REQUIRE(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("Replace query") { + struct Object { + int id; + std::string name; + +#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED + Object() = default; + Object(int id, std::string name) : id{id}, name{std::move(name)} {} +#endif + }; + + struct User { + + User(int id_, std::string name_) : id(id_), name(std::move(name_)) {} + + int getId() const { + return this->id; + } + + void setId(int id_) { + this->id = id_; + } + + std::string getName() const { + return this->name; + } + + void setName(std::string name_) { + this->name = std::move(name_); + } + + private: + int id = 0; + std::string name; + }; + + auto storage = make_storage( + "test_replace.sqlite", + make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name)), + make_table("users", + make_column("id", &User::getId, &User::setId, primary_key()), + make_column("name", &User::setName, &User::getName))); + + storage.sync_schema(); + storage.remove_all(); + storage.remove_all(); + + storage.replace(Object{ + 100, + "Baby", + }); + REQUIRE(storage.count() == 1); + auto baby = storage.get(100); + REQUIRE(baby.id == 100); + REQUIRE(baby.name == "Baby"); + + storage.replace(Object{ + 200, + "Time", + }); + REQUIRE(storage.count() == 2); + auto time = storage.get(200); + REQUIRE(time.id == 200); + REQUIRE(time.name == "Time"); + storage.replace(Object{ + 100, + "Ototo", + }); + REQUIRE(storage.count() == 2); + auto ototo = storage.get(100); + REQUIRE(ototo.id == 100); + REQUIRE(ototo.name == "Ototo"); + + 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(300, "Iggy")); + vector.push_back(std::make_unique(400, "Azalea")); + storage.replace_range(vector.begin(), vector.end(), &std::unique_ptr::operator*); + REQUIRE(storage.count() == 4); + + // test empty container + std::vector> emptyVector; + storage.replace_range(emptyVector.begin(), emptyVector.end(), &std::unique_ptr::operator*); + } + REQUIRE(storage.count() == 0); + storage.replace(User{10, "Daddy Yankee"}); +} + +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("Explicit insert") { + using Catch::Matchers::ContainsSubstring; + + struct User { + int id; + std::string name; + int age; + std::string email; + }; + + class Visit { + public: + const int& id() const { + return _id; + } + + void setId(int newValue) { + _id = newValue; + } + + const time_t& createdAt() const { + return _createdAt; + } + + void setCreatedAt(time_t newValue) { + _createdAt = newValue; + } + + const int& usedId() const { + return _usedId; + } + + void setUsedId(int newValue) { + _usedId = newValue; + } + + private: + int _id; + time_t _createdAt; + int _usedId; + }; + + auto storage = + make_storage("explicitinsert.sqlite", + 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"))), + make_table("visits", + make_column("id", &Visit::setId, &Visit::id, primary_key()), + make_column("created_at", &Visit::createdAt, &Visit::setCreatedAt, default_value(10)), + make_column("used_id", &Visit::usedId, &Visit::setUsedId))); + + storage.sync_schema(); + storage.remove_all(); + storage.remove_all(); + + SECTION("user") { + SECTION("two columns") { + User user{}; + user.name = "Juan"; + user.age = 57; + auto id = storage.insert(user, columns(&User::name, &User::age)); + REQUIRE(storage.get(id).email == "dummy@email.com"); + } + SECTION("three columns") { + User user2; + user2.id = 2; + user2.name = "Kevin"; + user2.age = 27; + REQUIRE(user2.id == storage.insert(user2, columns(&User::id, &User::name, &User::age))); + REQUIRE(storage.get(user2.id).email == "dummy@email.com"); + } + SECTION("four columns") { + User user3; + user3.id = 3; + user3.name = "Sia"; + user3.age = 42; + user3.email = "sia@gmail.com"; + auto insertedId = storage.insert(user3, columns(&User::id, &User::name, &User::age, &User::email)); + REQUIRE(user3.id == insertedId); + auto insertedUser3 = storage.get(user3.id); + REQUIRE(insertedUser3.email == user3.email); + REQUIRE(insertedUser3.age == user3.age); + REQUIRE(insertedUser3.name == user3.name); + } + SECTION("one column") { + User user4; + user4.name = "Egor"; + REQUIRE_THROWS_WITH(storage.insert(user4, columns(&User::name)), + ContainsSubstring("NOT NULL constraint failed")); + } + } + SECTION("visit") { + SECTION("one column not primary key") { + Visit visit; + SECTION("getter") { + visit.setUsedId(1); + visit.setId(storage.insert(visit, columns(&Visit::usedId))); + + auto visitFromStorage = storage.get(visit.id()); + REQUIRE(visitFromStorage.createdAt() == 10); + REQUIRE(visitFromStorage.usedId() == visit.usedId()); + storage.remove(visitFromStorage.usedId()); + } + } + SECTION("two columns") { + Visit visit2; + visit2.setId(2); + visit2.setUsedId(1); + SECTION("getters") { + auto insertedId = storage.insert(visit2, columns(&Visit::id, &Visit::usedId)); + REQUIRE(visit2.id() == insertedId); + auto visitFromStorage = storage.get(visit2.id()); + REQUIRE(visitFromStorage.usedId() == visit2.usedId()); + storage.remove(visit2.id()); + } + } + SECTION("one column primary key") { + Visit visit3; + visit3.setId(10); + SECTION("getter") { + REQUIRE_THROWS_WITH(storage.insert(visit3, columns(&Visit::id)), + ContainsSubstring("NOT NULL constraint failed")); + } + } + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/storage_tests.cpp b/libs/sqlite_orm-1.8.2/tests/storage_tests.cpp new file mode 100644 index 0000000..e1feeb4 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/storage_tests.cpp @@ -0,0 +1,218 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("busy handler") { + auto storage = make_storage({}); + storage.busy_handler([](int /*timesCount*/) { + return 0; + }); +} + +TEST_CASE("drop table") { + struct User { + int id = 0; + std::string name; + }; + struct Visit { + int id = 0; + std::string date; + }; + const std::string usersTableName = "users"; + const std::string visitsTableName = "visits"; + auto storage = make_storage( + {}, + make_table(usersTableName, make_column("id", &User::id, primary_key()), make_column("name", &User::name)), + make_table(visitsTableName, make_column("id", &Visit::id, primary_key()), make_column("date", &Visit::date))); + REQUIRE(!storage.table_exists(usersTableName)); + REQUIRE(!storage.table_exists(visitsTableName)); + + storage.sync_schema(); + REQUIRE(storage.table_exists(usersTableName)); + REQUIRE(storage.table_exists(visitsTableName)); + + storage.drop_table(usersTableName); + REQUIRE(!storage.table_exists(usersTableName)); + REQUIRE(storage.table_exists(visitsTableName)); + + storage.drop_table(visitsTableName); + REQUIRE(!storage.table_exists(usersTableName)); + REQUIRE(!storage.table_exists(visitsTableName)); +} + +TEST_CASE("rename table") { + struct User { + int id = 0; + std::string name; + }; + struct Visit { + int id = 0; + std::string date; + }; + const std::string usersTableName = "users"; + const std::string userNewTableName = "users_new"; + const std::string visitsTableName = "visits"; + const std::string visitsNewTableName = "visits_new"; + auto storage = make_storage( + {}, + make_table(usersTableName, make_column("id", &User::id, primary_key()), make_column("name", &User::name)), + make_table(visitsTableName, make_column("id", &Visit::id, primary_key()), make_column("date", &Visit::date))); + + REQUIRE(!storage.table_exists(usersTableName)); + REQUIRE(!storage.table_exists(userNewTableName)); + REQUIRE(!storage.table_exists(visitsTableName)); + REQUIRE(!storage.table_exists(visitsNewTableName)); + REQUIRE(storage.tablename() == usersTableName); + REQUIRE(storage.tablename() != userNewTableName); + REQUIRE(storage.tablename() == visitsTableName); + REQUIRE(storage.tablename() != visitsNewTableName); + + storage.sync_schema(); + REQUIRE(storage.table_exists(usersTableName)); + REQUIRE(!storage.table_exists(userNewTableName)); + REQUIRE(storage.table_exists(visitsTableName)); + REQUIRE(!storage.table_exists(visitsNewTableName)); + REQUIRE(storage.tablename() == usersTableName); + REQUIRE(storage.tablename() != userNewTableName); + REQUIRE(storage.tablename() == visitsTableName); + REQUIRE(storage.tablename() != visitsNewTableName); + + SECTION("with 1 argument") { + storage.rename_table(userNewTableName); + REQUIRE(storage.table_exists(usersTableName)); + REQUIRE(!storage.table_exists(userNewTableName)); + REQUIRE(storage.table_exists(visitsTableName)); + REQUIRE(!storage.table_exists(visitsNewTableName)); + REQUIRE(storage.tablename() != usersTableName); + REQUIRE(storage.tablename() == userNewTableName); + REQUIRE(storage.tablename() == visitsTableName); + REQUIRE(storage.tablename() != visitsNewTableName); + + storage.rename_table(visitsNewTableName); + REQUIRE(storage.table_exists(usersTableName)); + REQUIRE(!storage.table_exists(userNewTableName)); + REQUIRE(storage.table_exists(visitsTableName)); + REQUIRE(!storage.table_exists(visitsNewTableName)); + REQUIRE(storage.tablename() != usersTableName); + REQUIRE(storage.tablename() == userNewTableName); + REQUIRE(storage.tablename() != visitsTableName); + REQUIRE(storage.tablename() == visitsNewTableName); + } + SECTION("with 2 arguments") { + + storage.rename_table(usersTableName, userNewTableName); + REQUIRE(!storage.table_exists(usersTableName)); + REQUIRE(storage.table_exists(userNewTableName)); + REQUIRE(storage.table_exists(visitsTableName)); + REQUIRE(!storage.table_exists(visitsNewTableName)); + REQUIRE(storage.tablename() == usersTableName); + REQUIRE(storage.tablename() != userNewTableName); + REQUIRE(storage.tablename() == visitsTableName); + REQUIRE(storage.tablename() != visitsNewTableName); + + storage.rename_table(visitsTableName, visitsNewTableName); + REQUIRE(!storage.table_exists(usersTableName)); + REQUIRE(storage.table_exists(userNewTableName)); + REQUIRE(!storage.table_exists(visitsTableName)); + REQUIRE(storage.table_exists(visitsNewTableName)); + REQUIRE(storage.tablename() == usersTableName); + REQUIRE(storage.tablename() != userNewTableName); + REQUIRE(storage.tablename() == visitsTableName); + 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("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.find_column_name(&User::id) == "id"); + REQUIRE(*storage.find_column_name(&User::name) == "name"); + REQUIRE(*storage.find_column_name(&Visit::id) == "id"); + REQUIRE(*storage.find_column_name(&Visit::userId) == "user_id"); + REQUIRE(*storage.find_column_name(&Visit::date) == "date"); + REQUIRE(storage.find_column_name(&Visit::notUsed) == nullptr); +} + +namespace { + class Record final { + public: + using ID = std::uint64_t; + using TimeMs = std::uint64_t; + + inline ID id() const noexcept { + return m_id; + }; + inline void setId(ID val) noexcept { + m_id = val; + } + + inline TimeMs time() const noexcept { + return m_time; + } + inline void setTime(const TimeMs& val) noexcept { + m_time = val; + } + + private: + ID m_id{}; + TimeMs m_time{}; + }; +} +TEST_CASE("non-unique DBOs") { + auto idx1 = make_unique_index("idx_record_id", &Record::id); + auto idx2 = make_index("idx_record_time", &Record::time); + static_assert(std::is_same::value, ""); + auto db = make_storage({}, + idx1, + idx2, + make_table("record", + make_column("id", &Record::setId, &Record::id), + make_column("time", &Record::setTime, &Record::time))); + db.sync_schema(); +} diff --git a/libs/sqlite_orm-1.8.2/tests/sync_schema_tests.cpp b/libs/sqlite_orm-1.8.2/tests/sync_schema_tests.cpp new file mode 100644 index 0000000..a7e1d21 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/sync_schema_tests.cpp @@ -0,0 +1,556 @@ +#include +#include +#include + +using namespace sqlite_orm; + +/** + * this is the deal: assume we have a `users` table with schema + * `CREATE TABLE users (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL, category_id INTEGER, surname TEXT)`. + * We create a storage and insert several objects. Next we simulate schema changing (app update for example): create + * another storage with a new schema partial of the previous one: `CREATE TABLE users (id INTEGER NOT NULL PRIMARY KEY, + * name TEXT NOT NULL)`. Next we call `sync_schema(true)` and assert that all users are saved. This test tests whether + * REMOVE COLUMN imitation works well. + */ +TEST_CASE("Sync schema") { + + // this is an old version of user.. + struct UserBefore { + int id; + std::string name; + std::unique_ptr categoryId; + std::unique_ptr surname; + }; + + // this is a new version of user + struct UserAfter { + int id; + std::string name; + + bool operator==(const UserBefore& userBefore) const { + return this->id == userBefore.id && this->name == userBefore.name; + } + }; + + // create an old storage + auto filename = "sync_schema_text.sqlite"; + ::remove(filename); + auto storage = make_storage(filename, + make_table("users", + make_column("id", &UserBefore::id, primary_key()), + make_column("name", &UserBefore::name), + make_column("category_id", &UserBefore::categoryId), + make_column("surname", &UserBefore::surname))); + + // sync in case if it is first launch + auto syncSchemaSimulationRes = storage.sync_schema_simulate(); + auto syncSchemaRes = storage.sync_schema(); + + REQUIRE(syncSchemaRes == syncSchemaSimulationRes); + + // create c++ objects to insert into table + std::vector usersToInsert; + usersToInsert.push_back({-1, "Michael", nullptr, std::make_unique("Scofield")}); + usersToInsert.push_back({-1, "Lincoln", std::make_unique(4), std::make_unique("Burrows")}); + usersToInsert.push_back({-1, "Sucre", nullptr, nullptr}); + usersToInsert.push_back({-1, "Sara", std::make_unique(996), std::make_unique("Tancredi")}); + usersToInsert.push_back({-1, "John", std::make_unique(100500), std::make_unique("Abruzzi")}); + usersToInsert.push_back({-1, "Brad", std::make_unique(65), nullptr}); + usersToInsert.push_back({-1, "Paul", std::make_unique(65), nullptr}); + + for(auto& user: usersToInsert) { + auto insertedId = storage.insert(user); + user.id = insertedId; + } + + // assert count first cause we shall be asserting row by row next + REQUIRE(static_cast(storage.count()) == usersToInsert.size()); + + // now we create a new storage with a partial schema + auto newStorage = make_storage( + filename, + make_table("users", make_column("id", &UserAfter::id, primary_key()), make_column("name", &UserAfter::name))); + SECTION("preserve = false") { + syncSchemaSimulationRes = newStorage.sync_schema_simulate(false); + syncSchemaRes = newStorage.sync_schema(false); + + REQUIRE(syncSchemaRes.size() == 1); +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + REQUIRE(syncSchemaRes.begin()->second == sync_schema_result::old_columns_removed); +#else + REQUIRE(syncSchemaRes.begin()->second == sync_schema_result::dropped_and_recreated); +#endif + REQUIRE(syncSchemaSimulationRes == syncSchemaRes); + } + SECTION("preserve = true") { + syncSchemaSimulationRes = newStorage.sync_schema_simulate(true); + + // now call `sync_schema` with argument `preserve` as `true`. It will retain the data in case `sqlite_orm` needs to + // remove a column + syncSchemaRes = newStorage.sync_schema(true); + REQUIRE(syncSchemaRes.size() == 1); + REQUIRE(syncSchemaRes.begin()->second == sync_schema_result::old_columns_removed); + REQUIRE(syncSchemaSimulationRes == syncSchemaRes); + + // get all users after syncing the schema + auto usersFromDb = newStorage.get_all(order_by(&UserAfter::id)); + + REQUIRE(usersFromDb.size() == usersToInsert.size()); + + for(size_t i = 0; i < usersFromDb.size(); ++i) { + auto& userFromDb = usersFromDb[i]; + auto& oldUser = usersToInsert[i]; + REQUIRE(userFromDb == oldUser); + } + + auto usersCountBefore = newStorage.count(); + + syncSchemaSimulationRes = newStorage.sync_schema_simulate(); + syncSchemaRes = newStorage.sync_schema(); + REQUIRE(syncSchemaRes == syncSchemaSimulationRes); + + auto usersCountAfter = newStorage.count(); + REQUIRE(usersCountBefore == usersCountAfter); + } +} + +TEST_CASE("issue854") { + struct Base { + std::string name; + int64_t timestamp; + int64_t value; + }; + + struct A : public Base { + int64_t id; + }; + auto storage = make_storage({}, + make_table("entries", + make_column("id", &A::id, sqlite_orm::primary_key().autoincrement()), + make_column("name", &A::name), + make_column("timestamp", &A::timestamp), + unique(column(&Base::name), column(&Base::timestamp)))); + storage.sync_schema(); +} + +TEST_CASE("issue521") { + auto storagePath = "issue521.sqlite"; + + struct MockDatabasePoco { + int id{-1}; + std::string name; + uint32_t alpha{0}; + float beta{0.0}; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + MockDatabasePoco() = default; + MockDatabasePoco(int id, std::string name, uint32_t alpha, float beta) : + id{id}, name{std::move(name)}, alpha{alpha}, beta{beta} {} +#endif + }; + std::vector pocosToInsert; + + ::remove(storagePath); + { + // --- Create the initial database + auto storage = sqlite_orm::make_storage( + storagePath, + sqlite_orm::make_table("pocos", + sqlite_orm::make_column("id", &MockDatabasePoco::id, sqlite_orm::primary_key()), + sqlite_orm::make_column("name", &MockDatabasePoco::name))); + + // --- We simulate the synchronization first, then do it for real and compare + auto simulated = storage.sync_schema_simulate(true); + auto ssr = storage.sync_schema(true); + REQUIRE(ssr == simulated); + REQUIRE(ssr.at("pocos") == sqlite_orm::sync_schema_result::new_table_created); + + // --- Insert two rows + pocosToInsert.clear(); + pocosToInsert.push_back({-1, "Michael", 10, 10.10f}); + pocosToInsert.push_back({-1, "Joyce", 20, 20.20f}); + + for(auto& poco: pocosToInsert) { + auto insertedId = storage.insert(poco); + poco.id = insertedId; + } + + // --- Retrieve the pocos and verify + REQUIRE(static_cast(storage.count()) == pocosToInsert.size()); + + 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]; + + REQUIRE(pocoFromDb.id == oldPoco.id); + REQUIRE(pocoFromDb.name == oldPoco.name); + } + } + { + // --- Read the database and create the storage + auto storage = sqlite_orm::make_storage( + storagePath, + sqlite_orm::make_table("pocos", + sqlite_orm::make_column("id", &MockDatabasePoco::id, sqlite_orm::primary_key()), + sqlite_orm::make_column("name", &MockDatabasePoco::name))); + // --- We simulate the synchronization first, then do it for real and compare + auto simulated = storage.sync_schema_simulate(true); + auto ssr = storage.sync_schema(true); + REQUIRE(ssr == simulated); + REQUIRE(ssr["pocos"] == sqlite_orm::sync_schema_result::already_in_sync); + + REQUIRE(static_cast(storage.count()) == pocosToInsert.size()); + + 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]; + REQUIRE(pocoFromDb.id == oldPoco.id); + REQUIRE(pocoFromDb.name == oldPoco.name); + } + } + // --- Add a new column + { + // --- Read the database and create the storage + auto storage = sqlite_orm::make_storage( + storagePath, + sqlite_orm::make_table( + "pocos", + sqlite_orm::make_column("id", &MockDatabasePoco::id, sqlite_orm::primary_key()), + sqlite_orm::make_column("name", &MockDatabasePoco::name), + sqlite_orm::make_column("alpha", &MockDatabasePoco::alpha, sqlite_orm::default_value(1)))); + // --- We simulate the synchronization first, then do it for real and compare + auto simulated = storage.sync_schema_simulate(true); + auto ssr = storage.sync_schema(true); + REQUIRE(ssr == simulated); + REQUIRE(ssr["pocos"] == sqlite_orm::sync_schema_result::new_columns_added); + REQUIRE(static_cast(storage.count()) == pocosToInsert.size()); + + 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]; + REQUIRE(pocoFromDb.id == oldPoco.id); + REQUIRE(pocoFromDb.name == oldPoco.name); + REQUIRE(pocoFromDb.alpha == 1); + } + } + // --- Add a new column and delete an old one + { + // --- Read the database and create the storage + auto storage = sqlite_orm::make_storage( + storagePath, + sqlite_orm::make_table( + "pocos", + sqlite_orm::make_column("id", &MockDatabasePoco::id, sqlite_orm::primary_key()), + sqlite_orm::make_column("name", &MockDatabasePoco::name), + sqlite_orm::make_column("beta", &MockDatabasePoco::beta, sqlite_orm::default_value(1.1)))); + + // --- We simulate the synchronization first, then do it for real and compare + auto simulated = storage.sync_schema_simulate(true); + auto ssr = storage.sync_schema(true); + REQUIRE(ssr == simulated); + REQUIRE(ssr["pocos"] == sqlite_orm::sync_schema_result::new_columns_added_and_old_columns_removed); + REQUIRE(static_cast(storage.count()) == pocosToInsert.size()); + + 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]; + + REQUIRE(pocoFromDb.id == oldPoco.id); + REQUIRE(pocoFromDb.name == oldPoco.name); + REQUIRE(!(pocoFromDb.beta < 1)); + } + } +} + +bool compareUniquePointers(const std::unique_ptr& lhs, const std::unique_ptr& rhs) { + if(!lhs && !rhs) { + return true; + } else { + if(lhs && rhs) { + return *lhs == *rhs; + } else { + return false; + } + } +} + +TEST_CASE("sync_schema") { + using Catch::Matchers::UnorderedEquals; + struct User { + int id = 0; + std::string name; + int age = 0; + std::unique_ptr ageNullable; + + User() = default; + + User(int id_) : id(id_) {} + + User(int id_, std::string name_) : id(id_), name(std::move(name_)) {} + + User(int id_, int age_) : id(id_), age(age_) {} + + 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 { + return this->id == other.id && this->name == other.name && this->age == other.age; + } + }; + auto storagePath = "sync_schema.sqlite"; + std::string tableName = "users"; + struct { + const std::string id = "id"; + const std::string name = "name"; + const std::string age = "age"; + } columnNames; + ::remove(storagePath); + { + auto storage = make_storage(storagePath, + make_table(tableName, + make_column(columnNames.id, &User::id, primary_key()), + make_column(columnNames.name, &User::name))); + auto syncSchemaSimulateRes = storage.sync_schema_simulate(true); + auto syncSchemaRes = storage.sync_schema(true); + REQUIRE(syncSchemaSimulateRes == syncSchemaRes); + decltype(syncSchemaRes) expected{ + {tableName, sync_schema_result::new_table_created}, + }; + REQUIRE(syncSchemaRes == expected); + + storage.replace(User{1, "Alex"}); + storage.replace(User{2, "Michael"}); + } + SECTION("remove name column") { + auto storage = + make_storage(storagePath, make_table(tableName, make_column(columnNames.id, &User::id, primary_key()))); + SECTION("preserve = true") { + auto syncSchemaSimulateRes = storage.sync_schema_simulate(true); + auto syncSchemaRes = storage.sync_schema(true); + REQUIRE(syncSchemaSimulateRes == syncSchemaRes); + decltype(syncSchemaRes) expected{ + {tableName, sync_schema_result::old_columns_removed}, + }; + REQUIRE(syncSchemaRes == expected); + auto users = storage.get_all(); + REQUIRE_THAT(users, UnorderedEquals(std::vector{User{1}, User{2}})); + } + SECTION("preserve = false") { + auto syncSchemaSimulateRes = storage.sync_schema_simulate(); + auto syncSchemaRes = storage.sync_schema(); + REQUIRE(syncSchemaSimulateRes == syncSchemaRes); +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + decltype(syncSchemaRes) expected{ + {tableName, sync_schema_result::old_columns_removed}, + }; +#else + decltype(syncSchemaRes) expected{ + {tableName, sync_schema_result::dropped_and_recreated}, + }; +#endif + REQUIRE(syncSchemaRes == expected); + auto users = storage.get_all(); +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + REQUIRE(!users.empty()); +#else + REQUIRE(users.empty()); +#endif + } + } + SECTION("replace a column with no default value") { + auto storage = make_storage(storagePath, + make_table(tableName, + make_column(columnNames.id, &User::id, primary_key()), + make_column(columnNames.age, &User::age))); + std::map syncSchemaSimulateRes; + std::map syncSchemaRes; + SECTION( + "preserve = true") { // there is NO way we can preserve data by adding a column with no default value and not nullable! + syncSchemaSimulateRes = storage.sync_schema_simulate(true); + syncSchemaRes = storage.sync_schema(true); + } + SECTION("preserve = false") { + syncSchemaSimulateRes = storage.sync_schema_simulate(); + syncSchemaRes = storage.sync_schema(); + } + REQUIRE(syncSchemaSimulateRes == syncSchemaRes); + decltype(syncSchemaRes) expected{ + {tableName, sync_schema_result::dropped_and_recreated}, + }; + REQUIRE(syncSchemaRes == expected); + auto users = storage.get_all(); + REQUIRE(users.empty()); + } + SECTION("replace a column with default value") { + auto storage = make_storage(storagePath, + make_table(tableName, + make_column(columnNames.id, &User::id, primary_key()), + make_column(columnNames.age, &User::age, default_value(-1)))); + SECTION("preserve = true") { + auto syncSchemaSimulateRes = storage.sync_schema_simulate(true); + auto syncSchemaRes = storage.sync_schema(true); + REQUIRE(syncSchemaSimulateRes == syncSchemaRes); + decltype(syncSchemaRes) expected{ + {tableName, sync_schema_result::new_columns_added_and_old_columns_removed}, + }; + REQUIRE(syncSchemaRes == expected); + auto users = storage.get_all(); + REQUIRE_THAT(users, UnorderedEquals(std::vector{User{1, -1}, User{2, -1}})); + } + SECTION("preserve = false") { + auto syncSchemaSimulateRes = storage.sync_schema_simulate(); + auto syncSchemaRes = storage.sync_schema(); + REQUIRE(syncSchemaSimulateRes == syncSchemaRes); +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + decltype(syncSchemaRes) expected{ + {tableName, sync_schema_result::new_columns_added_and_old_columns_removed}, + }; +#else + decltype(syncSchemaRes) expected{ + {tableName, sync_schema_result::dropped_and_recreated}, + }; +#endif + REQUIRE(syncSchemaRes == expected); + auto users = storage.get_all(); +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + REQUIRE(!users.empty()); +#else + REQUIRE(users.empty()); +#endif + } + } + SECTION("replace a column with null") { + auto storage = make_storage(storagePath, + make_table(tableName, + make_column(columnNames.id, &User::id, primary_key()), + make_column(columnNames.age, &User::ageNullable))); + SECTION("preserve = true") { + auto syncSchemaSimulateRes = storage.sync_schema_simulate(true); + auto syncSchemaRes = storage.sync_schema(true); + REQUIRE(syncSchemaSimulateRes == syncSchemaRes); + { + decltype(syncSchemaRes) expected{ + {tableName, sync_schema_result::new_columns_added_and_old_columns_removed}, + }; + REQUIRE(syncSchemaRes == expected); + } + auto users = storage.get_all(); + REQUIRE_THAT(users, UnorderedEquals(std::vector{User{1}, User{2}})); + { + auto rows = storage.select(asterisk()); + decltype(rows) expected; + expected.push_back({1, std::unique_ptr()}); + expected.push_back({2, std::unique_ptr()}); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + } + SECTION("preserve = false") { + auto syncSchemaSimulateRes = storage.sync_schema_simulate(); + auto syncSchemaRes = storage.sync_schema(); + REQUIRE(syncSchemaSimulateRes == syncSchemaRes); +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + decltype(syncSchemaRes) expected{ + {tableName, sync_schema_result::new_columns_added_and_old_columns_removed}, + }; +#else + decltype(syncSchemaRes) expected{ + {tableName, sync_schema_result::dropped_and_recreated}, + }; +#endif + REQUIRE(syncSchemaRes == expected); + auto users = storage.get_all(); +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + REQUIRE(!users.empty()); +#else + REQUIRE(users.empty()); +#endif + } + } +} + +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(); +} +#if SQLITE_VERSION_NUMBER >= 3031000 +TEST_CASE("sync_schema with generated columns") { + struct User { + int id = 0; + int hash = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User() = default; + User(int id, int hash = 0) : id{id}, hash{hash} {} +#endif + + bool operator==(const User& other) const { + return this->id == other.id && this->hash == other.hash; + } + }; + auto storagePath = "sync_schema_with_generated.sqlite"; + ::remove(storagePath); + auto storage1 = make_storage(storagePath, make_table("users", make_column("id", &User::id))); + storage1.sync_schema(); + storage1.insert(User{5}); + SECTION("add a generated column and sync schema with preserve = false") { + auto generatedAlwaysConstraint = generated_always_as(c(&User::id) + 4); + std::vector allUsers; + decltype(allUsers) expectedUsers; + SECTION("virtual") { + generatedAlwaysConstraint = generatedAlwaysConstraint.virtual_(); + expectedUsers.push_back({5, 9}); + } + SECTION("not specified") { + expectedUsers.push_back({5, 9}); + } + SECTION("stored") { + generatedAlwaysConstraint = generatedAlwaysConstraint.stored(); + // with preserve == false nothing is preserved since this kind of generated column requires dropping the table + // thus we don't expect any users to be preserved! + } + auto storage2 = make_storage(storagePath, + make_table("users", + make_column("id", &User::id), + make_column("hash", &User::hash, generatedAlwaysConstraint))); + storage2.sync_schema(); + allUsers = storage2.get_all(); + REQUIRE(allUsers == expectedUsers); + } + SECTION("add a generated column and sync schema with preserve = true") { + auto generatedAlwaysConstraint = generated_always_as(c(&User::id) + 4); + std::vector allUsers; + decltype(allUsers) expectedUsers; + SECTION("not specified") { + expectedUsers.push_back({5, 9}); + } + SECTION("virtual") { + generatedAlwaysConstraint = generatedAlwaysConstraint.virtual_(); + expectedUsers.push_back({5, 9}); + } + SECTION("stored") { + generatedAlwaysConstraint = generatedAlwaysConstraint.stored(); + expectedUsers.push_back({5, 9}); + } + auto storage2 = make_storage(storagePath, + make_table("users", + make_column("id", &User::id), + make_column("hash", &User::hash, generatedAlwaysConstraint))); + storage2.sync_schema(true); + allUsers = storage2.get_all(); + REQUIRE(allUsers == expectedUsers); + } +} +#endif diff --git a/libs/sqlite_orm-1.8.2/tests/table_name_collector.cpp b/libs/sqlite_orm-1.8.2/tests/table_name_collector.cpp new file mode 100644 index 0000000..3a5b813 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/table_name_collector.cpp @@ -0,0 +1,47 @@ +#include +#include + +using namespace sqlite_orm; +using internal::alias_extractor; + +TEST_CASE("table name collector") { + struct User { + int id = 0; + std::string name; + }; + + auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table}; + internal::table_name_collector_base::table_name_set expected; + + SECTION("from table") { + internal::serializer_context context{dbObjects}; + auto collector = internal::make_table_name_collector(context.db_objects); + + SECTION("regular column") { + using als = alias_z; + auto expression = &User::id; + expected.emplace(table.name, ""); + iterate_ast(expression, collector); + } + SECTION("regular column pointer") { + auto expression = column(&User::id); + expected.emplace(table.name, ""); + iterate_ast(expression, collector); + } + SECTION("aliased regular column") { + using als = alias_z; + auto expression = alias_column(&User::id); + expected.emplace(table.name, "z"); + iterate_ast(expression, collector); + } + SECTION("aliased regular column pointer") { + using als = alias_z; + auto expression = alias_column(column(&User::id)); + expected.emplace(table.name, "z"); + iterate_ast(expression, collector); + } + REQUIRE(collector.table_names == expected); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/table_tests.cpp b/libs/sqlite_orm-1.8.2/tests/table_tests.cpp new file mode 100644 index 0000000..0b5618b --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/table_tests.cpp @@ -0,0 +1,209 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("table::find_column_name") { + SECTION("fields") { + struct Contact { + int id = 0; + std::string firstName; + std::string lastName; + int countryCode = 0; + std::string phoneNumber; + int visitsCount = 0; + }; + auto contactIdColumn = make_column("contact_id", &Contact::id, primary_key().autoincrement()); + { + using column_type = decltype(contactIdColumn); + STATIC_REQUIRE(internal::is_column::value); + } + + auto table = make_table("contacts", + contactIdColumn, + make_column("first_name", &Contact::firstName), + make_column("last_name", &Contact::lastName), + 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"); + } + SECTION("getters and setters") { + struct Contact { + private: + int _id = 0; + std::string _firstName; + std::string _lastName; + int _countryCode = 0; + std::string _phoneNumber; + int _visitsCount = 0; + + public: + int id() const { + return this->_id; + } + + void setId(int value) { + this->_id = value; + } + + const std::string& firstName() const { + return this->_firstName; + } + + void setFirstName(std::string value) { + this->_firstName = std::move(value); + } + + const std::string& lastName() const { + return this->_lastName; + } + + void setLastName(std::string value) { + this->_lastName = std::move(value); + } + + int countryCode() const { + return this->_countryCode; + } + + void setCountryCode(int value) { + this->_countryCode = value; + } + + const std::string& phoneNumber() const { + return this->_phoneNumber; + } + + void setPhoneNumber(std::string value) { + this->_phoneNumber = std::move(value); + } + + int visitsCount() const { + return this->_visitsCount; + } + + void setVisitsCount(int value) { + this->_visitsCount = value; + } + }; + auto table = make_table("contacts", + make_column("contact_id", &Contact::id, &Contact::setId, primary_key().autoincrement()), + make_column("first_name", &Contact::firstName, &Contact::setFirstName), + make_column("last_name", &Contact::lastName, &Contact::setLastName), + make_column("country_code", &Contact::countryCode, &Contact::setCountryCode), + 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::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::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::visitsCount) == "visits_count"); + REQUIRE(*table.find_column_name(&Contact::setVisitsCount) == "visits_count"); + } +} + +TEST_CASE("Composite key column names") { + + struct User { + int id = 0; + std::string name; + std::string info; + }; + + { + auto table = make_table("t", + make_column("id", &User::id), + make_column("name", &User::name), + make_column("info", &User::info), + primary_key(&User::id, &User::name)); + auto compositeKeyColumnsNames = table.composite_key_columns_names(); + std::vector expected = {"id", "name"}; + REQUIRE(std::equal(compositeKeyColumnsNames.begin(), compositeKeyColumnsNames.end(), expected.begin())); + } + { + auto table = make_table("t", + make_column("id", &User::id), + make_column("name", &User::name), + make_column("info", &User::info), + primary_key(&User::name, &User::id)); + auto compositeKeyColumnsNames = table.composite_key_columns_names(); + std::vector expected = {"name", "id"}; + REQUIRE(std::equal(compositeKeyColumnsNames.begin(), compositeKeyColumnsNames.end(), expected.begin())); + } + { + auto table = make_table("t", + make_column("id", &User::id), + make_column("name", &User::name), + make_column("info", &User::info), + primary_key(&User::name, &User::id, &User::info)); + auto compositeKeyColumnsNames = table.composite_key_columns_names(); + std::vector expected = {"name", "id", "info"}; + REQUIRE(std::equal(compositeKeyColumnsNames.begin(), compositeKeyColumnsNames.end(), expected.begin())); + } + { + auto table = make_table("t", + make_column("id", &User::id), + make_column("name", &User::name), + make_column("info", &User::info)); + auto compositeKeyColumnsNames = table.composite_key_columns_names(); + REQUIRE(compositeKeyColumnsNames.empty()); + } +} + +TEST_CASE("for_each_foreign_key") { + struct Location { + int id; + std::string place; + std::string country; + std::string city; + int distance; + }; + + struct Visit { + int id; + std::unique_ptr location; + std::unique_ptr user; + int visited_at; + uint8_t mark; + }; + auto locationTable = make_table("location", + make_column("id", &Location::id, primary_key()), + make_column("place", &Location::place), + make_column("country", &Location::country), + make_column("city", &Location::city), + make_column("distance", &Location::distance)); + auto visitTable = make_table("visit", + make_column("id", &Visit::id, primary_key()), + make_column("location", &Visit::location), + make_column("user", &Visit::user), + make_column("visited_at", &Visit::visited_at), + make_column("mark", &Visit::mark), + foreign_key(&Visit::location).references(&Location::id)); + locationTable.for_each_foreign_key([](auto&) { + REQUIRE(false); + }); + auto visitCallsCount = 0; + visitTable.for_each_foreign_key([&visitCallsCount](auto& foreignKey) { + ++visitCallsCount; + REQUIRE(foreignKey == foreign_key(&Visit::location).references(&Location::id)); + }); + REQUIRE(visitCallsCount == 1); +} diff --git a/libs/sqlite_orm-1.8.2/tests/tests.cpp b/libs/sqlite_orm-1.8.2/tests/tests.cpp new file mode 100644 index 0000000..d0e13dc --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/tests.cpp @@ -0,0 +1,237 @@ +#include +#include + +#include // std::vector +#include // std::string +#include // std::unique_ptr +#include // remove +#include // std::iota +#include // std::fill + +using namespace sqlite_orm; + +TEST_CASE("Limits") { + auto storage2 = make_storage("limits.sqlite"); + auto storage = storage2; + storage.sync_schema(); + + SECTION("length") { + auto length = storage.limit.length(); + auto newLength = length - 10; + storage.limit.length(newLength); + length = storage.limit.length(); + REQUIRE(length == newLength); + } + SECTION("sql_length") { + auto sqlLength = storage.limit.sql_length(); + auto newSqlLength = sqlLength - 10; + storage.limit.sql_length(newSqlLength); + sqlLength = storage.limit.sql_length(); + REQUIRE(sqlLength == newSqlLength); + } + SECTION("column") { + auto column = storage.limit.column(); + auto newColumn = column - 10; + storage.limit.column(newColumn); + column = storage.limit.column(); + REQUIRE(column == newColumn); + } + SECTION("expr_depth") { + auto exprDepth = storage.limit.expr_depth(); + auto newExprDepth = exprDepth - 10; + storage.limit.expr_depth(newExprDepth); + exprDepth = storage.limit.expr_depth(); + REQUIRE(exprDepth == newExprDepth); + } + SECTION("compound_select") { + auto compoundSelect = storage.limit.compound_select(); + auto newCompoundSelect = compoundSelect - 10; + storage.limit.compound_select(newCompoundSelect); + compoundSelect = storage.limit.compound_select(); + REQUIRE(compoundSelect == newCompoundSelect); + } + SECTION("vdbe_op") { + auto vdbeOp = storage.limit.vdbe_op(); + auto newVdbe_op = vdbeOp - 10; + storage.limit.vdbe_op(newVdbe_op); + vdbeOp = storage.limit.vdbe_op(); + REQUIRE(vdbeOp == newVdbe_op); + } + SECTION("function_arg") { + auto functionArg = storage.limit.function_arg(); + auto newFunctionArg = functionArg - 10; + storage.limit.function_arg(newFunctionArg); + functionArg = storage.limit.function_arg(); + REQUIRE(functionArg == newFunctionArg); + } + SECTION("attached") { + auto attached = storage.limit.attached(); + auto newAttached = attached - 1; + storage.limit.attached(newAttached); + attached = storage.limit.attached(); + REQUIRE(attached == newAttached); + } + SECTION("like_pattern_length") { + auto likePatternLength = storage.limit.like_pattern_length(); + auto newLikePatternLength = likePatternLength - 10; + storage.limit.like_pattern_length(newLikePatternLength); + likePatternLength = storage.limit.like_pattern_length(); + REQUIRE(likePatternLength == newLikePatternLength); + } + SECTION("variable_number") { + auto variableNumber = storage.limit.variable_number(); + auto newVariableNumber = variableNumber - 10; + storage.limit.variable_number(newVariableNumber); + variableNumber = storage.limit.variable_number(); + REQUIRE(variableNumber == newVariableNumber); + } + SECTION("trigger_depth") { + auto triggerDepth = storage.limit.trigger_depth(); + auto newTriggerDepth = triggerDepth - 10; + storage.limit.trigger_depth(newTriggerDepth); + triggerDepth = storage.limit.trigger_depth(); + REQUIRE(triggerDepth == newTriggerDepth); + } +#if SQLITE_VERSION_NUMBER >= 3008007 + SECTION("worker_threads") { + auto workerThreads = storage.limit.worker_threads(); + auto newWorkerThreads = workerThreads + 1; + storage.limit.worker_threads(newWorkerThreads); + workerThreads = storage.limit.worker_threads(); + REQUIRE(workerThreads == newWorkerThreads); + } +#endif +} + +TEST_CASE("Custom collate") { + using Catch::Matchers::ContainsSubstring; + + struct Item { + int id; + 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( + filename, + make_table("items", make_column("id", &Item::id, primary_key()), make_column("name", &Item::name))); + storage.open_forever(); + storage.sync_schema(); + storage.remove_all(); + storage.insert(Item{0, "Mercury"}); + storage.insert(Item{0, "Mars"}); + 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")); + + 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(); + } + REQUIRE_THROWS_WITH(storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("ototo"))), + ContainsSubstring("no such collation sequence")); + REQUIRE_THROWS_WITH(storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate())), + ContainsSubstring("no such collation sequence")); + REQUIRE_THROWS_WITH(storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("ototo2"))), + ContainsSubstring("no such collation sequence")); + + 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())); + + 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") { + struct Item { + int id; + std::string name; + }; + + auto storage = make_storage( + "vacuum.sqlite", + make_table("items", make_column("id", &Item::id, primary_key()), make_column("name", &Item::name))); + storage.sync_schema(); + storage.insert(Item{0, "One"}); + storage.insert(Item{0, "Two"}); + storage.insert(Item{0, "Three"}); + storage.insert(Item{0, "Four"}); + storage.insert(Item{0, "Five"}); + storage.remove_all(); + storage.vacuum(); +} diff --git a/libs/sqlite_orm-1.8.2/tests/tests2.cpp b/libs/sqlite_orm-1.8.2/tests/tests2.cpp new file mode 100644 index 0000000..871b9c3 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/tests2.cpp @@ -0,0 +1,634 @@ +#include +#include +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::default_delete +#include // free() + +using namespace sqlite_orm; +using std::default_delete; +using std::unique_ptr; + +TEST_CASE("Empty storage") { + auto storage = make_storage("empty.sqlite"); + storage.table_exists("table"); +} + +TEST_CASE("Remove") { + struct Object { + int id; + std::string name; + }; + + { + auto storage = make_storage( + "test_remove.sqlite", + make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name))); + + storage.sync_schema(); + storage.remove_all(); + + auto id1 = storage.insert(Object{0, "Skillet"}); + REQUIRE(storage.count() == 1); + storage.remove(id1); + REQUIRE(storage.count() == 0); + } + { + auto storage = make_storage("test_remove.sqlite", + make_table("objects", + make_column("id", &Object::id), + make_column("name", &Object::name), + primary_key(&Object::id))); + storage.sync_schema(); + storage.remove_all(); + + auto id1 = storage.insert(Object{0, "Skillet"}); + REQUIRE(storage.count() == 1); + storage.remove(id1); + REQUIRE(storage.count() == 0); + } + { + auto storage = make_storage("", + make_table("objects", + make_column("id", &Object::id), + make_column("name", &Object::name), + primary_key(&Object::id, &Object::name))); + storage.sync_schema(); + storage.replace(Object{1, "Skillet"}); + REQUIRE(storage.count() == 1); + storage.remove(1, "Skillet"); + REQUIRE(storage.count() == 0); + + storage.replace(Object{1, "Skillet"}); + storage.replace(Object{2, "Paul Cless"}); + REQUIRE(storage.count() == 2); + storage.remove(1, "Skillet"); + REQUIRE(storage.count() == 1); + } +} +#if SQLITE_VERSION_NUMBER >= 3031000 +TEST_CASE("insert with generated column") { + struct Product { + std::string name; + double price = 0; + double discount = 0; + double tax = 0; + double netPrice = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Product() = default; + Product(std::string name, double price, double discount, double tax, double netPrice) : + name{std::move(name)}, price{price}, discount{discount}, tax{tax}, netPrice{netPrice} {} +#endif + + bool operator==(const Product& other) const { + return this->name == other.name && this->price == other.price && this->discount == other.discount && + this->tax == other.tax && this->netPrice == other.netPrice; + } + }; + auto storage = + make_storage({}, + make_table("products", + make_column("name", &Product::name), + make_column("price", &Product::price), + make_column("discount", &Product::discount), + make_column("tax", &Product::tax), + make_column("net_price", + &Product::netPrice, + generated_always_as(c(&Product::price) * (1 - c(&Product::discount)) * + (1 + c(&Product::tax)))))); + storage.sync_schema(); + Product product{"ABC Widget", 100, 0.05, 0.07, -100}; + SECTION("insert") { + storage.insert(product); + } + SECTION("replace") { + storage.replace(product); + } + + auto allProducts = storage.get_all(); + decltype(allProducts) expectedProducts; + expectedProducts.push_back({"ABC Widget", 100, 0.05, 0.07, 101.65}); + REQUIRE(allProducts == expectedProducts); +} +#endif +TEST_CASE("insert") { + struct Object { + int id; + std::string name; + }; + + struct ObjectWithoutRowid { + int id; + std::string name; + }; + + auto storage = make_storage( + "test_insert.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(); + + for(auto i = 0; i < 100; ++i) { + storage.insert(Object{ + 0, + "Skillet", + }); + REQUIRE(storage.count() == i + 1); + } + + auto initList = { + Object{ + 0, + "Insane", + }, + Object{ + 0, + "Super", + }, + Object{ + 0, + "Sun", + }, + }; + + auto countBefore = storage.count(); + SECTION("straight") { + storage.insert_range(initList.begin(), initList.end()); + REQUIRE(storage.count() == countBefore + static_cast(initList.size())); + + // test empty container + std::vector emptyVector; + REQUIRE_NOTHROW(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(), &std::unique_ptr::operator*); + + // test empty container + std::vector> emptyVector; + REQUIRE_NOTHROW( + storage.insert_range(emptyVector.begin(), emptyVector.end(), &std::unique_ptr::operator*)); + } + + // test insert without rowid + storage.insert(ObjectWithoutRowid{10, "Life"}); + REQUIRE(storage.get(10).name == "Life"); + 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") { + using Catch::Matchers::ContainsSubstring; + + 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; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User() = default; + User(int id) : id{id} {} +#endif + }; + auto storage = make_storage(path, make_table("users", make_column("id", &User::id))); + storage.sync_schema(); + // call before creation + REQUIRE_THROWS_WITH(storage.select(func(4)), ContainsSubstring("no such function")); + + // 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(); +} + +// Wrap std::default_delete in a function +#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION +template +void delete_default(std::conditional_t::value, std::decay_t, T*> o) noexcept( + noexcept(std::default_delete{}(o))) { + std::default_delete{}(o); +} + +// Integral function constant for default deletion +template +using delete_default_t = std::integral_constant), delete_default>; +// Integral function constant variable for default deletion +template +SQLITE_ORM_INLINE_VAR constexpr delete_default_t delete_default_f{}; +#endif + +using free_t = std::integral_constant; +SQLITE_ORM_INLINE_VAR constexpr free_t free_f{}; + +TEST_CASE("obtain_xdestroy_for") { + + using internal::xdestroy_proxy; + + // class yielding a 'xDestroy' function pointer + struct xdestroy_holder { + xdestroy_fn_t xDestroy = free; + + constexpr operator xdestroy_fn_t() const noexcept { + return xDestroy; + } + }; + + // class yielding a function pointer not of type xdestroy_fn_t + struct int_destroy_holder { + using destroy_fn_t = void (*)(int*); + + destroy_fn_t destroy = nullptr; + + constexpr operator destroy_fn_t() const noexcept { + return destroy; + } + }; + + { + constexpr int* int_nullptr = nullptr; +#if !defined(SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION) || \ + (__cpp_constexpr >= 201907L) // Trivial default initialization in constexpr functions + constexpr const int* const_int_nullptr = nullptr; +#endif + + // null_xdestroy_f(int*) + constexpr xdestroy_fn_t xDestroy1 = obtain_xdestroy_for(null_xdestroy_f, int_nullptr); + STATIC_REQUIRE(xDestroy1 == nullptr); + REQUIRE(xDestroy1 == nullptr); + + // free(int*) + constexpr xdestroy_fn_t xDestroy2 = obtain_xdestroy_for(free, int_nullptr); + STATIC_REQUIRE(xDestroy2 == &free); + REQUIRE(xDestroy2 == &free); + + // free_f(int*) + constexpr xdestroy_fn_t xDestroy3 = obtain_xdestroy_for(free_f, int_nullptr); + STATIC_REQUIRE(xDestroy3 == &free); + REQUIRE(xDestroy3 == &free); + +#if __cpp_constexpr >= 201603L // constexpr lambda + // [](void* p){} + constexpr auto lambda4_1 = [](void*) {}; + constexpr xdestroy_fn_t xDestroy4_1 = obtain_xdestroy_for(lambda4_1, int_nullptr); + STATIC_REQUIRE(xDestroy4_1 == lambda4_1); + REQUIRE(xDestroy4_1 == lambda4_1); +#else +#if !defined(_MSC_VER) || (_MSC_VER >= 1914) // conversion of lambda closure to function pointer using `+` + // [](void* p){} + auto lambda4_1 = [](void*) {}; + xdestroy_fn_t xDestroy4_1 = obtain_xdestroy_for(lambda4_1, int_nullptr); + REQUIRE(xDestroy4_1 == lambda4_1); +#endif +#endif + + // [](int* p) { delete p; } +#if __cplusplus >= 202002L // default-constructible non-capturing lambdas + constexpr auto lambda4_2 = [](int* p) { + delete p; + }; + using lambda4_2_t = std::remove_const_t; + constexpr xdestroy_fn_t xDestroy4_2 = obtain_xdestroy_for(lambda4_2, int_nullptr); + STATIC_REQUIRE(xDestroy4_2 == &xdestroy_proxy); + REQUIRE((xDestroy4_2 == &xdestroy_proxy)); +#endif + + // default_delete(int*) + constexpr xdestroy_fn_t xDestroy5 = obtain_xdestroy_for(default_delete{}, int_nullptr); + STATIC_REQUIRE(xDestroy5 == &xdestroy_proxy, int>); + REQUIRE((xDestroy5 == &xdestroy_proxy, int>)); + +#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + // delete_default_f(int*) + constexpr xdestroy_fn_t xDestroy6 = obtain_xdestroy_for(delete_default_f, int_nullptr); + STATIC_REQUIRE(xDestroy6 == &xdestroy_proxy, int>); + REQUIRE((xDestroy6 == &xdestroy_proxy, int>)); + + // delete_default_f(const int*) + constexpr xdestroy_fn_t xDestroy7 = obtain_xdestroy_for(delete_default_f, const_int_nullptr); + STATIC_REQUIRE(xDestroy7 == &xdestroy_proxy, const int>); + REQUIRE((xDestroy7 == &xdestroy_proxy, const int>)); +#endif + +#if __cpp_constexpr >= 201907L // Trivial default initialization in constexpr functions + // xdestroy_holder{ free }(int*) + constexpr xdestroy_fn_t xDestroy8 = obtain_xdestroy_for(xdestroy_holder{free}, int_nullptr); + STATIC_REQUIRE(xDestroy8 == &free); + REQUIRE(xDestroy8 == &free); + + // xdestroy_holder{ free }(const int*) + constexpr xdestroy_fn_t xDestroy9 = obtain_xdestroy_for(xdestroy_holder{free}, const_int_nullptr); + STATIC_REQUIRE(xDestroy9 == &free); + REQUIRE(xDestroy9 == &free); + + // xdestroy_holder{ nullptr }(const int*) + constexpr xdestroy_fn_t xDestroy10 = obtain_xdestroy_for(xdestroy_holder{nullptr}, const_int_nullptr); + STATIC_REQUIRE(xDestroy10 == nullptr); + REQUIRE(xDestroy10 == nullptr); +#endif + + // expressions that do not work +#if 0 + // can't use functions that differ from xdestroy_fn_t + constexpr xdestroy_fn_t xDestroy = obtain_xdestroy_for(delete_default, int_nullptr); + // can't use object yielding a function pointer that differs from xdestroy_fn_t + constexpr xdestroy_fn_t xDestroy = obtain_xdestroy_for(int_destroy_holder{}, int_nullptr); + // successfully takes default_delete, but default_delete statically asserts on a non-complete type `void*` + constexpr xdestroy_fn_t xDestroy = obtain_xdestroy_for(default_delete{}, int_nullptr); + // successfully takes default_delete, but xdestroy_proxy can't call the deleter with a `const int*` + constexpr xdestroy_fn_t xDestroy = obtain_xdestroy_for(default_delete{}, const_int_nullptr); +#endif + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/tests3.cpp b/libs/sqlite_orm-1.8.2/tests/tests3.cpp new file mode 100644 index 0000000..2f945fa --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/tests3.cpp @@ -0,0 +1,415 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Multi order by") { + struct Singer { + int id; + std::string name; + std::string gender; + }; + + auto storage = make_storage("", + make_table("singers", + make_column("id", &Singer::id, primary_key()), + make_column("name", &Singer::name, unique()), + make_column("gender", &Singer::gender))); + storage.sync_schema(); + + storage.insert(Singer{0, "Alexandra Stan", "female"}); + storage.insert(Singer{0, "Inna", "female"}); + storage.insert(Singer{0, "Krewella", "female"}); + storage.insert(Singer{0, "Sting", "male"}); + storage.insert(Singer{0, "Lady Gaga", "female"}); + storage.insert(Singer{0, "Rameez", "male"}); + + { + // test double ORDER BY + auto singers = storage.get_all( + multi_order_by(order_by(&Singer::name).asc().collate_nocase(), order_by(&Singer::gender).desc())); + auto expectedIds = {1, 2, 3, 5, 6, 4}; + REQUIRE(expectedIds.size() == singers.size()); + auto it = expectedIds.begin(); + for(size_t i = 0; i < singers.size(); ++i) { + REQUIRE(*it == singers[i].id); + ++it; + } + } + + // test multi ORDER BY ith singl column with single ORDER BY + { + auto singers = storage.get_all(order_by(&Singer::id).asc()); + auto singers2 = storage.get_all(multi_order_by(order_by(&Singer::id).asc())); + REQUIRE(singers.size() == singers2.size()); + for(size_t i = 0; i < singers.size(); ++i) { + REQUIRE(singers[i].id == singers2[i].id); + } + } +} + +TEST_CASE("Issue 105") { + struct Data { + int str; + }; + + auto storage = make_storage("", make_table("data", make_column("str", &Data::str, primary_key()))); + + storage.sync_schema(); + + Data d{0}; + storage.insert(d); +} + +TEST_CASE("Issue 87") { + struct Data { + uint8_t mDefault = 0; /**< 0=User or 1=Default*/ + uint8_t mAppLang = 0; // en_GB + uint8_t mContentLang1 = 0; // de_DE + uint8_t mContentLang2 = 0; // en_GB + uint8_t mContentLang3 = 0; + uint8_t mContentLang4 = 0; + uint8_t mContentLang5 = 0; + }; + + Data data; + + auto storage = make_storage("", + make_table("data", + make_column("IsDef", &Data::mDefault, primary_key()), + make_column("AL", &Data::mAppLang), + make_column("CL1", &Data::mContentLang1), + make_column("CL2", &Data::mContentLang2), + make_column("CL3", &Data::mContentLang3), + make_column("CL4", &Data::mContentLang4), + make_column("CL5", &Data::mContentLang5))); + storage.sync_schema(); + + storage.update_all(set(c(&Data::mContentLang1) = data.mContentLang1, + c(&Data::mContentLang2) = data.mContentLang2, + c(&Data::mContentLang3) = data.mContentLang3, + c(&Data::mContentLang4) = data.mContentLang4, + c(&Data::mContentLang5) = data.mContentLang5), + where(c(&Data::mDefault) == data.mDefault)); +} + +#ifndef SQLITE_ORM_OMITS_CODECVT +TEST_CASE("Wide string") { + + struct Alphabet { + int id; + std::wstring letters; + }; + + auto storage = make_storage("wideStrings.sqlite", + make_table("alphabets", + make_column("id", &Alphabet::id, primary_key()), + make_column("letters", &Alphabet::letters))); + storage.sync_schema(); + storage.remove_all(); + + std::vector expectedStrings = { + L"ﻌﺾﺒﺑﺏﺖﺘﺗﺕﺚﺜﺛﺙﺞﺠﺟﺝﺢﺤﺣﺡﺦﺨﺧﺥﺪﺩﺬﺫﺮﺭﺰﺯﺲﺴﺳﺱﺶﺸﺷﺵﺺﺼﺻﺹﻀﺿﺽﻂﻄﻃﻁﻆﻈﻇﻅﻊﻋﻉﻎﻐﻏﻍﻒﻔﻓﻑﻖﻘﻗﻕﻚﻜﻛﻙﻞﻠﻟﻝﻢﻤﻣﻡﻦﻨﻧﻥﻪﻬﻫﻩﻮﻭﻲﻴﻳ" + L"ﻱ", // arabic + L"ԱաԲբԳգԴդԵեԶզԷէԸըԹթԺժԻիԼլԽխԾծԿկՀհՁձՂղՃճՄմՅյՆնՇշՈոՉչՊպՋջՌռՍսՎվՏտՐրՑցՒւՓփՔքՕօՖֆ", // armenian + L"АаБбВвГгДдЕеЁёЖжЗзИиЙйКкЛлМмНнОоППРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭэЮюЯя", // russian + L"AaBbCcÇçDdEeFFGgĞğHhIıİiJjKkLlMmNnOoÖöPpRrSsŞşTtUuÜüVvYyZz", // turkish + }; + for(auto& expectedString: expectedStrings) { + auto id = storage.insert(Alphabet{0, expectedString}); + REQUIRE(storage.get(id).letters == expectedString); + } +} +#endif // SQLITE_ORM_OMITS_CODECVT +#ifdef SQLITE_ENABLE_DBSTAT_VTAB +TEST_CASE("dbstat") { + struct User { + int id = 0; + std::string name; + }; + auto storage = + make_storage("dbstat.sqlite", + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)), + make_dbstat_table()); + storage.sync_schema(); + + storage.remove_all(); + + storage.replace(User{1, "Dua Lipa"}); + + auto dbstatRows = storage.get_all(); + std::ignore = dbstatRows; +} +#endif // SQLITE_ENABLE_DBSTAT_VTAB +TEST_CASE("Busy timeout") { + auto storage = make_storage("testBusyTimeout.sqlite"); + storage.busy_timeout(500); +} + +TEST_CASE("Aggregate functions") { + struct User { + int id; + std::string name; + int age; + + void setId(int newValue) { + this->id = newValue; + } + + const int& getId() const { + return this->id; + } + + void setName(std::string newValue) { + this->name = newValue; + } + + const std::string& getName() const { + return this->name; + } + + void setAge(int newValue) { + this->age = newValue; + } + + const int& getAge() const { + return this->age; + } + }; + + auto storage = make_storage("test_aggregate.sqlite", + make_table("users", + make_column("id", &User::id, primary_key()), + make_column("name", &User::name), + make_column("age", &User::age))); + auto storage2 = make_storage("test_aggregate.sqlite", + make_table("users", + make_column("id", &User::getId, &User::setId, primary_key()), + make_column("name", &User::getName, &User::setName), + make_column("age", &User::getAge, &User::setAge))); + storage.sync_schema(); + storage.remove_all(); + + storage.replace(User{1, "Bebe Rexha", 28}); + storage.replace(User{2, "Rihanna", 29}); + storage.replace(User{3, "Cheryl Cole", 34}); + + auto avgId = storage.avg(&User::id); + REQUIRE(avgId == 2); + + auto avgId2 = storage2.avg(&User::getId); + REQUIRE(avgId2 == avgId); + + auto avgId3 = storage2.avg(&User::setId); + REQUIRE(avgId3 == avgId2); + + auto avgRaw = storage.select(avg(&User::id)).front(); + REQUIRE(avgRaw == avgId); + + auto distinctAvg = storage.select(distinct(avg(&User::id))).front(); + REQUIRE(distinctAvg == avgId); + + auto allAvg = storage.select(all(avg(&User::id))).front(); + REQUIRE(allAvg == avgId); + + auto avgRaw2 = storage2.select(avg(&User::getId)).front(); + REQUIRE(avgRaw2 == avgId); + + auto avgRaw3 = storage2.select(avg(&User::setId)).front(); + REQUIRE(avgRaw3 == avgRaw2); + + auto distinctAvg2 = storage2.select(distinct(avg(&User::setId))).front(); + REQUIRE(distinctAvg2 == avgId); + + auto distinctAvg3 = storage2.select(distinct(avg(&User::getId))).front(); + REQUIRE(distinctAvg3 == distinctAvg2); + + auto allAvg2 = storage2.select(all(avg(&User::getId))).front(); + REQUIRE(allAvg2 == avgId); + + auto allAvg3 = storage2.select(all(avg(&User::setId))).front(); + REQUIRE(allAvg3 == allAvg2); + + // next we test that all aggregate functions support arguments bindings. + // This is why id = 1 condition is important here + { + auto avg1 = storage.avg(&User::id, where(is_equal(&User::id, 1))); + REQUIRE(avg1 == 1); + } + { + auto count1 = storage.count(&User::id, where(is_equal(&User::id, 1))); + REQUIRE(count1 == 1); + } + { + auto max1 = storage.max(&User::id, where(is_equal(&User::id, 1))); + REQUIRE(max1); + REQUIRE(*max1 == 1); + } + { + auto min1 = storage.min(&User::id, where(is_equal(&User::id, 1))); + REQUIRE(min1); + REQUIRE(*min1 == 1); + } + { + auto total1 = storage.total(&User::id, where(is_equal(&User::id, 1))); + REQUIRE(total1 == 1); + } + { + auto sum1 = storage.sum(&User::id, where(is_equal(&User::id, 1))); + REQUIRE(sum1); + REQUIRE(*sum1 == 1); + } + { + auto groupConcat = storage.group_concat(&User::name, where(is_equal(&User::id, 1))); + REQUIRE(groupConcat == "Bebe Rexha"); + } +} + +TEST_CASE("Current timestamp") { + auto storage = make_storage(""); + REQUIRE(storage.current_timestamp().size()); + + storage.begin_transaction(); + REQUIRE(storage.current_timestamp().size()); + storage.commit(); +} + +TEST_CASE("Open forever") { + struct User { + int id; + std::string name; + }; + + auto storage = make_storage( + "open_forever.sqlite", + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name))); + storage.sync_schema(); + + storage.remove_all(); + + storage.open_forever(); + + storage.insert(User{1, "Demi"}); + storage.insert(User{2, "Luis"}); + storage.insert(User{3, "Shakira"}); + + storage.open_forever(); + + REQUIRE(storage.count() == 3); + + storage.begin_transaction(); + storage.insert(User{4, "Calvin"}); + storage.commit(); + + REQUIRE(storage.count() == 4); +} + +// appeared after #54 +TEST_CASE("Blob") { + struct BlobData { + std::vector data; + }; + using byte = char; + + 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; + } else { + data[i] = static_cast((rand() % 100) + 1); + } + } + return data; + }; + + auto storage = make_storage("blob.db", make_table("blob", make_column("data", &BlobData::data))); + storage.sync_schema(); + storage.remove_all(); + + size_t size = 100; + auto data = generateData(size); + + // write data + BlobData d; + std::vector v(data, data + size); + d.data = v; + storage.insert(d); + + // read data with get_all + { + auto vd = storage.get_all(); + REQUIRE(vd.size() == 1); + auto& blob = vd.front(); + REQUIRE(blob.data.size() == size); + REQUIRE(std::equal(data, data + size, blob.data.begin())); + } + + // read data with select (single column) + { + auto blobData = storage.select(&BlobData::data); + REQUIRE(blobData.size() == 1); + auto& blob = blobData.front(); + REQUIRE(blob.size() == size); + REQUIRE(std::equal(data, data + size, blob.begin())); + } + + // read data with select (multi column) + { + auto blobData = storage.select(columns(&BlobData::data)); + REQUIRE(blobData.size() == 1); + auto& blob = std::get<0>(blobData.front()); + REQUIRE(blob.size() == size); + REQUIRE(std::equal(data, data + size, blob.begin())); + } + + storage.insert(BlobData{}); + + free(data); +} + +/** + * Created by fixing https://github.com/fnc12/sqlite_orm/issues/42 + */ +TEST_CASE("Escape chars") { + struct Employee { + int id; + std::string name; + int age; + std::string address; + double salary; + }; + auto storage = make_storage("test_escape_chars.sqlite", + make_table("COMPANY", + make_column("INDEX", &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(); + storage.remove_all(); + + storage.insert(Employee{ + 0, + "Paul'l", + 20, + "Sacramento 20", + 40000, + }); + + auto paulL = storage.get_all(where(is_equal(&Employee::name, "Paul'l"))); + + storage.replace(Employee{ + 10, + "Selena", + 24, + "Florida", + 500000, + }); + + auto selena = storage.get(10); + auto selenaMaybe = storage.get_no_throw(10); + selena.name = "Gomez"; + storage.update(selena); + storage.remove(10); +} diff --git a/libs/sqlite_orm-1.8.2/tests/tests4.cpp b/libs/sqlite_orm-1.8.2/tests/tests4.cpp new file mode 100644 index 0000000..3a690df --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/tests4.cpp @@ -0,0 +1,290 @@ +#include +#include +#include // std::count_if +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#include // std::optional +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + +using namespace sqlite_orm; + +TEST_CASE("Unique ptr in update") { + + 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.insert(User{}); + storage.insert(User{}); + storage.insert(User{}); + + { + storage.update_all(set(assign(&User::name, std::make_unique("Nick")))); + REQUIRE(storage.count(where(is_null(&User::name))) == 0); + } + { + std::unique_ptr ptr; + storage.update_all(set(assign(&User::name, std::move(ptr)))); + REQUIRE(storage.count(where(is_not_null(&User::name))) == 0); + } +} + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +TEST_CASE("optional in update") { + + struct User { + int id = 0; + std::optional carYear; // will be empty if user takes the bus. + }; + + auto storage = make_storage( + {}, + make_table("users", make_column("id", &User::id, primary_key()), make_column("car_year", &User::carYear))); + storage.sync_schema(); + + storage.insert(User{}); + storage.insert(User{}); + storage.insert(User{}); + storage.insert(User{0, 2006}); + + REQUIRE(storage.count(where(is_not_null(&User::carYear))) == 1); + + { + storage.update_all(set(assign(&User::carYear, std::optional{}))); + REQUIRE(storage.count(where(is_not_null(&User::carYear))) == 0); + } + { + storage.update_all(set(assign(&User::carYear, 1994))); + REQUIRE(storage.count(where(is_null(&User::carYear))) == 0); + } + { + storage.update_all(set(assign(&User::carYear, nullptr))); + REQUIRE(storage.count(where(is_not_null(&User::carYear))) == 0); + } +} +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + +TEST_CASE("join") { + + struct User { + int id = 0; + std::string name; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + User() = default; + User(int id, std::string name) : id{id}, name{std::move(name)} {} +#endif + }; + + struct Visit { + int id = 0; + int userId = 0; + time_t date = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Visit() = default; + Visit(int id, int userId, time_t date) : id{id}, userId{userId}, date{date} {} +#endif + }; + + 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))); + storage.sync_schema(); + + int id = 1; + User will{id++, "Will"}; + User smith{id++, "Smith"}; + User nicole{id++, "Nicole"}; + + storage.replace(will); + storage.replace(smith); + storage.replace(nicole); + + id = 1; + storage.replace(Visit{id++, will.id, 10}); + storage.replace(Visit{id++, will.id, 20}); + storage.replace(Visit{id++, will.id, 30}); + + storage.replace(Visit{id++, smith.id, 25}); + storage.replace(Visit{id++, smith.id, 35}); + + { + auto rows = storage.get_all(left_join(on(is_equal(&Visit::userId, 2)))); + REQUIRE(rows.size() == 6); + } + { + auto rows = storage.get_all(join(on(is_equal(&Visit::userId, 2)))); + REQUIRE(rows.size() == 6); + } + { + auto rows = storage.get_all(left_outer_join(on(is_equal(&Visit::userId, 2)))); + REQUIRE(rows.size() == 6); + } + { + auto rows = storage.get_all(inner_join(on(is_equal(&Visit::userId, 2)))); + REQUIRE(rows.size() == 6); + } + { + auto rows = storage.get_all(cross_join()); + REQUIRE(rows.size() == 15); + } + { + auto rows = storage.get_all(natural_join()); + REQUIRE(rows.size() == 3); + } +} + +TEST_CASE("two joins") { + struct Statement { + int id_statement; + long date; + }; + + struct Concepto { + int id_concepto; + std::string name; // TFT-SINPE A: 15103-02**-****-8467 + int fkey_account; // { 15103-02**-****-8467, ...} + }; + + 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 + }; + + struct Pais { + int id_pais; + std::string name; + }; + + struct Banco { + int id_bank; + std::string nombre; + std::string ubicacion; + int fkey_pais; + }; + + struct AccountOwner { + int id_owner; + std::string name; + }; + + 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 Categoria { + int id_categoria; + std::string name; + bool is_expense_or_income; + }; + + auto storage = make_storage( + {}, + make_table("Statement", + make_column("id_statement", &Statement::id_statement, primary_key().autoincrement()), + make_column("date", &Statement::date)), + make_table("Categoria", + make_column("id_category", &Categoria::id_categoria, primary_key().autoincrement()), + 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, primary_key().autoincrement()), + 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, primary_key().autoincrement()), + 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, primary_key().autoincrement()), + 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, primary_key().autoincrement()), + make_column("name", &AccountOwner::name, collate_nocase())), + make_table("Transaccion", + make_column("id_transaccion", &Transaccion::id_transaccion, primary_key().autoincrement()), + 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, primary_key().autoincrement()), + make_column("name", &Pais::name, collate_nocase()))); + storage.sync_schema(); + + 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/libs/sqlite_orm-1.8.2/tests/tests5.cpp b/libs/sqlite_orm-1.8.2/tests/tests5.cpp new file mode 100644 index 0000000..fc683d1 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/tests5.cpp @@ -0,0 +1,351 @@ +#include +#include +#include // std::iota + +using namespace sqlite_orm; + +TEST_CASE("Iterate blob") { + struct Test { + int64_t id; + std::vector key; + }; + + struct TestComparator { + bool operator()(const Test& lhs, const Test& rhs) const { + return lhs.id == rhs.id && lhs.key == rhs.key; + } + }; + + auto db = + make_storage("", + make_table("Test", make_column("key", &Test::key), make_column("id", &Test::id, primary_key()))); + db.sync_schema(true); + + std::vector key(255); + iota(key.begin(), key.end(), '\0'); + + Test v{5, key}; + + db.replace(v); + + TestComparator testComparator; + for(auto& obj: db.iterate()) { + REQUIRE(testComparator(obj, v)); + } // test that view_t and iterator_t compile + + for(const auto& obj: db.iterate()) { + REQUIRE(testComparator(obj, v)); + } // test that view_t and iterator_t compile + + { + auto keysCount = db.count(where(c(&Test::key) == key)); + auto keysCountRows = db.select(count(), where(c(&Test::key) == key)); + REQUIRE(keysCountRows.size() == 1); + REQUIRE(keysCountRows.front() == 1); + REQUIRE(keysCount == keysCountRows.front()); + REQUIRE(db.get_all(where(c(&Test::key) == key)).size() == 1); + } + { + int iterationsCount = 0; + for(auto& w: db.iterate(where(c(&Test::key) == key))) { + REQUIRE(testComparator(w, v)); + ++iterationsCount; + } + REQUIRE(iterationsCount == 1); + } +} + +TEST_CASE("Threadsafe") { + threadsafe(); +} + +TEST_CASE("Different getters and setters") { + 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_); + } + }; + + auto filename = "different.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))); + storage0.sync_schema(); + storage0.remove_all(); + + REQUIRE(storage0.count() == 0); + REQUIRE(storage1.count() == 0); + REQUIRE(storage2.count() == 0); + + storage0.replace(User{1, "Da buzz"}); + + REQUIRE(storage0.count() == 1); + REQUIRE(storage1.count() == 1); + REQUIRE(storage2.count() == 1); + + { + auto ids = storage0.select(&User::id); + REQUIRE(ids.size() == 1); + REQUIRE(ids.front() == 1); + auto ids2 = storage1.select(&User::getIdByValConst); + REQUIRE(ids == ids2); + auto ids3 = storage1.select(&User::setIdByVal); + REQUIRE(ids3 == ids2); + auto ids4 = storage2.select(&User::getConstIdByRefConst); + REQUIRE(ids4 == ids3); + auto ids5 = storage2.select(&User::setIdByRef); + REQUIRE(ids5 == ids4); + } + { + auto ids = storage0.select(&User::id, where(is_equal(&User::name, "Da buzz"))); + REQUIRE(ids.size() == 1); + REQUIRE(ids.front() == 1); + auto ids2 = storage1.select(&User::getIdByValConst, where(is_equal(&User::setNameByConstRef, "Da buzz"))); + REQUIRE(ids == ids2); + auto ids3 = storage1.select(&User::setIdByVal, where(is_equal(&User::getNameByVal, "Da buzz"))); + REQUIRE(ids3 == ids2); + auto ids4 = + storage2.select(&User::getConstIdByRefConst, where(is_equal(&User::getConstNameByRefConst, "Da buzz"))); + REQUIRE(ids4 == ids3); + auto ids5 = storage2.select(&User::setIdByRef, where(is_equal(&User::setNameByRef, "Da buzz"))); + REQUIRE(ids5 == ids4); + } + { + auto ids = storage0.select(columns(&User::id), where(is_equal(&User::name, "Da buzz"))); + REQUIRE(ids.size() == 1); + REQUIRE(std::get<0>(ids.front()) == 1); + auto ids2 = + storage1.select(columns(&User::getIdByValConst), where(is_equal(&User::setNameByConstRef, "Da buzz"))); + REQUIRE(ids == ids2); + auto ids3 = storage1.select(columns(&User::setIdByVal), where(is_equal(&User::getNameByVal, "Da buzz"))); + REQUIRE(ids3 == ids2); + auto ids4 = storage2.select(columns(&User::getConstIdByRefConst), + where(is_equal(&User::getConstNameByRefConst, "Da buzz"))); + REQUIRE(ids4 == ids3); + auto ids5 = storage2.select(columns(&User::setIdByRef), where(is_equal(&User::setNameByRef, "Da buzz"))); + REQUIRE(ids5 == ids4); + } + { + auto avgValue = storage0.avg(&User::id); + REQUIRE(avgValue == storage1.avg(&User::getIdByValConst)); + REQUIRE(avgValue == storage1.avg(&User::setIdByVal)); + REQUIRE(avgValue == storage2.avg(&User::getConstIdByRefConst)); + REQUIRE(avgValue == storage2.avg(&User::setIdByRef)); + } + { + auto count = storage0.count(&User::id); + REQUIRE(count == storage1.count(&User::getIdByValConst)); + REQUIRE(count == storage1.count(&User::setIdByVal)); + REQUIRE(count == storage2.count(&User::getConstIdByRefConst)); + REQUIRE(count == storage2.count(&User::setIdByRef)); + } + { + auto groupConcat = storage0.group_concat(&User::id); + REQUIRE(groupConcat == storage1.group_concat(&User::getIdByValConst)); + REQUIRE(groupConcat == storage1.group_concat(&User::setIdByVal)); + REQUIRE(groupConcat == storage2.group_concat(&User::getConstIdByRefConst)); + REQUIRE(groupConcat == storage2.group_concat(&User::setIdByRef)); + } + { + auto arg = "ototo"; + auto groupConcat = storage0.group_concat(&User::id, arg); + REQUIRE(groupConcat == storage1.group_concat(&User::getIdByValConst, arg)); + REQUIRE(groupConcat == storage1.group_concat(&User::setIdByVal, arg)); + REQUIRE(groupConcat == storage2.group_concat(&User::getConstIdByRefConst, arg)); + REQUIRE(groupConcat == storage2.group_concat(&User::setIdByRef, arg)); + } + { + auto max = storage0.max(&User::id); + REQUIRE(max); + REQUIRE(*max == *storage1.max(&User::getIdByValConst)); + REQUIRE(*max == *storage1.max(&User::setIdByVal)); + REQUIRE(*max == *storage2.max(&User::getConstIdByRefConst)); + REQUIRE(*max == *storage2.max(&User::setIdByRef)); + } + { + auto min = storage0.min(&User::id); + REQUIRE(min); + REQUIRE(*min == *storage1.min(&User::getIdByValConst)); + REQUIRE(*min == *storage1.min(&User::setIdByVal)); + REQUIRE(*min == *storage2.min(&User::getConstIdByRefConst)); + REQUIRE(*min == *storage2.min(&User::setIdByRef)); + } + { + auto sum = storage0.sum(&User::id); + REQUIRE(sum); + REQUIRE(*sum == *storage1.sum(&User::getIdByValConst)); + REQUIRE(*sum == *storage1.sum(&User::setIdByVal)); + REQUIRE(*sum == *storage2.sum(&User::getConstIdByRefConst)); + REQUIRE(*sum == *storage2.sum(&User::setIdByRef)); + } + { + auto total = storage0.total(&User::id); + REQUIRE(total == storage1.total(&User::getIdByValConst)); + REQUIRE(total == storage1.total(&User::setIdByVal)); + REQUIRE(total == storage2.total(&User::getConstIdByRefConst)); + REQUIRE(total == storage2.total(&User::setIdByRef)); + } +} + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +TEST_CASE("Dump") { + + struct User { + int id = 0; + std::optional carYear; // will be empty if user takes the bus. + }; + + auto storage = make_storage( + {}, + make_table("users", make_column("id", &User::id, primary_key()), make_column("car_year", &User::carYear))); + storage.sync_schema(); + + auto userId_1 = storage.insert(User{0, {}}); + auto userId_2 = storage.insert(User{0, 2006}); + std::ignore = userId_2; + + REQUIRE(storage.count(where(is_not_null(&User::carYear))) == 1); + + auto rows = storage.select(&User::carYear, where(is_equal(&User::id, userId_1))); + REQUIRE(rows.size() == 1); + REQUIRE(!rows.front().has_value()); + + auto allUsers = storage.get_all(); + REQUIRE(allUsers.size() == 2); + + const std::string dumpUser1 = storage.dump(allUsers[0]); + REQUIRE(dumpUser1 == std::string{"{ id : '1', car_year : 'null' }"}); + + const std::string dumpUser2 = storage.dump(allUsers[1]); + 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_REQUIRE(std::is_same::value); +} + +TEST_CASE("issue822") { + class A { + public: + A() = default; + A(const uint8_t& address, const uint8_t& type, const uint8_t& idx, std::shared_ptr value) : + address(address), type(type), idx(idx), value(std::move(value)){}; + + const uint8_t& getAddress() const { + return this->address; + } + + void setAddress(const uint8_t& address) { + this->address = address; + } + + const uint8_t& getType() const { + return this->type; + } + + void setType(const uint8_t& type) { + this->type = type; + } + + const uint8_t& getIndex() const { + return this->idx; + } + + void setIndex(const uint8_t& index) { + this->idx = index; + } + + std::shared_ptr getValue() const { + return this->value; + } + + void setValue(std::shared_ptr value) { + this->value = std::move(value); + } + + private: + uint8_t address; + uint8_t type; + uint8_t idx; + std::shared_ptr value; + }; + auto storage = make_storage("", + make_table("A", + make_column("address", &A::getAddress, &A::setAddress), + make_column("type", &A::getType, &A::setType), + make_column("idx", &A::getIndex, &A::setIndex), + make_column("value", &A::getValue, &A::setValue), + primary_key(&A::getAddress, &A::getType, &A::getIndex))); + storage.sync_schema(); + storage.replace(A(1, 1, 0, std::make_shared(55.5))); + auto records = storage.get_all(where(c(&A::getAddress) == 1 and c(&A::getType) == 1 and c(&A::getIndex) == 0)); + if(records.size() != 0) { + A a = records[0]; + a.setValue(std::make_shared(10)); + storage.update(a); + records = storage.get_all(where(c(&A::getAddress) == 1 and c(&A::getType) == 1 and c(&A::getIndex) == 0)); + REQUIRE(records.size() == 1); + REQUIRE(*records[0].getValue() == 10); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/transaction_tests.cpp b/libs/sqlite_orm-1.8.2/tests/transaction_tests.cpp new file mode 100644 index 0000000..6df82d2 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/transaction_tests.cpp @@ -0,0 +1,206 @@ +#include +#include + +using namespace sqlite_orm; + +namespace { + struct Object { + int id = 0; + std::string name; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Object() = default; + Object(int id, std::string name) : id{id}, name{std::move(name)} {} +#endif + + bool operator==(const Object& other) const { + return this->id == other.id && this->name == other.name; + } + }; +} + +TEST_CASE("transaction") { + auto filename = "transaction_test.sqlite"; + ::remove(filename); + auto storage = make_storage( + "test_transaction_guard.sqlite", + make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name))); + REQUIRE(!storage.is_opened()); + storage.sync_schema(); + REQUIRE(!storage.is_opened()); + storage.transaction([&] { + storage.insert(Object{0, "Jack"}); + return true; + }); + REQUIRE(!storage.is_opened()); +} + +TEST_CASE("transaction_rollback") { + 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(const std::system_error& e) { + REQUIRE(e.code() == orm_error_code::not_found); + auto countNow = storage.count(); + REQUIRE(countBefore == countNow); + } + } +} + +TEST_CASE("begin_transaction") { + auto storage = make_storage( + {}, + make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name))); + storage.sync_schema(); + + SECTION("begin_transaction") { + storage.begin_transaction(); + } + SECTION("begin_deferred_transaction") { + storage.begin_deferred_transaction(); + } + SECTION("begin_exclusive_transaction") { + storage.begin_exclusive_transaction(); + } + SECTION("begin_immediate_transaction") { + storage.begin_immediate_transaction(); + } + + storage.replace(Object{1, "Leony"}); + + storage.commit(); + + std::vector expected{{1, "Leony"}}; + REQUIRE(storage.get_all() == expected); +} + +TEST_CASE("Transaction guard") { + ::remove("guard.sqlite"); + auto table = + make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name)); + auto storage = make_storage("guard.sqlite", table); + + 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 { + auto guard = storage.transaction_guard(); + + storage.insert(Object{0, "John"}); + + storage.get(-1); + + REQUIRE(false); + } catch(const std::system_error& e) { + REQUIRE(e.code() == orm_error_code::not_found); + auto countNow = storage.count(); + + REQUIRE(countBefore == countNow); + } + } + SECTION("check that one can call other transaction functions without exceptions") { + REQUIRE_NOTHROW(storage.transaction([] { + return false; + })); + } + SECTION("commit explicitly and check that after exception data was saved") { + auto countBefore = storage.count(); + try { + auto guard = storage.transaction_guard(); + storage.insert(Object{0, "John"}); + guard.commit(); + storage.get(-1); + REQUIRE(false); + } catch(const std::system_error& e) { + REQUIRE(e.code() == orm_error_code::not_found); + auto countNow = storage.count(); + REQUIRE(countNow == countBefore + 1); + } + } + SECTION("rollback explicitly") { + auto countBefore = storage.count(); + try { + auto guard = storage.transaction_guard(); + storage.insert(Object{0, "Michael"}); + guard.rollback(); + storage.get(-1); + REQUIRE(false); + } catch(const std::system_error& e) { + REQUIRE(e.code() == orm_error_code::not_found); + auto countNow = storage.count(); + REQUIRE(countNow == countBefore); + } + } + SECTION("commit on exception") { + auto countBefore = storage.count(); + try { + auto guard = storage.transaction_guard(); + guard.commit_on_destroy = true; + storage.insert(Object{0, "Michael"}); + storage.get(-1); + REQUIRE(false); + } catch(const std::system_error& e) { + REQUIRE(e.code() == orm_error_code::not_found); + auto countNow = storage.count(); + REQUIRE(countNow == countBefore + 1); + } + } + SECTION("work without exception") { + auto countBefore = storage.count(); + // transaction scope + { + auto guard = storage.transaction_guard(); + guard.commit_on_destroy = true; + REQUIRE_NOTHROW(storage.insert(Object{0, "Lincoln"})); + } + auto countNow = storage.count(); + REQUIRE(countNow == countBefore + 1); + } + SECTION("std::move ctor") { + std::vector guards; + auto countBefore = storage.count(); + { + auto guard = storage.transaction_guard(); + storage.insert(Object{0, "Lincoln"}); + guards.push_back(std::move(guard)); + REQUIRE(storage.count() == countBefore + 1); + } + REQUIRE(storage.count() == countBefore + 1); + guards.clear(); + REQUIRE(storage.count() == countBefore); + } + SECTION("exception propagated from dtor") { + using Catch::Matchers::ContainsSubstring; + + // create a second database connection + auto storage2 = make_storage("guard.sqlite", table); + auto guard2 = storage2.transaction_guard(); + storage2.get_all(); + + alignas(alignof(internal::transaction_guard_t)) char buffer[sizeof(internal::transaction_guard_t)]; + auto guard = new(&buffer) internal::transaction_guard_t{storage.transaction_guard()}; + storage.insert({}); + guard->commit_on_destroy = true; + REQUIRE_THROWS_WITH(guard->~transaction_guard_t(), ContainsSubstring("database is locked")); + } + ::remove("guard.sqlite"); +} diff --git a/libs/sqlite_orm-1.8.2/tests/trigger_tests.cpp b/libs/sqlite_orm-1.8.2/tests/trigger_tests.cpp new file mode 100644 index 0000000..a5946ac --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/trigger_tests.cpp @@ -0,0 +1,120 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("triggers_basics") { + struct TestInsert { + int id; + std::string text; + int x = 0; + int y = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + TestInsert() = default; + TestInsert(int id, std::string text, int x, int y) : id{id}, text{std::move(text)}, x{x}, y{y} {} +#endif + }; + struct TestUpdate { + int id; + std::string text; + int x = 0; + int y = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + TestUpdate() = default; + TestUpdate(int id, std::string text, int x, int y) : id{id}, text{std::move(text)}, x{x}, y{y} {} +#endif + }; + struct TestDelete { + int id; + std::string text; + int x = 0; + int y = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + TestDelete() = default; + TestDelete(int id, std::string text, int x, int y) : id{id}, text{std::move(text)}, x{x}, y{y} {} +#endif + }; + + TestInsert test_insert{4, "test", 1, 2}; + TestUpdate test_update{4, "test", 1, 2}; + TestDelete test_delete{4, "test", 1, 2}; + auto storage = make_storage( + "", + make_trigger( + "trigger_insert", + after() + .update_of(&TestInsert::x) + .on() + .begin(insert(test_insert), + insert(into(), + columns(&TestInsert::id, &TestInsert::text, &TestInsert::x, &TestInsert::y), + values(std::make_tuple(123, "HelloTrigger", 12, 13))), + insert(test_insert, columns(&TestInsert::id, &TestInsert::text, &TestInsert::x, &TestInsert::y)), + replace(TestInsert{8, "replace", 3, 4}), + replace(test_insert)) + .end()), + make_trigger("trigger_update", + after() + .insert() + .on() + .begin(update(TestUpdate{test_update.id, "update", test_update.x, test_update.y}), + update_all(set(c(&TestUpdate::x) = 42), where(c(&TestUpdate::text) == "update"))) + .end()), + make_trigger( + "trigger_delete", + after() + .insert() + .on() + .begin( + // select(columns(&TestDelete::id), where(greater_than(&TestDelete::x, select(avg(&TestDelete::x))))), // TODO near "(": syntax error: SQL logic error (expression is surronded by parenthesis and SQL returns an error for that) + remove(test_delete.id), + remove_all(where(c(&TestDelete::text) != "test"))) + .end()), + make_table("test_insert", + make_column("sql_id", &TestInsert::id), + make_column("sql_text", &TestInsert::text), + make_column("sql_x", &TestInsert::x), + make_column("sql_y", &TestInsert::y), + primary_key(&TestInsert::id)), + make_table("test_delete", + make_column("id", &TestDelete::id), + make_column("text", &TestDelete::text), + make_column("x", &TestDelete::x), + make_column("y", &TestDelete::y), + primary_key(&TestDelete::id)), + make_table("test_update", + make_column("id", &TestUpdate::id), + make_column("text", &TestUpdate::text), + make_column("x", &TestUpdate::x), + make_column("y", &TestUpdate::y), + primary_key(&TestUpdate::id))); + storage.sync_schema(); + + SECTION("insert") { + storage.insert(TestInsert{0, "SQLite trigger", 8, 2}); + REQUIRE(storage.count() == 1); + auto records = storage.get_all(); + TestInsert t = records[0]; + t.x += 12; + storage.update(t); + REQUIRE(storage.count() == 5); + } + SECTION("update") { + storage.replace(test_update); + REQUIRE(storage.count() == 1); + auto records = storage.get_all(); + TestUpdate t = records[0]; + REQUIRE(t.text == "update"); + REQUIRE(t.x == 42); + } + SECTION("delete") { + storage.replace(test_delete); + storage.insert(TestDelete{0, "test", 1, 2}); + storage.insert(TestDelete{0, "test", 1, 2}); + storage.insert(TestDelete{0, "will be removed", 1, 2}); + REQUIRE(storage.count() == 2); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/tuple_iteration.cpp b/libs/sqlite_orm-1.8.2/tests/tuple_iteration.cpp new file mode 100644 index 0000000..a08c928 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/tuple_iteration.cpp @@ -0,0 +1,81 @@ +#include +#include +#include // std::type_index +#include // std::string +#include // std::remove_pointer + +using namespace sqlite_orm; + +TEST_CASE("tuple iteration") { + using namespace internal; + + std::vector expected; + std::vector types; + SECTION("iterate_tuple with tuple instance") { + auto lambda = [&types](const auto& item) { + types.emplace_back(typeid(item)); + }; + SECTION("empty") { + std::tuple<> tuple; + iterate_tuple(tuple, lambda); + } + SECTION("int") { + std::tuple tuple; + iterate_tuple(tuple, lambda); + expected = {typeid(int)}; + } + SECTION("std::string, long") { + std::tuple tuple; + iterate_tuple(tuple, lambda); + expected = {typeid(std::string), typeid(long)}; + } + SECTION("index selection") { + constexpr size_t selectedIdx = 1; + std::tuple tuple; + iterate_tuple(tuple, std::index_sequence{}, lambda); + expected = {typeid(long)}; + } + } + SECTION("iterate_tuple with no tuple instance") { + auto lambda = [&types](auto* itemPointer) { + using Item = std::remove_pointer_t; + types.emplace_back(typeid(Item)); + }; + SECTION("empty") { + iterate_tuple>(lambda); + } + SECTION("int") { + iterate_tuple>(lambda); + expected = {typeid(int)}; + } + SECTION("std::string, long") { + iterate_tuple>(lambda); + expected = {typeid(std::string), typeid(long)}; + } + } + REQUIRE(expected == types); +} + +TEST_CASE("creation from tuple") { + using namespace internal; + using Catch::Matchers::Equals; + + std::tuple tpl{"abc", "xyz"}; + SECTION("identity") { + std::vector expected{get<0>(tpl), get<1>(tpl)}; + auto strings = create_from_tuple>(tpl); + REQUIRE_THAT(strings, Equals(expected)); + } + SECTION("projected") { + std::vector expected{get<0>(tpl).c_str(), get<1>(tpl).c_str()}; + auto strings = create_from_tuple>(tpl, &std::string::c_str); + REQUIRE_THAT(strings, Equals(expected)); + } + SECTION("index selection") { + constexpr size_t selectedIdx = 1; + std::vector expected{get(tpl).c_str()}; + auto strings = + create_from_tuple>(tpl, std::index_sequence{}, &std::string::c_str); + REQUIRE_THAT(strings, Equals(expected)); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/unique_cases/delete_with_two_fields.cpp b/libs/sqlite_orm-1.8.2/tests/unique_cases/delete_with_two_fields.cpp new file mode 100644 index 0000000..558bd3e --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/unique_cases/delete_with_two_fields.cpp @@ -0,0 +1,48 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("delete with two fields") { + struct Device { + std::string serialNumber; + std::string deviceId; + }; + auto storage = make_storage({}, + make_table("devices", + make_column("serial_number", &Device::serialNumber), + make_column("device_id", &Device::deviceId))); + storage.sync_schema(); + + SECTION("none") { + storage.remove_all(where(in(std::make_tuple(&Device::serialNumber, &Device::deviceId), + values(std::make_tuple("abc", "123"), std::make_tuple("def", "456"))))); + + storage.remove_all( + where(in(std::make_tuple(&Device::serialNumber, &Device::deviceId), + values(std::vector>{std::make_tuple("abc", "123"), + std::make_tuple("def", "456")})))); + REQUIRE(storage.count() == 0); + } + SECTION("two devices") { + Device device_abc{"abc", "123"}; + Device device_def{"def", "456"}; + + storage.replace(device_abc); + REQUIRE(storage.count() == 1); + storage.replace(device_def); + REQUIRE(storage.count() == 2); + + SECTION("static condition") { + storage.remove_all(where(in(std::make_tuple(&Device::serialNumber, &Device::deviceId), + values(std::make_tuple("abc", "123"), std::make_tuple("def", "456"))))); + } + SECTION("dynamic condition") { + storage.remove_all( + where(in(std::make_tuple(&Device::serialNumber, &Device::deviceId), + values(std::vector>{std::make_tuple("abc", "123"), + std::make_tuple("def", "456")})))); + } + REQUIRE(storage.count() == 0); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/unique_cases/get_all_with_two_tables.cpp b/libs/sqlite_orm-1.8.2/tests/unique_cases/get_all_with_two_tables.cpp new file mode 100644 index 0000000..91d17ac --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/unique_cases/get_all_with_two_tables.cpp @@ -0,0 +1,89 @@ +#include +#include + +using namespace sqlite_orm; + +namespace { + struct Pattern { + std::string value; + +#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED + Pattern() = default; + Pattern(std::string value) : value{std::move(value)} {} +#endif + }; + struct Item { + int id = 0; + std::string attributes; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + Item() = default; + Item(int id, std::string attributes) : id{id}, attributes{std::move(attributes)} {} +#endif + }; + + inline bool operator==(const Item& lhs, const Item& rhs) { + return lhs.id == rhs.id && lhs.attributes == rhs.attributes; + } +} + +TEST_CASE("get_all with two tables") { + using Catch::Matchers::UnorderedEquals; + + auto storage = make_storage( + {}, + make_table("patterns", make_column("pattern", &Pattern::value)), + make_table("items", make_column("id", &Item::id, primary_key()), make_column("attributes", &Item::attributes))); + storage.sync_schema(); + + const Item item1{1, "one"}; + const Item item2{2, "two"}; + const Item item3{3, "three"}; + const Item item4{4, "nwa"}; + + storage.replace(item1); + storage.replace(item2); + storage.replace(item3); + + SECTION("straight insert") { + std::vector patterns; + patterns.push_back({"n"}); + patterns.push_back({"w"}); + + storage.begin_transaction(); + storage.insert_range(patterns.begin(), patterns.end()); + } + SECTION("pointers insert") { + std::vector> patterns; + patterns.push_back(std::make_unique("n")); + patterns.push_back(std::make_unique("w")); + + storage.begin_transaction(); + storage.insert_range(patterns.begin(), patterns.end(), &std::unique_ptr::operator*); + } + { + auto rows = storage.select(&Item::id, where(like(&Item::attributes, conc(conc("%", &Pattern::value), "%")))); + REQUIRE_THAT(rows, UnorderedEquals({1, 2})); + + auto items = storage.get_all(where( + in(&Item::id, select(&Item::id, where(like(&Item::attributes, conc(conc("%", &Pattern::value), "%"))))))); + REQUIRE_THAT(items, UnorderedEquals({item1, item2})); + } + { + storage.replace(Item{4, "nwa"}); + auto rows = storage.select(&Item::id, + where(like(&Item::attributes, conc(conc("%", &Pattern::value), "%"))), + group_by(&Item::id), + having(is_equal(count(&Pattern::value), select(count())))); + REQUIRE_THAT(rows, UnorderedEquals({4})); + + auto items = storage.get_all( + where(in(&Item::id, + select(&Item::id, + where(like(&Item::attributes, conc(conc("%", &Pattern::value), "%"))), + group_by(&Item::id), + having(is_equal(count(&Pattern::value), select(count()))))))); + REQUIRE_THAT(items, UnorderedEquals({item4})); + } + storage.rollback(); +} diff --git a/libs/sqlite_orm-1.8.2/tests/unique_cases/index_named_table_with_fk.cpp b/libs/sqlite_orm-1.8.2/tests/unique_cases/index_named_table_with_fk.cpp new file mode 100644 index 0000000..7d3b18e --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/unique_cases/index_named_table_with_fk.cpp @@ -0,0 +1,79 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("index named table") { + using std::string; + struct OrderTable { + string order_number; + string start_time; + string end_time; + int type; + int renewal_times; + int status; + float amount; + string openid; + string nickname; + string parking_space_number; + string license_plate_number; + string parking_code; + string phone_number; + string appointment_time; + string arrival_time; + string leave_parking_lot_time; + string parking_time; + string leave_parking_space_time; + string in_barrier_gate_number; + string out_barrier_gate_number; + int parking_duration; + string create_time; + }; + + struct pay_info { + int id; + + int pay_type; + int payment; + float amount; + string pay_time; + std::unique_ptr order_number; + }; + + auto storage = + make_storage("mysqlite.sqlite", + make_table("order", + make_column("order_number", &OrderTable::order_number, primary_key()), + make_column("start_time", &OrderTable::start_time), + make_column("end_time", &OrderTable::end_time), + make_column("type", &OrderTable::type), + make_column("renewal_times", &OrderTable::renewal_times), + make_column("status", &OrderTable::status), + make_column("amount", &OrderTable::amount), + make_column("openid", &OrderTable::openid), + make_column("nickname", &OrderTable::nickname), + make_column("parking_space_number", &OrderTable::parking_space_number), + make_column("license_plate_number", &OrderTable::license_plate_number), + make_column("parking_code", &OrderTable::parking_code), + make_column("phone_number", &OrderTable::phone_number), + make_column("appointment_time", &OrderTable::appointment_time), + make_column("arrival_time", &OrderTable::arrival_time), + make_column("leave_parking_lot_time", &OrderTable::leave_parking_lot_time), + make_column("parking_time", &OrderTable::parking_time), + make_column("leave_parking_space_time", &OrderTable::leave_parking_space_time), + make_column("in_barrier_gate_number", &OrderTable::in_barrier_gate_number), + make_column("out_barrier_gate_number", &OrderTable::out_barrier_gate_number), + make_column("parking_duration", &OrderTable::parking_duration), + make_column("create_time", &OrderTable::create_time)), + + make_table("pay_info", + make_column("id", &pay_info::id, primary_key()), + + make_column("pay_type", &pay_info::pay_type), + make_column("payment", &pay_info::payment), + make_column("amount", &pay_info::amount), + make_column("pay_time", &pay_info::pay_time), + make_column("order_number", &pay_info::order_number), + foreign_key(&pay_info::order_number).references(&OrderTable::order_number))); + storage.sync_schema(); +} diff --git a/libs/sqlite_orm-1.8.2/tests/unique_cases/issue525.cpp b/libs/sqlite_orm-1.8.2/tests/unique_cases/issue525.cpp new file mode 100644 index 0000000..1b71e02 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/unique_cases/issue525.cpp @@ -0,0 +1,35 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("issue525") { + struct User { + int id; + std::string firstName; + std::string lastName; + int birthDate; + std::unique_ptr imageUrl; + int typeId; + }; + + struct UserType { + int id; + std::string name; + }; + + auto filename = "db1.sqlite"; + auto storage = make_storage(filename, + make_table("users", + make_column("id", &User::id, primary_key().autoincrement()), + make_column("first_name", &User::firstName), + make_column("last_name", &User::lastName), + make_column("birth_date", &User::birthDate), + make_column("image_url", &User::imageUrl), + make_column("type_id", &User::typeId)), + make_table("user_types", + make_column("id", &UserType::id, primary_key().autoincrement()), + make_column("name", &UserType::name, default_value("name_placeholder")))); + + storage.sync_schema(); +} diff --git a/libs/sqlite_orm-1.8.2/tests/unique_cases/issue663.cpp b/libs/sqlite_orm-1.8.2/tests/unique_cases/issue663.cpp new file mode 100644 index 0000000..b4ab613 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/unique_cases/issue663.cpp @@ -0,0 +1,183 @@ +#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 + +namespace { + struct User1 { + bool operator==(const User1& rhs) const { + return std::tie(id, name, age, email) == std::tie(rhs.id, rhs.name, rhs.age, rhs.email); + } + bool operator!=(const User1& rhs) const { + return !(rhs == *this); + } + int id; + std::string name; + int age; + std::string email; + }; +} + +TEST_CASE("Issue 663 - pk inside") { + + auto storage = make_storage("", + make_table("users", + make_column("id", &User1::id, primary_key()), + make_column("name", &User1::name), + make_column("age", &User1::age), + make_column("email", &User1::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") { + + auto storage = make_storage("", + make_table("users", + make_column("id", &User1::id), + make_column("name", &User1::name), + make_column("age", &User1::age), + make_column("email", &User1::email, default_value("dummy@email.com")), + primary_key(&User1::id))); + storage.sync_schema(); + + SECTION("insert") { + primary_key_case::insertSection(storage); + } + + SECTION("insert_range") { + primary_key_case::insertRangeSection(storage); + } +} + +namespace { + struct User2 { + std::string id; + std::string name; + }; +} +TEST_CASE("Issue 663 - pk outside, with default") { + auto storage = + make_storage("", + make_table("users", + make_column("id", &User2::id, default_value(default_value_case::defaultID)), + make_column("name", &User2::name, default_value(default_value_case::defaultName)), + primary_key(&User2::id, &User2::name))); + storage.sync_schema(); + + SECTION("insert") { + default_value_case::insertSection(storage); + } + + SECTION("insert_range") { + default_value_case::insertRangeSection(storage); + } +} + +namespace { + struct User3 { + std::string id; + }; +} +TEST_CASE("Issue 663 - pk inside, with default") { + auto storage = + make_storage("", make_table("users", make_column("id", &User3::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") { + auto storage = + make_storage("", make_table("users", make_column("id", &User3::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() == make_error_code(orm_error_code::cannot_use_default_value)); + REQUIRE(storage.count() == 0); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/unique_cases/issue86.cpp b/libs/sqlite_orm-1.8.2/tests/unique_cases/issue86.cpp new file mode 100644 index 0000000..ddae778 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/unique_cases/issue86.cpp @@ -0,0 +1,25 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Issue 86") { + struct Data { + uint8_t mUsed = 0; + int mId = 0; + int mCountryId = 0; + int mLangId = 0; + std::string mName; + }; + + auto storage = make_storage("", + make_table("cities", + make_column("U", &Data::mUsed), + make_column("Id", &Data::mId), + make_column("CntId", &Data::mCountryId), + make_column("LId", &Data::mLangId), + make_column("N", &Data::mName))); + storage.sync_schema(); + std::string CurrCity = "ototo"; + auto vms = storage.select(columns(&Data::mId, &Data::mLangId), where(like(&Data::mName, CurrCity))); +} diff --git a/libs/sqlite_orm-1.8.2/tests/unique_cases/issue937.cpp b/libs/sqlite_orm-1.8.2/tests/unique_cases/issue937.cpp new file mode 100644 index 0000000..51ece11 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/unique_cases/issue937.cpp @@ -0,0 +1,88 @@ +#include +#include + +using namespace sqlite_orm; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +TEST_CASE("issue937") { + struct Employee { + int m_empno; + std::string m_ename; + std::string m_job; + std::optional m_mgr; + std::string m_hiredate; + double m_salary; + std::optional m_commission; + int m_depno; + }; + + struct Department { + int m_deptno; + std::string m_deptname; + std::string m_loc; + }; + + struct EmpBonus { + int m_id; + int m_empno; + std::string m_received; // date + int m_type; + }; + + using namespace sqlite_orm; + + ::remove("SQLCookbook.sqlite"); + auto storage = make_storage("SQLCookbook.sqlite", + make_table("Emp", + make_column("empno", &Employee::m_empno, primary_key().autoincrement()), + make_column("ename", &Employee::m_ename), + make_column("job", &Employee::m_job), + make_column("mgr", &Employee::m_mgr), + make_column("hiredate", &Employee::m_hiredate), + make_column("salary", &Employee::m_salary), + make_column("comm", &Employee::m_commission), + make_column("depno", &Employee::m_depno), + foreign_key(&Employee::m_depno).references(&Department::m_deptno)), + make_table("Dept", + make_column("deptno", &Department::m_deptno, primary_key().autoincrement()), + make_column("deptname", &Department::m_deptname), + make_column("loc", &Department::m_loc)), + make_table("Emp_bonus", + make_column("id", &EmpBonus::m_id, primary_key().autoincrement()), + make_column("empno", &EmpBonus::m_empno), + make_column("received", &EmpBonus::m_received), + make_column("type", &EmpBonus::m_type), + foreign_key(&EmpBonus::m_empno).references(&Employee::m_empno))); + storage.sync_schema(); + + struct NamesAlias : alias_tag { + static const std::string& get() { + static const std::string res = "ENAME_AND_DNAME"; + return res; + } + }; + + auto statement = storage.prepare(select(union_all( + select(columns(as(&Department::m_deptname), as_optional(&Department::m_deptno))), + select(union_all(select(columns(quote("--------------------"), std::optional())), + select(columns(as(&Employee::m_ename), as_optional(&Employee::m_depno)))))))); + auto sql = statement.expanded_sql(); + auto rows = storage.execute(statement); + { // issue953 + auto expression = select( + columns(&Employee::m_empno, &Employee::m_ename, &Employee::m_job, &Employee::m_salary, &Employee::m_depno), + where(c(std::make_tuple(&Employee::m_ename, &Employee::m_job, &Employee::m_salary)) + .in(select(columns(&Employee::m_ename, &Employee::m_job, &Employee::m_salary), + where(c(&Employee::m_job) == "Clerk"))))); + REQUIRE_NOTHROW(storage.prepare(expression)); + } + { // issue969 + auto expression = select( + columns(&Employee::m_empno, &Employee::m_ename, &Employee::m_job, &Employee::m_salary, &Employee::m_depno), + where(in(std::make_tuple(&Employee::m_ename, &Employee::m_job, &Employee::m_salary), + intersect(select(columns(&Employee::m_ename, &Employee::m_job, &Employee::m_salary)), + select(columns(&Employee::m_ename, &Employee::m_job, &Employee::m_salary), + where(c(&Employee::m_job) == "Clerk")))))); + REQUIRE_NOTHROW(storage.prepare(expression)); + } +} +#endif diff --git a/libs/sqlite_orm-1.8.2/tests/unique_cases/join_iterator_ctor_compilation_error.cpp b/libs/sqlite_orm-1.8.2/tests/unique_cases/join_iterator_ctor_compilation_error.cpp new file mode 100644 index 0000000..03d4e02 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/unique_cases/join_iterator_ctor_compilation_error.cpp @@ -0,0 +1,39 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Join iterator ctor compilation error") { + struct Tag { + int objectId; + std::string text; + }; + + auto storage = + make_storage("join_error.sqlite", + make_table("tags", make_column("object_id", &Tag::objectId), make_column("text", &Tag::text))); + storage.sync_schema(); + + auto offs = 0; + auto lim = 5; + storage.select(columns(&Tag::text, count(&Tag::text)), + group_by(&Tag::text), + order_by(count(&Tag::text)).desc(), + limit(offs, lim)); + { + auto statement = storage.prepare(select(columns(&Tag::text, count(&Tag::text)), + group_by(&Tag::text), + order_by(count(&Tag::text)).desc(), + limit(offs, lim))); + REQUIRE(get<0>(statement) == offs); + REQUIRE(get<1>(statement) == lim); + } + { + auto statement = storage.prepare(select(columns(&Tag::text, count(&Tag::text)), + group_by(&Tag::text), + order_by(count(&Tag::text)).desc(), + limit(lim, offset(offs)))); + REQUIRE(get<0>(statement) == lim); + REQUIRE(get<1>(statement) == offs); + } +} diff --git a/libs/sqlite_orm-1.8.2/tests/unique_cases/prepare_get_all_with_case.cpp b/libs/sqlite_orm-1.8.2/tests/unique_cases/prepare_get_all_with_case.cpp new file mode 100644 index 0000000..60d21dc --- /dev/null +++ b/libs/sqlite_orm-1.8.2/tests/unique_cases/prepare_get_all_with_case.cpp @@ -0,0 +1,46 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Prepare with case") { + struct UserProfile { + int id = 0; + std::string firstName; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + UserProfile() = default; + UserProfile(int id, std::string firstName) : id{id}, firstName{std::move(firstName)} {} +#endif + }; + + auto storage = make_storage({}, + make_table("user_profiles", + make_column("id", &UserProfile::id, primary_key()), + make_column("first_name", &UserProfile::firstName))); + storage.sync_schema(); + + const std::string name = "Iggy"; + + SECTION("case string name") { + auto statement = storage.prepare(get_all(where(is_equal( + &UserProfile::firstName, + case_(name).when((length(name) > 0), then(name)).else_(&UserProfile::firstName).end())))); + std::ignore = statement; + } + SECTION("case string") { + auto statement = storage.prepare(get_all(where( + case_().when(length(name) > 0, then(like(&UserProfile::firstName, name))).else_(true).end()))); + std::ignore = statement; + } + SECTION("case int") { + storage.insert(UserProfile{1, "Iggy"}); + storage.insert(UserProfile{2, "I%"}); + auto rows = storage.get_all(); + auto statement3 = storage.prepare(get_all( + where(is_equal(&UserProfile::id, + case_(c(&UserProfile::id) * 2).when(2, then(1)).when(4, then(2)).else_(3).end())))); + auto r2 = storage.execute(statement3); + REQUIRE(r2.size() == rows.size()); // does not filter at all + } +} diff --git a/libs/sqlite_orm-1.8.2/third_party/amalgamate/CHANGES.md b/libs/sqlite_orm-1.8.2/third_party/amalgamate/CHANGES.md new file mode 100755 index 0000000..c12e203 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/third_party/amalgamate/CHANGES.md @@ -0,0 +1,11 @@ +The following changes have been made to the code with respect to : + +- Resolved inspection results from PyCharm: + - replaced tabs with spaces + - added encoding annotation + - reindented file to remove trailing whitespaces + - unused import `sys` + - membership check + - made function from `_is_within` + - removed unused variable `actual_path` + - normalized paths diff --git a/libs/sqlite_orm-1.8.2/third_party/amalgamate/LICENSE.md b/libs/sqlite_orm-1.8.2/third_party/amalgamate/LICENSE.md new file mode 100755 index 0000000..7fe9cf0 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/third_party/amalgamate/LICENSE.md @@ -0,0 +1,27 @@ +amalgamate.py - Amalgamate C source and header files +Copyright (c) 2012, Erik Edlund + +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 Erik Edlund, 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 OWNER 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. diff --git a/libs/sqlite_orm-1.8.2/third_party/amalgamate/README.md b/libs/sqlite_orm-1.8.2/third_party/amalgamate/README.md new file mode 100755 index 0000000..975ca0b --- /dev/null +++ b/libs/sqlite_orm-1.8.2/third_party/amalgamate/README.md @@ -0,0 +1,66 @@ + +# amalgamate.py - Amalgamate C source and header files + +Origin: https://bitbucket.org/erikedlund/amalgamate + +Mirror: https://github.com/edlund/amalgamate + +`amalgamate.py` aims to make it easy to use SQLite-style C source and header +amalgamation in projects. + +For more information, please refer to: http://sqlite.org/amalgamation.html + +## Here be dragons + +`amalgamate.py` is quite dumb, it only knows the bare minimum about C code +required in order to be able to handle trivial include directives. It can +produce weird results for unexpected code. + +Things to be aware of: + +`amalgamate.py` will not handle complex include directives correctly: + + #define HEADER_PATH "path/to/header.h" + #include HEADER_PATH + +In the above example, `path/to/header.h` will not be included in the +amalgamation (HEADER_PATH is never expanded). + +`amalgamate.py` makes the assumption that each source and header file which +is not empty will end in a new-line character, which is not immediately +preceded by a backslash character (see 5.1.1.2p1.2 of ISO C99). + +`amalgamate.py` should be usable with C++ code, but raw string literals from +C++11 will definitely cause problems: + + R"delimiter(Terrible raw \ data " #include )delimiter" + R"delimiter(Terrible raw \ data " escaping)delimiter" + +In the examples above, `amalgamate.py` will stop parsing the raw string literal +when it encounters the first quotation mark, which will produce unexpected +results. + +## Installing amalgamate.py + +Python v.2.7.0 or higher is required. + +`amalgamate.py` can be tested and installed using the following commands: + + ./test.sh && sudo -k cp ./amalgamate.py /usr/local/bin/ + +## Using amalgamate.py + + amalgamate.py [-v] -c path/to/config.json -s path/to/source/dir \ + [-p path/to/prologue.(c|h)] + + * The `-c, --config` option should specify the path to a JSON config file which + lists the source files, include paths and where to write the resulting + amalgamation. Have a look at `test/source.c.json` and `test/include.h.json` + to see two examples. + + * The `-s, --source` option should specify the path to the source directory. + This is useful for supporting separate source and build directories. + + * The `-p, --prologue` option should specify the path to a file which will be + added to the beginning of the amalgamation. It is optional. + diff --git a/libs/sqlite_orm-1.8.2/third_party/amalgamate/amalgamate.py b/libs/sqlite_orm-1.8.2/third_party/amalgamate/amalgamate.py new file mode 100755 index 0000000..40da016 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/third_party/amalgamate/amalgamate.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +# coding=utf-8 + +# amalgamate.py - Amalgamate C source and header files. +# Copyright (c) 2012, Erik Edlund +# +# 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 Erik Edlund, 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 OWNER 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. + +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import argparse +import datetime +import json +import os +import re + + +class Amalgamation(object): + + # Prepends self.source_path to file_path if needed. + def actual_path(self, file_path): + if not os.path.isabs(file_path): + file_path = os.path.join(self.source_path, file_path) + return file_path + + # Search included file_path in self.include_paths and + # in source_dir if specified. + def find_included_file(self, include_path, source_dir): + search_dirs = self.include_paths[:] + if source_dir: + search_dirs.insert(0, source_dir) + + for search_dir in search_dirs: + search_path = os.path.normpath(os.path.join(search_dir, include_path)) + if os.path.isfile(self.actual_path(search_path)): + return search_path + return None + + def __init__(self, args): + with open(args.config, 'r') as f: + config = json.loads(f.read()) + for key in config: + setattr(self, key, config[key]) + self.target = os.path.normpath(self.target) + self.sources = [os.path.normpath(src_path) for src_path in self.sources] + self.include_paths = [os.path.normpath(i) for i in self.include_paths] + + self.verbose = args.verbose == "yes" + self.prologue = args.prologue + self.source_path = args.source_path + self.included_files = [] + + # Generate the amalgamation and write it to the target file. + def generate(self): + amalgamation = "" + + if self.prologue: + with open(self.prologue, 'r') as f: + amalgamation += datetime.datetime.now().strftime(f.read()) + + if self.verbose: + print("Config:") + print(" target = {0}".format(self.target)) + print(" working_dir = {0}".format(os.getcwd())) + print(" include_paths = {0}".format(self.include_paths)) + print("Creating amalgamation:") + for file_path in self.sources: + # Do not check the include paths while processing the source + # list, all given source paths must be correct. + # actual_path = self.actual_path(file_path) + print(" - processing \"{0}\"".format(file_path)) + t = TranslationUnit(file_path, self, True) + amalgamation += t.content + + with open(self.target, mode='w', newline='\n') as f: + f.write(amalgamation) + + print("...done!\n") + if self.verbose: + print("Files processed: {0}".format(self.sources)) + print("Files included: {0}".format(self.included_files)) + print("") + + +def _is_within(match, matches): + for m in matches: + if match.start() > m.start() and \ + match.end() < m.end(): + return True + return False + + +class TranslationUnit(object): + # // C++ comment. + cpp_comment_pattern = re.compile(r"//.*?\n") + + # /* C comment. */ + c_comment_pattern = re.compile(r"/\*.*?\*/", re.S) + + # "complex \"stri\\\ng\" value". + string_pattern = re.compile("[^']" r'".*?(?<=[^\\])"', re.S) + + # Handle simple include directives. Support for advanced + # directives where macros and defines needs to expanded is + # not a concern right now. + include_pattern = re.compile( + r'#\s*include\s+(<|")(?P.*?)("|>)', re.S) + + # #pragma once + pragma_once_pattern = re.compile(r'#\s*pragma\s+once', re.S) + + # Search for pattern in self.content, add the match to + # contexts if found and update the index accordingly. + def _search_content(self, index, pattern, contexts): + match = pattern.search(self.content, index) + if match: + contexts.append(match) + return match.end() + return index + 2 + + # Return all the skippable contexts, i.e., comments and strings + def _find_skippable_contexts(self): + # Find contexts in the content in which a found include + # directive should not be processed. + skippable_contexts = [] + + # Walk through the content char by char, and try to grab + # skippable contexts using regular expressions when found. + i = 1 + content_len = len(self.content) + while i < content_len: + j = i - 1 + current = self.content[i] + previous = self.content[j] + + if current == '"': + # String value. + i = self._search_content(j, self.string_pattern, + skippable_contexts) + elif current == '*' and previous == '/': + # C style comment. + i = self._search_content(j, self.c_comment_pattern, + skippable_contexts) + elif current == '/' and previous == '/': + # C++ style comment. + i = self._search_content(j, self.cpp_comment_pattern, + skippable_contexts) + else: + # Skip to the next char. + i += 1 + + return skippable_contexts + + # Returns True if the match is within list of other matches + + # Removes pragma once from content + def _process_pragma_once(self): + content_len = len(self.content) + if content_len < len("#include "): + return 0 + + # Find contexts in the content in which a found include + # directive should not be processed. + skippable_contexts = self._find_skippable_contexts() + + pragmas = [] + pragma_once_match = self.pragma_once_pattern.search(self.content) + while pragma_once_match: + if not _is_within(pragma_once_match, skippable_contexts): + pragmas.append(pragma_once_match) + + pragma_once_match = self.pragma_once_pattern.search(self.content, + pragma_once_match.end()) + + # Handle all collected pragma once directives. + prev_end = 0 + tmp_content = '' + for pragma_match in pragmas: + tmp_content += self.content[prev_end:pragma_match.start()] + prev_end = pragma_match.end() + tmp_content += self.content[prev_end:] + self.content = tmp_content + + # Include all trivial #include directives into self.content. + def _process_includes(self): + content_len = len(self.content) + if content_len < len("#include "): + return 0 + + # Find contexts in the content in which a found include + # directive should not be processed. + skippable_contexts = self._find_skippable_contexts() + + # Search for include directives in the content, collect those + # which should be included into the content. + includes = [] + include_match = self.include_pattern.search(self.content) + while include_match: + if not _is_within(include_match, skippable_contexts): + include_path = include_match.group("path") + search_same_dir = include_match.group(1) == '"' + found_included_path = self.amalgamation.find_included_file( + include_path, self.file_dir if search_same_dir else None) + if found_included_path: + includes.append((include_match, found_included_path)) + + include_match = self.include_pattern.search(self.content, + include_match.end()) + + # Handle all collected include directives. + prev_end = 0 + tmp_content = '' + for include in includes: + include_match, found_included_path = include + tmp_content += self.content[prev_end:include_match.start()] + tmp_content += "// {0}\n".format(include_match.group(0)) + if found_included_path not in self.amalgamation.included_files: + if self.amalgamation.verbose: + print(" * including \"{0}\"".format(found_included_path)) + t = TranslationUnit(found_included_path, self.amalgamation, False) + tmp_content += t.content + prev_end = include_match.end() + tmp_content += self.content[prev_end:] + self.content = tmp_content + + return len(includes) + + # Make all content processing + def _process(self): + if not self.is_root: + self._process_pragma_once() + self._process_includes() + + def __init__(self, file_path, amalgamation, is_root): + self.file_path = file_path + self.file_dir = os.path.dirname(file_path) + self.amalgamation = amalgamation + self.is_root = is_root + + self.amalgamation.included_files.append(self.file_path) + + actual_path = self.amalgamation.actual_path(file_path) + if not os.path.isfile(actual_path): + raise IOError("File not found: \"{0}\"".format(file_path)) + with open(actual_path, 'r') as f: + self.content = f.read() + self._process() + + +def main(): + description = "Amalgamate C source and header files." + usage = " ".join([ + "amalgamate.py", + "[-v]", + "-c path/to/config.json", + "-s path/to/source/dir", + "[-p path/to/prologue.(c|h)]" + ]) + argsparser = argparse.ArgumentParser( + description=description, usage=usage) + + argsparser.add_argument("-v", "--verbose", dest="verbose", + choices=["yes", "no"], metavar="", help="be verbose") + + argsparser.add_argument("-c", "--config", dest="config", + required=True, metavar="", help="path to a JSON config file") + + argsparser.add_argument("-s", "--source", dest="source_path", + required=True, metavar="", help="source code path") + + argsparser.add_argument("-p", "--prologue", dest="prologue", + required=False, metavar="", help="path to a C prologue file") + + amalgamation = Amalgamation(argsparser.parse_args()) + amalgamation.generate() + + +if __name__ == "__main__": + main() diff --git a/libs/sqlite_orm-1.8.2/third_party/amalgamate/config.json b/libs/sqlite_orm-1.8.2/third_party/amalgamate/config.json new file mode 100755 index 0000000..11a7c23 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/third_party/amalgamate/config.json @@ -0,0 +1,43 @@ +{ + "project": "SQLite ORM", + "target": "include/sqlite_orm/sqlite_orm.h", + "sources": [ + "dev/functional/start_macros.h", + "dev/functional/config.h", + "dev/type_traits.h", + "dev/error_code.h", + "dev/type_printer.h", + "dev/collate_argument.h", + "dev/constraints.h", + "dev/type_is_nullable.h", + "dev/operators.h", + "dev/column.h", + "dev/field_printer.h", + "dev/conditions.h", + "dev/alias.h", + "dev/core_functions.h", + "dev/typed_comparator.h", + "dev/select_constraints.h", + "dev/table_info.h", + "dev/triggers.h", + "dev/statement_finalizer.h", + "dev/arithmetic_tag.h", + "dev/pointer_value.h", + "dev/statement_binder.h", + "dev/row_extractor.h", + "dev/util.h", + "dev/sync_schema_result.h", + "dev/index.h", + "dev/rowid.h", + "dev/table.h", + "dev/storage_impl.h", + "dev/default_value_extractor.h", + "dev/storage.h", + "dev/get_prepared_statement.h", + "dev/carray.h", + "dev/dbstat.h", + "dev/interface_definitions.h", + "dev/functional/finish_macros.h" + ], + "include_paths": [ "dev" ] +} diff --git a/libs/sqlite_orm-1.8.2/vcpkg/triplets/x64-linux.cmake b/libs/sqlite_orm-1.8.2/vcpkg/triplets/x64-linux.cmake new file mode 100644 index 0000000..55bb50b --- /dev/null +++ b/libs/sqlite_orm-1.8.2/vcpkg/triplets/x64-linux.cmake @@ -0,0 +1,5 @@ +set(VCPKG_TARGET_ARCHITECTURE x64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE dynamic) + +set(VCPKG_CMAKE_SYSTEM_NAME Linux) diff --git a/libs/sqlite_orm-1.8.2/vcpkg/triplets/x64-osx.cmake b/libs/sqlite_orm-1.8.2/vcpkg/triplets/x64-osx.cmake new file mode 100644 index 0000000..21fc205 --- /dev/null +++ b/libs/sqlite_orm-1.8.2/vcpkg/triplets/x64-osx.cmake @@ -0,0 +1,6 @@ +set(VCPKG_TARGET_ARCHITECTURE x64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE dynamic) + +set(VCPKG_CMAKE_SYSTEM_NAME Darwin) +set(VCPKG_OSX_ARCHITECTURES x86_64) diff --git a/src/filemanager.cpp b/src/filemanager.cpp new file mode 100644 index 0000000..f4193ba --- /dev/null +++ b/src/filemanager.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2024 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include "blt/std/time.h" +#include + +namespace db +{ + + void guild_fs_manager::message_create(blt::u64 channel_id, blt::u64 msg_id, std::string_view content, std::string_view username, + std::string_view display_name, const std::vector& attachments) + { + + } + + void guild_fs_manager::message_delete(blt::u64 channel_id, blt::u64 msg_id) + { + + } + + void guild_fs_manager::message_update(blt::u64 channel_id, blt::u64 msg_id, std::string_view new_content) + { + + } + + void guild_fs_manager::flush(std::string new_path) + { + complete = 3; + check_for_guild_updates(); + flusher.flush_channels(channels); + complete--; + path = std::move(new_path); + } + + void guild_fs_manager::check_for_channel_updates() + { + bot.guild_get(dpp::snowflake(guildID), [this](const dpp::confirmation_callback_t& con) { + auto guild = con.get(); + f_server_name.seekg(0); + + }); + } + + void guild_fs_manager::check_for_guild_updates() + { + + } + + void guild_fs_manager::await_completions() + { + BLT_DEBUG("Awaiting all requests to complete"); + while (complete > 0) + {} + complete = 0; + BLT_DEBUG("Awaiting completed"); + } + + void fs_manager::flush() + { + BLT_TIME_FUNC(now); + if (current_day != now.tm_yday) + { + is_flushing = true; + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + current_day = now.tm_yday; + + for (auto& gm : guild_handlers) + { + gm.second->flush(create_path(gm.first)); + gm.second->await_completions(); + } + + is_flushing = false; + } + } + + fs_manager::fs_manager(std::string_view archive_path, dpp::cluster& bot): bot(bot), archive_path(archive_path) + { + BLT_INFO("Creating with path '%s'", this->archive_path.c_str()); + BLT_TIME_FUNC(now); + current_day = now.tm_yday; + blt::string::replaceAll(this->archive_path, "\\", "/"); + if (!blt::string::ends_with(archive_path, '/')) + this->archive_path += "/"; + BLT_INFO("Modified path '%s'", this->archive_path.c_str()); + } + + std::string fs_manager::create_path(blt::u64 guildID) + { + BLT_TIME_FUNC(now); + auto local_guild_path = archive_path + "raw/" + std::to_string(guildID) + "/"; + auto local_day_path = local_guild_path + std::to_string(now.tm_year + 1900) + "-" + std::to_string(now.tm_mon + 1) + + "-" + std::to_string(now.tm_mday) + "/"; + std::filesystem::create_directories(local_day_path); + return local_day_path; + } + + void channel_flusher::flush_channels(HASHMAP>& map) + { + channels_to_flush.reserve(map.size()); + for (auto& v : map) + channels_to_flush.push_back(std::move(v.second)); + map.clear(); + } +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 12352ea..ba9de5b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,40 +3,43 @@ #include #include #include +#include + int main(int argc, const char** argv) { blt::arg_parse parser; - parser.addArgument( - blt::arg_builder("-t", "--token").setDefault("uWU").setAction(blt::arg_action_t::STORE).setHelp("The discord bot token").setRequired() - .build()); - -// for (int i = 0; i < argc; i++) -// BLT_TRACE(argv[i]); + parser.addArgument(blt::arg_builder("-t", "--token").setAction(blt::arg_action_t::STORE).setHelp("The discord bot token").build()); + parser.addArgument(blt::arg_builder("-p", "--path").setAction(blt::arg_action_t::STORE).setHelp("Path to store the archive data").build()); auto args = parser.parse_args(argc, argv); - BLT_TRACE(args.get("--token")); - BLT_TRACE(argv[argc - 1]); - dpp::cluster bot(argv[argc - 1], dpp::i_default_intents | dpp::i_message_content | dpp::i_all_intents); + dpp::cluster bot(args.get("token"), dpp::i_default_intents | dpp::i_message_content | dpp::i_all_intents); + + db::fs_manager manager{args.get("path"), bot}; + + bot.start_timer([&manager](auto) { + manager.flush(); + }, 60); + + bot.on_message_delete([&bot](const dpp::message_delete_t& event) { - bot.on_slashcommand([](auto event) { - if (event.command.get_command_name() == "ping") - { - event.reply("Pong!"); - } }); - bot.on_ready([&bot](auto event) { - if (dpp::run_once()) - { - bot.global_command_create(dpp::slashcommand("ping", "Ping pong!", bot.me.id)); - } + bot.on_message_delete_bulk([&bot](const dpp::message_delete_bulk_t& event) { + + }); + + bot.on_message_update([&bot](const dpp::message_update_t& event) { + }); bot.on_message_create([&bot](const dpp::message_create_t& event) { if (event.msg.id == bot.me.id) return; + bot.guild_get(event.msg.guild_id, [](const dpp::confirmation_callback_t& con) { + BLT_INFO("Guild name: %s", con.get().name.c_str()); + }); BLT_TRACE("(%s)> %s", event.msg.author.username.c_str(), event.msg.content.c_str()); for (const dpp::attachment& attach : event.msg.attachments) { @@ -55,6 +58,4 @@ int main(int argc, const char** argv) bot.start(dpp::st_wait); return 0; - std::cout << "Hello, World!" << std::endl; - return 0; }