diff --git a/.github/workflows/clang-format-lint.yaml b/.github/workflows/clang-format-lint.yaml index 56ff33638..fa76253c1 100644 --- a/.github/workflows/clang-format-lint.yaml +++ b/.github/workflows/clang-format-lint.yaml @@ -10,5 +10,3 @@ jobs: - uses: actions/checkout@v1 - name: clang-format lint uses: DoozyX/clang-format-lint-action@v0.3.1 - with: - exclude: ./include diff --git a/.gitignore b/.gitignore index a2c5a7b71..f5cff1a34 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ examples/simple_neural_network.cpp cmake-build-debug/ .idea/ + +/compile diff --git a/.travis.yml b/.travis.yml index b5faca2d7..7811f8aa8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ # Defaults os: linux -dist: xenial +dist: bionic matrix: include: @@ -38,18 +38,18 @@ matrix: env: - SQLITE_ORM_OMITS_CODECVT: ON - - name: "[C++14] AppleClang-10.0.1" - os: osx - osx_image: xcode10.2 - language: cpp - env: - - SQLITE_ORM_OMITS_CODECVT: ON - addons: - homebrew: - packages: - - catch2 - - ninja - update: true +# - name: "[C++14] AppleClang-10.0.1" +# os: osx +# osx_image: xcode10.2 +# language: cpp +# env: +# - SQLITE_ORM_OMITS_CODECVT: ON +# addons: +# homebrew: +# packages: +# - catch2 +# - ninja +# update: true # - name: "[C++14] LLVM/Clang (latest)" # os: osx @@ -71,19 +71,19 @@ matrix: # - CXX: /usr/local/opt/llvm/bin/clang++ # - SQLITE_ORM_OMITS_CODECVT: ON - - name: "[C++14] GCC-6" - os: osx - osx_image: xcode10.2 - addons: - homebrew: - packages: - - gcc@6 - - catch2 - - ninja - update: true - env: - - CC: gcc-6 - - CXX: g++-6 +# - name: "[C++14] GCC-6" +# os: osx +# osx_image: xcode10.2 +# addons: +# homebrew: +# packages: +# - gcc@6 +# - catch2 +# - ninja +# update: true +# env: +# - CC: gcc-6 +# - CXX: g++-6 - name: "[C++17] GCC-9" addons: @@ -111,19 +111,19 @@ matrix: - CXX: g++-7 - SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_17=ON" - - name: "[C++17] AppleClang-10.0.1" - os: osx - osx_image: xcode10.2 - language: cpp - env: - - SQLITE_ORM_OMITS_CODECVT: ON - - SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_17=ON" - addons: - homebrew: - packages: - - catch2 - - ninja - update: true +# - name: "[C++17] AppleClang-10.0.1" +# os: osx +# osx_image: xcode10.2 +# language: cpp +# env: +# - SQLITE_ORM_OMITS_CODECVT: ON +# - SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_17=ON" +# addons: +# homebrew: +# packages: +# - catch2 +# - ninja +# update: true # - name: "[C++17] LLVM/Clang (latest)" # os: osx @@ -151,16 +151,12 @@ before_install: if [[ ${TRAVIS_OS_NAME} == "osx" ]]; then export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH" # Use coreutils from homebrew. fi - -install: - - | - # Catch2 test framework - if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then - git clone --depth=1 --quiet https://github.com/catchorg/Catch2.git - cd Catch2 - cmake -Bbuild -H. -DBUILD_TESTING=OFF - sudo env "PATH=$PATH" cmake --build ./build --target install - fi + # Add ppa repo for cmake and delete cmake 3.12.4 folder of travis + - wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | sudo tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null + - sudo apt-add-repository -y 'deb https://apt.kitware.com/ubuntu/ bionic main' + - sudo apt-get update -qq -y + - sudo apt-get install -y cmake + - sudo rm -r /usr/local/cmake-3.12.4/ # scripts to run before build before_script: diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ffbdf3d3..dd86d2f1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,4 @@ -cmake_minimum_required (VERSION 3.2) -cmake_policy(SET CMP0057 NEW) +cmake_minimum_required (VERSION 3.16.0) # Handling C++ standard version to use option(SQLITE_ORM_ENABLE_CXX_17 "Enable C++ 17" OFF) @@ -23,32 +22,29 @@ set(PACKAGE_VERSION ${sqlite_orm_VERSION}) project("sqlite_orm" VERSION ${PACKAGE_VERSION}) + set(CMAKE_VERBOSE_MAKEFILE ON) message(STATUS "Configuring ${CMAKE_PROJECT_NAME} ${sqlite_orm_VERSION}") -set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/build/cmake") - -# Build time options are defined here -include(DefineInstallationPaths) - -# Generate the SqliteOrmConfig.cmake module -include(GenerateConfigModule) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") -set(ProjectName "SqliteOrm") +include(CTest) -option(SqliteOrm_BuildTests "Build sqlite_orm unit tests" ON) +### 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_sources(sqlite_orm INTERFACE $) -target_include_directories(sqlite_orm INTERFACE - $ - $) +target_include_directories(sqlite_orm INTERFACE $) include(ucm) @@ -69,26 +65,14 @@ ucm_print_flags() message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") # Tests -include(CTest) -if(SqliteOrm_BuildTests AND BUILD_TESTING) - enable_testing() +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) add_subdirectory(tests) endif() -add_subdirectory(examples) - -install(TARGETS sqlite_orm EXPORT "${ProjectName}Targets" - INCLUDES DESTINATION "${INCLUDE_INSTALL_DIR}" COMPONENT Development - PUBLIC_HEADER DESTINATION "${INCLUDE_INSTALL_DIR}" COMPONENT Development) - -install(FILES "include/sqlite_orm/sqlite_orm.h" - DESTINATION "${INCLUDE_INSTALL_DIR}" COMPONENT Development) - -export(EXPORT "${ProjectName}Targets" - FILE "${CMAKE_CURRENT_BINARY_DIR}/${ProjectName}/${ProjectName}Targets.cmake" - NAMESPACE "sqlite_orm::") +option(BUILD_EXAMPLES ON) +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_EXAMPLES) + add_subdirectory(examples) +endif() -install(EXPORT "${ProjectName}Targets" - FILE "${ProjectName}Targets.cmake" - NAMESPACE "sqlite_orm::" - DESTINATION "${CMAKE_INSTALL_DIR}/sqlite_orm") +### Packaging +add_subdirectory(packaging) diff --git a/README.md b/README.md index 159731b63..968133108 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,6 @@ SQLite ORM light header only library for modern C++ * **Easy integration** - single header only lib. * **The only dependency** - libsqlite3 * **C++ standard code style** -* **No undefined behaviour** - if something goes wrong lib throws an exception * **In memory database support** - provide `:memory:` or empty filename * **COLLATE support** * **Limits setting/getting support** @@ -754,31 +753,56 @@ For more details please check the project [wiki](https://github.com/fnc12/sqlite # Installation -Use popular package manager like [vcpkg](https://github.com/Microsoft/vcpkg) and just install it with `vcpkg install sqlite-orm` command. +**Note**: Installation is not necessary if you plan to use the fetchContent method, see below in Usage. -Or you can use below instructions +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 -mkdir compile -cd compile -cmake .. -cmake --build . -sudo make install +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). -then you can just include `sqlite_orm.h` that is installed in system-wide header files location or in case you use cmake build system you can just add below commands in CMakeLists.txt +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(sqlite_orm CONFIG REQUIRED) +find_package(SqliteOrm REQUIRED) + target_link_libraries(main PRIVATE sqlite_orm::sqlite_orm) -target_include_directories(main PRIVATE ${SQLITE_ORM_INCLUDE_DIR}) ``` -Or just put `include/sqlite_orm/sqlite_orm.h` into you folder with headers. Also it is recommended to keep project libraries' sources in separate folders cause there is no dominant normal dependency manager for C++ yet. +## 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). -* libsqlite3 linked to your binary +* 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/TODO.md b/TODO.md index b86065dc5..315e99e47 100644 --- a/TODO.md +++ b/TODO.md @@ -2,20 +2,19 @@ `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 +* `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 -* reusing of prepared statements - useful for query optimisation * explicit FROM for subqueries in FROM argument -* backup API https://www.sqlite.org/backup.html -* busy handler https://sqlite.org/c3ref/busy_handler.html * CREATE VIEW and other view operations https://sqlite.org/lang_createview.html * triggers * query static check for correct order (e.g. `GROUP BY` after `WHERE`) * `WINDOW` * `UPSERT` https://www.sqlite.org/lang_UPSERT.html -* `CHECK` constraint * `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) +* generated columns https://www.sqlite.org/gencol.html +* named constraints: constraint can have name `CREATE TABLE heroes(id INTEGER CONSTRAINT pk PRIMARY KEY)` Please feel free to add any feature that isn't listed here and not implemented yet. diff --git a/appveyor.yml b/appveyor.yml index 866a6c507..7b097a7b1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -44,7 +44,7 @@ init: install: - C:\Tools\vcpkg\vcpkg integrate install - - vcpkg install catch2:%PLATFORM%-windows + - vcpkg install sqlite3:%PLATFORM%-windows # scripts to run before build before_build: diff --git a/build/cmake/DefineInstallationPaths.cmake b/build/cmake/DefineInstallationPaths.cmake deleted file mode 100644 index 3406e68e4..000000000 --- a/build/cmake/DefineInstallationPaths.cmake +++ /dev/null @@ -1,12 +0,0 @@ -# Define the default install paths -set(BIN_INSTALL_DIR "bin" CACHE PATH "The binary install dir (default: bin)") -set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH "The library install dir (default: lib${LIB_SUFFIX})") -set(INCLUDE_INSTALL_DIR "include" CACHE PATH "The library install dir (default: include)") -set(CMAKE_INSTALL_DIR "lib/cmake" CACHE PATH "The subdirectory to install cmake config files (default: cmake)") -set(PKGCONFIG_INSTALL_DIR "lib/pkgconfig" CACHE PATH "The subdirectory to install pkgconfig config files (default: lib/pkgconfig)") -set(DOC_INSTALL_DIR "share/doc" CACHE PATH "The subdirectory to install documentation files (default: share/doc)") -set(prefix "${CMAKE_INSTALL_PREFIX}") -set(exec_prefix "${CMAKE_INSTALL_PREFIX}/bin") -set(libdir "${CMAKE_INSTALL_PREFIX}/lib") -set(includedir "${CMAKE_INSTALL_PREFIX}/include") -set(cmakedir "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DIR}") diff --git a/build/cmake/GenerateConfigModule.cmake b/build/cmake/GenerateConfigModule.cmake deleted file mode 100644 index 3c5143d3e..000000000 --- a/build/cmake/GenerateConfigModule.cmake +++ /dev/null @@ -1,24 +0,0 @@ -include(CMakePackageConfigHelpers) - -set(PACKAGE_INCLUDE_INSTALL_DIR "${includedir}/sqlite_orm") -set(PACKAGE_CMAKE_INSTALL_DIR "${cmakedir}/sqlite_orm") - -# In CYGWIN enviroment below commands does not work properly -if (NOT CYGWIN) - configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/build/cmake/SqliteOrmConfig.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/SqliteOrmConfig.cmake" - INSTALL_DESTINATION "${CMAKE_INSTALL_DIR}/sqlite_orm" - PATH_VARS - PACKAGE_INCLUDE_INSTALL_DIR - PACKAGE_CMAKE_INSTALL_DIR - ) - - 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 "${CMAKE_INSTALL_DIR}/sqlite_orm") -endif() \ No newline at end of file diff --git a/build/cmake/SqliteOrmConfig.cmake.in b/build/cmake/SqliteOrmConfig.cmake.in deleted file mode 100644 index a6b6f20b9..000000000 --- a/build/cmake/SqliteOrmConfig.cmake.in +++ /dev/null @@ -1,22 +0,0 @@ -set(SQLITE_ORM_VERSION ${sqlite_orm_VERSION}) - -@PACKAGE_INIT@ - -set_and_check(SQLITE_ORM_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@") -set_and_check(SQLITE_ORM_CMAKE_DIR "@PACKAGE_CMAKE_INSTALL_DIR@") - -if (NOT TARGET sqlite_orm::sqlite_orm) - include("${SQLITE_ORM_CMAKE_DIR}/SqliteOrmTargets.cmake") -endif() - -set(SQLITE_ORM_LIBRARIES sqlite_orm::sqlite_orm) - -if ("${SQLITE_ORM_LIBRARIES}" STREQUAL "") - message(FATAL_ERROR "sqlite_orm libraries were not found") -endif() - -if (NOT SqliteOrm_FIND_QUIETLY) - message(STATUS "Found sqlite_orm: ${PACKAGE_PREFIX_DIR}") -endif() - -check_required_components(SqliteOrm) diff --git a/cmake/SqliteOrmConfig.cmake.in b/cmake/SqliteOrmConfig.cmake.in new file mode 100644 index 000000000..e0635d28a --- /dev/null +++ b/cmake/SqliteOrmConfig.cmake.in @@ -0,0 +1,4 @@ +include(CMakeFindDependencyMacro) +find_dependency(SQLite3) + +include(${CMAKE_CURRENT_LIST_DIR}/SqliteOrmTargets.cmake) diff --git a/build/cmake/ucm.cmake b/cmake/ucm.cmake similarity index 100% rename from build/cmake/ucm.cmake rename to cmake/ucm.cmake diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt new file mode 100644 index 000000000..376ede119 --- /dev/null +++ b/dependencies/CMakeLists.txt @@ -0,0 +1,13 @@ +include(FetchContent) + +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) + FetchContent_Declare( + catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + ) +endif() + +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) + add_subdirectory(catch2) +endif() +add_subdirectory(sqlite3) \ No newline at end of file diff --git a/dependencies/catch2/CMakeLists.txt b/dependencies/catch2/CMakeLists.txt new file mode 100644 index 000000000..efbcfd8af --- /dev/null +++ b/dependencies/catch2/CMakeLists.txt @@ -0,0 +1 @@ +FetchContent_MakeAvailable(catch2) diff --git a/dependencies/sqlite3/CMakeLists.txt b/dependencies/sqlite3/CMakeLists.txt new file mode 100644 index 000000000..028c757ed --- /dev/null +++ b/dependencies/sqlite3/CMakeLists.txt @@ -0,0 +1,2 @@ +# find_package exposes targets only globally, but caches them. So call find_package again in the directory of usage +find_package(SQLite3 REQUIRED) \ No newline at end of file diff --git a/dev/aggregate_functions.h b/dev/aggregate_functions.h deleted file mode 100644 index 5d8340e6d..000000000 --- a/dev/aggregate_functions.h +++ /dev/null @@ -1,272 +0,0 @@ -#pragma once - -namespace sqlite_orm { - - namespace aggregate_functions { - - struct avg_string { - operator std::string() const { - return "AVG"; - } - }; - - /** - * Result of avg(...) call. - * T is an argument type - */ - template - struct avg_t : avg_string { - using arg_type = T; - - arg_type arg; - - avg_t(arg_type arg_) : arg(std::move(arg_)) {} - }; - - struct count_string { - operator std::string() const { - return "COUNT"; - } - }; - - /** - * Result of count(...) call. - * T is an argument type - */ - template - struct count_t : count_string { - using arg_type = T; - - arg_type arg; - - count_t(arg_type arg_) : arg(std::move(arg_)) {} - }; - - /** - * T is use to specify type explicitly for queries like - * SELECT COUNT(*) FROM table_name; - * T can be omitted with void. - */ - template - struct count_asterisk_t : count_string { - using type = T; - }; - - /** - * The same thing as count() but without T arg. - * Is used in cases like this: - * SELECT cust_code, cust_name, cust_city, grade - * FROM customer - * WHERE grade=2 AND EXISTS - * (SELECT COUNT(*) - * FROM customer - * WHERE grade=2 - * GROUP BY grade - * HAVING COUNT(*)>2); - * `c++` - * auto rows = - * storage.select(columns(&Customer::code, &Customer::name, &Customer::city, &Customer::grade), - * where(is_equal(&Customer::grade, 2) - * and exists(select(count(), - * where(is_equal(&Customer::grade, 2)), - * group_by(&Customer::grade), - * having(greater_than(count(), 2)))))); - */ - struct count_asterisk_without_type : count_string {}; - - struct sum_string { - operator std::string() const { - return "SUM"; - } - }; - - /** - * Result of sum(...) call. - * T is an argument type - */ - template - struct sum_t : sum_string { - using arg_type = T; - - arg_type arg; - - sum_t(arg_type arg_) : arg(std::move(arg_)) {} - }; - - struct total_string { - operator std::string() const { - return "TOTAL"; - } - }; - - /** - * Result of total(...) call. - * T is an argument type - */ - template - struct total_t : total_string { - using arg_type = T; - - arg_type arg; - - total_t(arg_type arg_) : arg(std::move(arg_)) {} - }; - - struct max_string { - operator std::string() const { - return "MAX"; - } - }; - - /** - * Result of max(...) call. - * T is an argument type - */ - template - struct max_t : max_string { - using arg_type = T; - - arg_type arg; - - max_t(arg_type arg_) : arg(std::move(arg_)) {} - }; - - struct min_string { - operator std::string() const { - return "MIN"; - } - }; - - /** - * Result of min(...) call. - * T is an argument type - */ - template - struct min_t : min_string { - using arg_type = T; - - arg_type arg; - - min_t(arg_type arg_) : arg(std::move(arg_)) {} - }; - - struct group_concat_string { - operator std::string() const { - return "GROUP_CONCAT"; - } - }; - - /** - * Result of group_concat(X) call. - * T is an argument type - */ - template - struct group_concat_single_t : group_concat_string { - using arg_type = T; - - arg_type arg; - - group_concat_single_t(arg_type arg_) : arg(std::move(arg_)) {} - }; - - struct group_concat_double_base : group_concat_string { - std::string y; - - group_concat_double_base(std::string y_) : y(move(y_)) {} - }; - - /** - * Result of group_concat(X, Y) call. - * T is an argument type - */ - template - struct group_concat_double_t : group_concat_double_base { - using arg_type = T; - - arg_type arg; - - group_concat_double_t(arg_type arg_, std::string y) : - group_concat_double_base(move(y)), arg(std::move(arg_)) {} - }; - } - - /** - * AVG(X) aggregate function. - */ - template - aggregate_functions::avg_t avg(T t) { - return {std::move(t)}; - } - - /** - * COUNT(X) aggregate function. - */ - template - aggregate_functions::count_t count(T t) { - return {std::move(t)}; - } - - /** - * COUNT(*) without FROM function. - */ - inline aggregate_functions::count_asterisk_without_type count() { - return {}; - } - - /** - * COUNT(*) with FROM function. Specified type T will be serializeed as - * a from argument. - */ - template - aggregate_functions::count_asterisk_t count() { - return {}; - } - - /** - * SUM(X) aggregate function. - */ - template - aggregate_functions::sum_t sum(T t) { - return {std::move(t)}; - } - - /** - * MAX(X) aggregate function. - */ - template - aggregate_functions::max_t max(T t) { - return {std::move(t)}; - } - - /** - * MIN(X) aggregate function. - */ - template - aggregate_functions::min_t min(T t) { - return {std::move(t)}; - } - - /** - * TOTAL(X) aggregate function. - */ - template - aggregate_functions::total_t total(T t) { - return {std::move(t)}; - } - - /** - * GROUP_CONCAT(X) aggregate function. - */ - template - aggregate_functions::group_concat_single_t group_concat(T t) { - return {std::move(t)}; - } - - /** - * GROUP_CONCAT(X, Y) aggregate function. - */ - template - aggregate_functions::group_concat_double_t group_concat(T t, Y y) { - return {std::move(t), std::move(y)}; - } -} diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h index 61cae1764..30c35020f 100644 --- a/dev/ast_iterator.h +++ b/dev/ast_iterator.h @@ -9,6 +9,7 @@ #include "tuple_helper.h" #include "core_functions.h" #include "prepared_statement.h" +#include "values.h" namespace sqlite_orm { @@ -56,8 +57,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::where_t; + struct ast_iterator, void> { + using node_type = where_t; template void operator()(const node_type &where, const L &l) const { @@ -66,9 +67,7 @@ namespace sqlite_orm { }; template - struct ast_iterator< - T, - typename std::enable_if::value>::type> { + struct ast_iterator::value>::type> { using node_type = T; template @@ -100,8 +99,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::in_t; + struct ast_iterator, void> { + using node_type = in_t; template void operator()(const node_type &in, const C &l) const { @@ -154,9 +153,9 @@ namespace sqlite_orm { } }; - template - struct ast_iterator, void> { - using node_type = get_all_t; + template + struct ast_iterator, void> { + using node_type = get_all_t; template void operator()(const node_type &get, const L &l) const { @@ -230,8 +229,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::having_t; + struct ast_iterator, void> { + using node_type = having_t; template void operator()(const node_type &hav, const L &l) const { @@ -240,8 +239,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::cast_t; + struct ast_iterator, void> { + using node_type = cast_t; template void operator()(const node_type &c, const L &l) const { @@ -250,8 +249,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::exists_t; + struct ast_iterator, void> { + using node_type = exists_t; template void operator()(const node_type &e, const L &l) const { @@ -260,8 +259,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::like_t; + struct ast_iterator, void> { + using node_type = like_t; template void operator()(const node_type &lk, const L &l) const { @@ -274,8 +273,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::glob_t; + struct ast_iterator, void> { + using node_type = glob_t; template void operator()(const node_type &lk, const L &l) const { @@ -285,8 +284,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::between_t; + struct ast_iterator, void> { + using node_type = between_t; template void operator()(const node_type &b, const L &l) const { @@ -297,8 +296,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::named_collate; + struct ast_iterator, void> { + using node_type = named_collate; template void operator()(const node_type &col, const L &l) const { @@ -307,8 +306,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::negated_condition_t; + struct ast_iterator, void> { + using node_type = negated_condition_t; template void operator()(const node_type &neg, const L &l) const { @@ -317,8 +316,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::is_null_t; + struct ast_iterator, void> { + using node_type = is_null_t; template void operator()(const node_type &i, const L &l) const { @@ -327,8 +326,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::is_not_null_t; + struct ast_iterator, void> { + using node_type = is_not_null_t; template void operator()(const node_type &i, const L &l) const { @@ -337,8 +336,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = core_functions::core_function_t; + struct ast_iterator, void> { + using node_type = core_function_t; template void operator()(const node_type &f, const L &l) const { @@ -347,8 +346,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::left_join_t; + struct ast_iterator, void> { + using node_type = left_join_t; template void operator()(const node_type &j, const L &l) const { @@ -357,8 +356,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::on_t; + struct ast_iterator, void> { + using node_type = on_t; template void operator()(const node_type &o, const L &l) const { @@ -367,8 +366,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::join_t; + struct ast_iterator, void> { + using node_type = join_t; template void operator()(const node_type &j, const L &l) const { @@ -377,8 +376,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::left_outer_join_t; + struct ast_iterator, void> { + using node_type = left_outer_join_t; template void operator()(const node_type &j, const L &l) const { @@ -387,8 +386,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::inner_join_t; + struct ast_iterator, void> { + using node_type = inner_join_t; template void operator()(const node_type &j, const L &l) const { @@ -402,8 +401,8 @@ namespace sqlite_orm { template void operator()(const node_type &c, const L &l) const { - c.case_expression.apply([&l](auto &c) { - iterate_ast(c, l); + c.case_expression.apply([&l](auto &c_) { + iterate_ast(c_, l); }); iterate_tuple(c.args, [&l](auto &pair) { iterate_ast(pair.first, l); @@ -426,8 +425,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::limit_t; + struct ast_iterator, void> { + using node_type = limit_t; template void operator()(const node_type &a, const L &l) const { @@ -436,8 +435,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::limit_t; + struct ast_iterator, void> { + using node_type = limit_t; template void operator()(const node_type &a, const L &l) const { @@ -449,8 +448,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::limit_t; + struct ast_iterator, void> { + using node_type = limit_t; template void operator()(const node_type &a, const L &l) const { @@ -460,5 +459,66 @@ namespace sqlite_orm { iterate_ast(a.lim, l); } }; + + template + struct ast_iterator, void> { + using node_type = distinct_t; + + template + void operator()(const node_type &a, const L &l) const { + iterate_ast(a.t, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = all_t; + + template + void operator()(const node_type &a, const L &l) const { + iterate_ast(a.t, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = bitwise_not_t; + + template + void operator()(const node_type &a, const L &l) const { + iterate_ast(a.argument, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = values_t; + + template + void operator()(const node_type &node, const L &l) const { + iterate_ast(node.tuple, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = dynamic_values_t; + + template + void operator()(const node_type &node, const L &l) const { + iterate_ast(node.vector, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = collate_t; + + template + void operator()(const node_type &node, const L &l) const { + iterate_ast(node.expr, l); + } + }; + } } diff --git a/dev/backup.h b/dev/backup.h index 51c1f3196..0459b0a4c 100644 --- a/dev/backup.h +++ b/dev/backup.h @@ -24,14 +24,14 @@ namespace sqlite_orm { const std::string &zSourceName, std::unique_ptr holder_) : handle(sqlite3_backup_init(to_.get(), zDestName.c_str(), from_.get(), zSourceName.c_str())), - holder(move(holder_)), to(to_), from(from_) { + to(to_), from(from_), holder(move(holder_)) { if(!this->handle) { throw std::system_error(std::make_error_code(orm_error_code::failed_to_init_a_backup)); } } backup_t(backup_t &&other) : - handle(other.handle), holder(move(other.holder)), to(other.to), from(other.from) { + handle(other.handle), to(other.to), from(other.from), holder(move(other.holder)) { other.handle = nullptr; } @@ -65,9 +65,9 @@ namespace sqlite_orm { protected: sqlite3_backup *handle = nullptr; - std::unique_ptr holder; connection_ref to; connection_ref from; + std::unique_ptr holder; }; } } diff --git a/dev/column.h b/dev/column.h index 3b02d9440..2293ede03 100644 --- a/dev/column.h +++ b/dev/column.h @@ -9,6 +9,7 @@ #include "tuple_helper.h" #include "default_value_extractor.h" #include "constraints.h" +#include "getter_traits.h" namespace sqlite_orm { @@ -58,14 +59,13 @@ namespace sqlite_orm { */ constraints_type constraints; - column_t(std::string name, + column_t(std::string name_, member_pointer_t member_pointer_, getter_type getter_, setter_type setter_, constraints_type constraints_) : - column_base{std::move(name)}, - member_pointer(member_pointer_), getter(getter_), setter(setter_), - constraints(std::move(constraints_)) {} + column_base{std::move(name_)}, + member_pointer(member_pointer_), getter(getter_), setter(setter_), constraints(move(constraints_)) {} /** * Simplified interface for `NOT NULL` constraint @@ -121,152 +121,26 @@ namespace sqlite_orm { template struct is_column> : public std::true_type {}; - template - struct is_field_member_pointer : std::false_type {}; - template - struct is_field_member_pointer::value && - !std::is_member_function_pointer::value>::type> - : std::true_type {}; - - /** - * Getters aliases - */ - template - using getter_by_value_const = T (O::*)() const; - - template - using getter_by_value = T (O::*)(); - - template - using getter_by_ref_const = T &(O::*)() const; - - template - using getter_by_ref = T &(O::*)(); - - template - using getter_by_const_ref_const = const T &(O::*)() const; - - template - using getter_by_const_ref = const T &(O::*)(); - - /** - * Setters aliases - */ - template - using setter_by_value = void (O::*)(T); - - template - using setter_by_ref = void (O::*)(T &); - - template - using setter_by_const_ref = void (O::*)(const T &); - - template - struct is_getter : std::false_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_getter> : std::true_type {}; - - template - struct is_setter : std::false_type {}; - - template - struct is_setter> : std::true_type {}; - - template - struct is_setter> : std::true_type {}; - - template - struct is_setter> : std::true_type {}; - - template - struct getter_traits; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = false; + struct column_field_type { + using type = void; }; - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = false; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; - }; - - template - struct getter_traits> { - using object_type = O; - using field_type = T; - - static constexpr const bool returns_lvalue = true; + template + struct column_field_type> { + using type = typename column_t::field_type; }; template - struct setter_traits; - - template - struct setter_traits> { - using object_type = O; - using field_type = T; + struct column_constraints_type { + using type = std::tuple<>; }; - template - struct setter_traits> { - using object_type = O; - using field_type = T; + template + struct column_constraints_type> { + using type = typename column_t::constraints_type; }; - template - struct setter_traits> { - using object_type = O; - using field_type = T; - }; } /** @@ -278,8 +152,10 @@ namespace sqlite_orm { class... Op> internal::column_t make_column(const std::string &name, T O::*m, Op... constraints) { - static_assert(constraints::constraints_size::value == std::tuple_size>::value, + static_assert(constraints::template constraints_size::value == std::tuple_size>::value, "Incorrect constraints pack"); + static_assert(internal::is_field_member_pointer::value, + "second argument expected as a member field pointer, not member function pointer"); return {name, m, nullptr, nullptr, std::make_tuple(constraints...)}; } @@ -300,7 +176,7 @@ namespace sqlite_orm { static_assert(std::is_same::field_type, typename internal::getter_traits::field_type>::value, "Getter and setter must get and set same data type"); - static_assert(constraints::constraints_size::value == std::tuple_size>::value, + static_assert(constraints::template constraints_size::value == std::tuple_size>::value, "Incorrect constraints pack"); return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; } @@ -323,7 +199,7 @@ namespace sqlite_orm { static_assert(std::is_same::field_type, typename internal::getter_traits::field_type>::value, "Getter and setter must get and set same data type"); - static_assert(constraints::constraints_size::value == std::tuple_size>::value, + static_assert(constraints::template constraints_size::value == std::tuple_size>::value, "Incorrect constraints pack"); return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; } diff --git a/dev/column_names_getter.h b/dev/column_names_getter.h new file mode 100644 index 000000000..26e43481c --- /dev/null +++ b/dev/column_names_getter.h @@ -0,0 +1,97 @@ +#pragma once + +#include // std::string +#include // std::vector +#include // std::reference_wrapper + +#include "error_code.h" +#include "select_constraints.h" + +namespace sqlite_orm { + + namespace internal { + + template + std::string serialize(const T &t, const C &context); + + template + struct column_names_getter { + using expression_type = T; + + template + std::vector operator()(const expression_type &t, const C &context) { + auto newContext = context; + newContext.skip_table_name = false; + auto columnName = serialize(t, newContext); + if(columnName.length()) { + return {move(columnName)}; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } + } + }; + + template + std::vector get_column_names(const T &t, const C &context) { + column_names_getter serializator; + return serializator(t, context); + } + + template + struct column_names_getter, void> { + using expression_type = std::reference_wrapper; + + template + std::vector operator()(const expression_type &expression, const C &context) { + return get_column_names(expression.get(), context); + } + }; + + template + struct column_names_getter, void> { + using expression_type = asterisk_t; + + template + std::vector operator()(const expression_type &, const C &) { + std::vector res; + res.push_back("*"); + return res; + } + }; + + template + struct column_names_getter, void> { + using expression_type = object_t; + + template + std::vector operator()(const expression_type &, const C &) { + std::vector res; + res.push_back("*"); + return res; + } + }; + + template + struct column_names_getter, void> { + using expression_type = columns_t; + + template + std::vector operator()(const expression_type &cols, const C &context) { + std::vector columnNames; + columnNames.reserve(static_cast(cols.count)); + auto newContext = context; + newContext.skip_table_name = false; + iterate_tuple(cols.columns, [&columnNames, &newContext](auto &m) { + auto columnName = serialize(m, newContext); + if(columnName.length()) { + columnNames.push_back(columnName); + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } + }); + return columnNames; + } + }; + + } +} diff --git a/dev/column_result.h b/dev/column_result.h index e0c868448..a32771be2 100644 --- a/dev/column_result.h +++ b/dev/column_result.h @@ -1,11 +1,10 @@ #pragma once -#include // std::enable_if, std::is_same, std::decay +#include // std::enable_if, std::is_same, std::decay, std::is_arithmetic #include // std::tuple #include // std::reference_wrapper #include "core_functions.h" -#include "aggregate_functions.h" #include "select_constraints.h" #include "operators.h" #include "rowid.h" @@ -15,12 +14,15 @@ namespace sqlite_orm { + using int64 = sqlite_int64; + using uint64 = sqlite_uint64; + namespace internal { /** * 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 core_functions::length_t has `type` int cause + * 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 * T - C++ type @@ -53,61 +55,23 @@ namespace sqlite_orm { using type = typename setter_traits::field_type; }; - template - struct column_result_t< - St, - T, - typename std::enable_if::value>::type> { - using type = typename T::return_type; - }; - - template - struct column_result_t, void> { - using type = double; + template + struct column_result_t, void> { + using type = R; }; - template - struct column_result_t, void> { - using type = int; + template + struct column_result_t, S, X>, void> { + using type = std::unique_ptr::type>; }; template - struct column_result_t, void> { + struct column_result_t, void> { using type = int; }; - template - struct column_result_t, void> { - using type = std::unique_ptr; - }; - - template - struct column_result_t, void> { - using type = double; - }; - - template - struct column_result_t, void> { - using type = std::string; - }; - - template - struct column_result_t, void> { - using type = std::string; - }; - - template - struct column_result_t, void> { - using type = std::unique_ptr::type>; - }; - - template - struct column_result_t, void> { - using type = std::unique_ptr::type>; - }; - template - struct column_result_t { + struct column_result_t { using type = int; }; @@ -151,6 +115,31 @@ namespace sqlite_orm { 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; @@ -237,8 +226,13 @@ namespace sqlite_orm { using type = typename storage_traits::storage_mapped_columns::type; }; + template + struct column_result_t, void> { + using type = T; + }; + template - struct column_result_t, void> { + struct column_result_t, void> { using type = T; }; @@ -248,17 +242,17 @@ namespace sqlite_orm { }; template - struct column_result_t, void> { + struct column_result_t, void> { using type = bool; }; template - struct column_result_t, void> { + struct column_result_t, void> { using type = bool; }; template - struct column_result_t, void> { + struct column_result_t, void> { using type = bool; }; diff --git a/dev/conditions.h b/dev/conditions.h index bf468caa4..cf09c3e08 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -8,6 +8,7 @@ #include "collate_argument.h" #include "constraints.h" #include "optional_container.h" +#include "negatable.h" namespace sqlite_orm { @@ -15,7 +16,7 @@ namespace sqlite_orm { struct arithmetic_t; } - namespace conditions { + namespace internal { struct limit_string { operator std::string() const { @@ -71,7 +72,7 @@ namespace sqlite_orm { T expr; internal::collate_argument argument; - collate_t(T expr_, internal::collate_argument argument_) : expr(expr_), argument(argument_) {} + collate_t(T expr_, internal::collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} operator std::string() const { return constraints::collate_t{this->argument}; @@ -170,15 +171,11 @@ namespace sqlite_orm { * = and == operators object */ template - struct is_equal_t : binary_condition, is_equal_string { + struct is_equal_t : binary_condition, is_equal_string, internal::negatable_t { using self = is_equal_t; using binary_condition::binary_condition; - negated_condition_t operator!() const { - return {*this}; - } - collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } @@ -206,15 +203,11 @@ namespace sqlite_orm { * != operator object */ template - struct is_not_equal_t : binary_condition, is_not_equal_string { + struct is_not_equal_t : binary_condition, is_not_equal_string, internal::negatable_t { using self = is_not_equal_t; using binary_condition::binary_condition; - negated_condition_t operator!() const { - return {*this}; - } - collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } @@ -238,15 +231,11 @@ namespace sqlite_orm { * > operator object. */ template - struct greater_than_t : binary_condition, greater_than_string { + struct greater_than_t : binary_condition, greater_than_string, internal::negatable_t { using self = greater_than_t; using binary_condition::binary_condition; - negated_condition_t operator!() const { - return {*this}; - } - collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } @@ -270,15 +259,11 @@ namespace sqlite_orm { * >= operator object. */ template - struct greater_or_equal_t : binary_condition, greater_or_equal_string { + struct greater_or_equal_t : binary_condition, greater_or_equal_string, internal::negatable_t { using self = greater_or_equal_t; using binary_condition::binary_condition; - negated_condition_t operator!() const { - return {*this}; - } - collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } @@ -302,15 +287,11 @@ namespace sqlite_orm { * < operator object. */ template - struct lesser_than_t : binary_condition, lesser_than_string { + struct lesser_than_t : binary_condition, lesser_than_string, internal::negatable_t { using self = lesser_than_t; using binary_condition::binary_condition; - negated_condition_t operator!() const { - return {*this}; - } - collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } @@ -334,15 +315,11 @@ namespace sqlite_orm { * <= operator object. */ template - struct lesser_or_equal_t : binary_condition, lesser_or_equal_string { + struct lesser_or_equal_t : binary_condition, lesser_or_equal_string, internal::negatable_t { using self = lesser_or_equal_t; using binary_condition::binary_condition; - negated_condition_t> operator!() const { - return {*this}; - } - collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } @@ -372,17 +349,13 @@ namespace sqlite_orm { * IN operator object. */ template - struct in_t : condition_t, in_base { + struct in_t : condition_t, in_base, internal::negatable_t { using self = in_t; L l; // left expression A arg; // in arg - in_t(L l_, A arg_, bool negative) : in_base{negative}, l(l_), arg(std::move(arg_)) {} - - negated_condition_t operator!() const { - return {*this}; - } + in_t(L l_, A arg_, bool negative_) : in_base{negative_}, l(l_), arg(std::move(arg_)) {} }; struct is_null_string { @@ -395,16 +368,12 @@ namespace sqlite_orm { * IS NULL operator object. */ template - struct is_null_t : is_null_string { + struct is_null_t : is_null_string, internal::negatable_t { using self = is_null_t; T t; is_null_t(T t_) : t(std::move(t_)) {} - - negated_condition_t operator!() const { - return {*this}; - } }; struct is_not_null_string { @@ -417,16 +386,12 @@ namespace sqlite_orm { * IS NOT NULL operator object. */ template - struct is_not_null_t : is_not_null_string { + struct is_not_null_t : is_not_null_string, internal::negatable_t { using self = is_not_null_t; T t; is_not_null_t(T t_) : t(std::move(t_)) {} - - negated_condition_t operator!() const { - return {*this}; - } }; struct where_string { @@ -526,27 +491,29 @@ namespace sqlite_orm { 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_, move(collate_argument_)}, name(move(name_)) {} + }; + /** - * S - storage class + * C - serializator context class */ - template + template struct dynamic_order_by_t : order_by_string { - using storage_type = S; - - struct entry_t : order_by_base { - std::string name; - - entry_t(decltype(name) name_, int asc_desc, std::string collate_argument) : - order_by_base{asc_desc, move(collate_argument)}, name(move(name_)) {} - }; - + 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 storage_type &storage_) : storage(storage_) {} + dynamic_order_by_t(const context_t &context_) : context(context_) {} template void push_back(order_by_t order_by) { - auto columnName = this->storage.string_from_expression(order_by.o, true); + auto newContext = this->context; + newContext.skip_table_name = true; + auto columnName = serialize(order_by.o, newContext); entries.emplace_back(move(columnName), order_by.asc_desc, move(order_by._collate_argument)); } @@ -564,7 +531,7 @@ namespace sqlite_orm { protected: std::vector entries; - const storage_type &storage; + context_t context; }; template @@ -576,8 +543,8 @@ namespace sqlite_orm { template struct is_order_by> : std::true_type {}; - template - struct is_order_by> : std::true_type {}; + template + struct is_order_by> : std::true_type {}; struct group_by_string { operator std::string() const { @@ -635,7 +602,7 @@ namespace sqlite_orm { * LIKE operator object. */ template - struct like_t : condition_t, like_string { + struct like_t : condition_t, like_string, internal::negatable_t { using self = like_t; using arg_t = A; using pattern_t = T; @@ -646,17 +613,13 @@ namespace sqlite_orm { sqlite_orm::internal::optional_container arg3; // not escape cause escape exists as a function here - like_t(arg_t arg_, pattern_t pattern_, sqlite_orm::internal::optional_container escape) : - arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape)) {} + like_t(arg_t arg_, pattern_t pattern_, sqlite_orm::internal::optional_container escape_) : + arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {} template like_t escape(C c) const { - sqlite_orm::internal::optional_container arg3{std::move(c)}; - return {std::move(this->arg), std::move(this->pattern), std::move(arg3)}; - } - - negated_condition_t operator!() const { - return {*this}; + sqlite_orm::internal::optional_container newArg3{std::move(c)}; + return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; } }; @@ -667,7 +630,7 @@ namespace sqlite_orm { }; template - struct glob_t : condition_t, glob_string { + struct glob_t : condition_t, glob_string, internal::negatable_t { using self = glob_t; using arg_t = A; using pattern_t = T; @@ -676,10 +639,6 @@ namespace sqlite_orm { pattern_t pattern; glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {} - - negated_condition_t operator!() const { - return {*this}; - } }; struct cross_join_string { @@ -834,17 +793,13 @@ namespace sqlite_orm { }; template - struct exists_t : condition_t, exists_string { + struct exists_t : condition_t, exists_string, internal::negatable_t { using type = T; using self = exists_t; type t; exists_t(T t_) : t(std::move(t_)) {} - - negated_condition_t operator!() const { - return {*this}; - } }; struct having_string { @@ -896,66 +851,71 @@ namespace sqlite_orm { } + template::value>::type> + internal::negated_condition_t operator!(T arg) { + return {std::move(arg)}; + } + /** * Cute operators for columns */ template - conditions::lesser_than_t operator<(internal::expression_t expr, R r) { + internal::lesser_than_t operator<(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template - conditions::lesser_than_t operator<(L l, internal::expression_t expr) { + internal::lesser_than_t operator<(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template - conditions::lesser_or_equal_t operator<=(internal::expression_t expr, R r) { + internal::lesser_or_equal_t operator<=(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template - conditions::lesser_or_equal_t operator<=(L l, internal::expression_t expr) { + internal::lesser_or_equal_t operator<=(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template - conditions::greater_than_t operator>(internal::expression_t expr, R r) { + internal::greater_than_t operator>(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template - conditions::greater_than_t operator>(L l, internal::expression_t expr) { + internal::greater_than_t operator>(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template - conditions::greater_or_equal_t operator>=(internal::expression_t expr, R r) { + internal::greater_or_equal_t operator>=(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template - conditions::greater_or_equal_t operator>=(L l, internal::expression_t expr) { + internal::greater_or_equal_t operator>=(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template - conditions::is_equal_t operator==(internal::expression_t expr, R r) { + internal::is_equal_t operator==(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template - conditions::is_equal_t operator==(L l, internal::expression_t expr) { + internal::is_equal_t operator==(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template - conditions::is_not_equal_t operator!=(internal::expression_t expr, R r) { + internal::is_not_equal_t operator!=(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template - conditions::is_not_equal_t operator!=(L l, internal::expression_t expr) { + internal::is_not_equal_t operator!=(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } @@ -1050,184 +1010,184 @@ namespace sqlite_orm { } template - conditions::using_t using_(F O::*p) { + internal::using_t using_(F O::*p) { return {std::move(p)}; } template - conditions::on_t on(T t) { + internal::on_t on(T t) { return {std::move(t)}; } template - conditions::cross_join_t cross_join() { + internal::cross_join_t cross_join() { return {}; } template - conditions::natural_join_t natural_join() { + internal::natural_join_t natural_join() { return {}; } template - conditions::left_join_t left_join(O o) { + internal::left_join_t left_join(O o) { return {std::move(o)}; } template - conditions::join_t join(O o) { + internal::join_t join(O o) { return {std::move(o)}; } template - conditions::left_outer_join_t left_outer_join(O o) { + internal::left_outer_join_t left_outer_join(O o) { return {std::move(o)}; } template - conditions::inner_join_t inner_join(O o) { + internal::inner_join_t inner_join(O o) { return {std::move(o)}; } template - conditions::offset_t offset(T off) { + internal::offset_t offset(T off) { return {std::move(off)}; } template - conditions::limit_t limit(T lim) { + internal::limit_t limit(T lim) { return {std::move(lim)}; } template - typename std::enable_if::value, conditions::limit_t>::type - limit(O off, T lim) { + typename std::enable_if::value, internal::limit_t>::type limit(O off, + T lim) { return {std::move(lim), {std::move(off)}}; } template - conditions::limit_t limit(T lim, conditions::offset_t offt) { + internal::limit_t limit(T lim, internal::offset_t offt) { return {std::move(lim), {std::move(offt.off)}}; } template::value || - std::is_base_of::value>::type> - conditions::and_condition_t operator&&(L l, R r) { + typename = typename std::enable_if::value || + std::is_base_of::value>::type> + internal::and_condition_t operator&&(L l, R r) { return {std::move(l), std::move(r)}; } template::value || - std::is_base_of::value>::type> - conditions::or_condition_t operator||(L l, R r) { + typename = typename std::enable_if::value || + std::is_base_of::value>::type> + internal::or_condition_t operator||(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::is_not_null_t is_not_null(T t) { + internal::is_not_null_t is_not_null(T t) { return {std::move(t)}; } template - conditions::is_null_t is_null(T t) { + internal::is_null_t is_null(T t) { return {std::move(t)}; } template - conditions::in_t> in(L l, std::vector values) { + internal::in_t> in(L l, std::vector values) { return {std::move(l), std::move(values), false}; } template - conditions::in_t> in(L l, std::initializer_list values) { + internal::in_t> in(L l, std::initializer_list values) { return {std::move(l), std::move(values), false}; } template - conditions::in_t in(L l, A arg) { + internal::in_t in(L l, A arg) { return {std::move(l), std::move(arg), false}; } template - conditions::in_t> not_in(L l, std::vector values) { + internal::in_t> not_in(L l, std::vector values) { return {std::move(l), std::move(values), true}; } template - conditions::in_t> not_in(L l, std::initializer_list values) { + internal::in_t> not_in(L l, std::initializer_list values) { return {std::move(l), std::move(values), true}; } template - conditions::in_t not_in(L l, A arg) { + internal::in_t not_in(L l, A arg) { return {std::move(l), std::move(arg), true}; } template - conditions::is_equal_t is_equal(L l, R r) { + internal::is_equal_t is_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::is_equal_t eq(L l, R r) { + internal::is_equal_t eq(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::is_not_equal_t is_not_equal(L l, R r) { + internal::is_not_equal_t is_not_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::is_not_equal_t ne(L l, R r) { + internal::is_not_equal_t ne(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::greater_than_t greater_than(L l, R r) { + internal::greater_than_t greater_than(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::greater_than_t gt(L l, R r) { + internal::greater_than_t gt(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::greater_or_equal_t greater_or_equal(L l, R r) { + internal::greater_or_equal_t greater_or_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::greater_or_equal_t ge(L l, R r) { + internal::greater_or_equal_t ge(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::lesser_than_t lesser_than(L l, R r) { + internal::lesser_than_t lesser_than(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::lesser_than_t lt(L l, R r) { + internal::lesser_than_t lt(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::lesser_or_equal_t lesser_or_equal(L l, R r) { + internal::lesser_or_equal_t lesser_or_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::lesser_or_equal_t le(L l, R r) { + internal::lesser_or_equal_t le(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::where_t where(C c) { + internal::where_t where(C c) { return {std::move(c)}; } @@ -1236,7 +1196,7 @@ namespace sqlite_orm { * Example: storage.select(&User::name, order_by(&User::id)) */ template - conditions::order_by_t order_by(O o) { + internal::order_by_t order_by(O o) { return {std::move(o)}; } @@ -1245,7 +1205,7 @@ namespace sqlite_orm { * Example: storage.get_all(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) */ template - conditions::multi_order_by_t multi_order_by(Args &&... args) { + internal::multi_order_by_t multi_order_by(Args &&... args) { return {std::make_tuple(std::forward(args)...)}; } @@ -1262,8 +1222,10 @@ namespace sqlite_orm { * } */ template - conditions::dynamic_order_by_t dynamic_order_by(const S &storage) { - return {storage}; + internal::dynamic_order_by_t> + dynamic_order_by(const S &storage) { + internal::serializator_context_builder builder(storage); + return builder(); } /** @@ -1271,7 +1233,7 @@ namespace sqlite_orm { * Example: storage.get_all(group_by(&Employee::name)) */ template - conditions::group_by_t group_by(Args &&... args) { + internal::group_by_t group_by(Args &&... args) { return {std::make_tuple(std::forward(args)...)}; } @@ -1280,7 +1242,7 @@ namespace sqlite_orm { * Example: storage.select(between(&User::id, 10, 20)) */ template - conditions::between_t between(A expr, T b1, T b2) { + internal::between_t between(A expr, T b1, T b2) { return {std::move(expr), std::move(b1), std::move(b2)}; } @@ -1289,7 +1251,7 @@ namespace sqlite_orm { * Example: storage.select(like(&User::name, "T%")) */ template - conditions::like_t like(A a, T t) { + internal::like_t like(A a, T t) { return {std::move(a), std::move(t), {}}; } @@ -1298,7 +1260,7 @@ namespace sqlite_orm { * Example: storage.select(glob(&User::name, "*S")) */ template - conditions::glob_t glob(A a, T t) { + internal::glob_t glob(A a, T t) { return {std::move(a), std::move(t)}; } @@ -1307,7 +1269,7 @@ namespace sqlite_orm { * Example: storage.select(like(&User::name, "T%", "%")) */ template - conditions::like_t like(A a, T t, E e) { + internal::like_t like(A a, T t, E e) { return {std::move(a), std::move(t), {std::move(e)}}; } @@ -1320,7 +1282,7 @@ namespace sqlite_orm { order_by(&Agent::comission)); */ template - conditions::exists_t exists(T t) { + internal::exists_t exists(T t) { return {std::move(t)}; } @@ -1329,7 +1291,7 @@ namespace sqlite_orm { * Example: storage.get_all(group_by(&Employee::name), having(greater_than(count(&Employee::name), 2))); */ template - conditions::having_t having(T t) { + internal::having_t having(T t) { return {std::move(t)}; } @@ -1338,7 +1300,7 @@ namespace sqlite_orm { * Example: cast(&User::id) */ template - conditions::cast_t cast(E e) { + internal::cast_t cast(E e) { return {std::move(e)}; } } diff --git a/dev/constraints.h b/dev/constraints.h index 6ba75e7b6..1b21964a1 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -53,13 +53,11 @@ namespace sqlite_orm { template struct primary_key_t : primary_key_base { using order_by = primary_key_base::order_by; + using columns_tuple = std::tuple; - std::tuple columns; + columns_tuple columns; - primary_key_t(decltype(columns) c) : columns(std::move(c)) {} - - using field_type = void; // for column iteration. Better be deleted - using constraints_type = std::tuple<>; + primary_key_t(decltype(columns) c) : columns(move(c)) {} primary_key_t asc() const { auto res = *this; @@ -74,14 +72,22 @@ namespace sqlite_orm { } }; + struct unique_base { + operator std::string() const { + return "UNIQUE"; + } + }; + /** * UNIQUE constraint class. */ - struct unique_t { + template + struct unique_t : unique_base { + using columns_tuple = std::tuple; - operator std::string() const { - return "UNIQUE"; - } + columns_tuple columns; + + unique_t(columns_tuple columns_) : columns(move(columns_)) {} }; /** @@ -164,8 +170,8 @@ namespace sqlite_orm { 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_) {} + 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; @@ -255,9 +261,6 @@ namespace sqlite_orm { return *this; } - using field_type = void; // for column iteration. Better be deleted - using constraints_type = std::tuple<>; - template void for_each_column(const L &) {} @@ -281,8 +284,8 @@ namespace sqlite_orm { foreign_key_intermediate_t(tuple_type columns_) : columns(std::move(columns_)) {} template - foreign_key_t, std::tuple> references(Rs... references) { - return {std::move(this->columns), std::make_tuple(std::forward(references)...)}; + foreign_key_t, std::tuple> references(Rs... refs) { + return {std::move(this->columns), std::make_tuple(std::forward(refs)...)}; } }; #endif @@ -310,6 +313,21 @@ namespace sqlite_orm { } }; + struct check_string { + operator std::string() const { + return "CHECK"; + } + }; + + template + struct check_t : check_string { + using expression_type = T; + + expression_type expression; + + check_t(expression_type expression_) : expression(std::move(expression_)) {} + }; + template struct is_constraint : std::false_type {}; @@ -319,8 +337,8 @@ namespace sqlite_orm { template struct is_constraint> : std::true_type {}; - template<> - struct is_constraint : std::true_type {}; + template + struct is_constraint> : std::true_type {}; template struct is_constraint> : std::true_type {}; @@ -331,6 +349,9 @@ namespace sqlite_orm { template<> struct is_constraint : std::true_type {}; + template + struct is_constraint> : std::true_type {}; + template struct constraints_size; @@ -360,8 +381,13 @@ namespace sqlite_orm { /** * UNIQUE constraint builder function. */ - inline constraints::unique_t unique() { - return {}; + template + constraints::unique_t unique(Args... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + inline constraints::unique_t<> unique() { + return {{}}; } inline constraints::autoincrement_t autoincrement() { @@ -369,9 +395,12 @@ namespace sqlite_orm { } template - inline constraints::primary_key_t primary_key(Cs... cs) { - using ret_type = constraints::primary_key_t; - return ret_type(std::make_tuple(cs...)); + constraints::primary_key_t primary_key(Cs... cs) { + return {std::make_tuple(std::forward(cs)...)}; + } + + inline constraints::primary_key_t<> primary_key() { + return {{}}; } template @@ -391,6 +420,11 @@ namespace sqlite_orm { return {internal::collate_argument::rtrim}; } + template + constraints::check_t check(T t) { + return {std::move(t)}; + } + namespace internal { /** diff --git a/dev/core_functions.h b/dev/core_functions.h index e3091c9c1..27454be20 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -12,7 +12,10 @@ namespace sqlite_orm { - namespace core_functions { + namespace internal { + + template + struct unique_ptr_result_of {}; /** * Base class for operator overloading @@ -81,6 +84,42 @@ namespace sqlite_orm { } }; + struct hex_string { + operator std::string() const { + return "HEX"; + } + }; + + struct quote_string { + operator std::string() const { + return "QUOTE"; + } + }; + + struct randomblob_string { + operator std::string() const { + return "RANDOMBLOB"; + } + }; + + struct instr_string { + operator std::string() const { + return "INSTR"; + } + }; + + struct replace_string { + operator std::string() const { + return "REPLACE"; + } + }; + + struct round_string { + operator std::string() const { + return "ROUND"; + } + }; + #if SQLITE_VERSION_NUMBER >= 3007016 struct char_string { @@ -109,6 +148,12 @@ namespace sqlite_orm { } }; + struct time_string { + operator std::string() const { + return "TIME"; + } + }; + struct datetime_string { operator std::string() const { return "DATETIME"; @@ -121,6 +166,12 @@ namespace sqlite_orm { } }; + struct strftime_string { + operator std::string() const { + return "STRFTIME"; + } + }; + struct zeroblob_string { operator std::string() const { return "ZEROBLOB"; @@ -132,57 +183,138 @@ namespace sqlite_orm { return "SUBSTR"; } }; +#ifdef SQLITE_SOUNDEX + struct soundex_string { + operator std::string() const { + return "SOUNDEX"; + } + }; +#endif + struct total_string { + operator std::string() const { + return "TOTAL"; + } + }; + + struct sum_string { + operator std::string() const { + return "SUM"; + } + }; + + struct count_string { + operator std::string() const { + return "COUNT"; + } + }; + + /** + * T is use to specify type explicitly for queries like + * SELECT COUNT(*) FROM table_name; + * T can be omitted with void. + */ + template + struct count_asterisk_t : count_string { + using type = T; + }; + + /** + * The same thing as count() but without T arg. + * Is used in cases like this: + * SELECT cust_code, cust_name, cust_city, grade + * FROM customer + * WHERE grade=2 AND EXISTS + * (SELECT COUNT(*) + * FROM customer + * WHERE grade=2 + * GROUP BY grade + * HAVING COUNT(*)>2); + * `c++` + * auto rows = + * storage.select(columns(&Customer::code, &Customer::name, &Customer::city, &Customer::grade), + * where(is_equal(&Customer::grade, 2) + * and exists(select(count(), + * where(is_equal(&Customer::grade, 2)), + * group_by(&Customer::grade), + * having(greater_than(count(), 2)))))); + */ + struct count_asterisk_without_type : count_string {}; + + struct avg_string { + operator std::string() const { + return "AVG"; + } + }; + + struct max_string { + operator std::string() const { + return "MAX"; + } + }; + + struct min_string { + operator std::string() const { + return "MIN"; + } + }; + + struct group_concat_string { + operator std::string() const { + return "GROUP_CONCAT"; + } + }; + } /** * Cute operators for core functions */ - template::value>::type> - conditions::lesser_than_t operator<(F f, R r) { + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::lesser_than_t operator<(F f, R r) { return {std::move(f), std::move(r)}; } - template::value>::type> - conditions::lesser_or_equal_t operator<=(F f, R r) { + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::lesser_or_equal_t operator<=(F f, R r) { return {std::move(f), std::move(r)}; } - template::value>::type> - conditions::greater_than_t operator>(F f, R r) { + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::greater_than_t operator>(F f, R r) { return {std::move(f), std::move(r)}; } - template::value>::type> - conditions::greater_or_equal_t operator>=(F f, R r) { + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::greater_or_equal_t operator>=(F f, R r) { return {std::move(f), std::move(r)}; } - template::value>::type> - conditions::is_equal_t operator==(F f, R r) { + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::is_equal_t operator==(F f, R r) { return {std::move(f), std::move(r)}; } - template::value>::type> - conditions::is_not_equal_t operator!=(F f, R r) { + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::is_not_equal_t operator!=(F f, R r) { return {std::move(f), std::move(r)}; } @@ -190,42 +322,42 @@ namespace sqlite_orm { * LENGTH(x) function https://sqlite.org/lang_corefunc.html#length */ template - core_functions::core_function_t length(T t) { + internal::core_function_t length(T t) { std::tuple args{std::forward(t)}; - return {std::move(args)}; + return {move(args)}; } /** * ABS(x) function https://sqlite.org/lang_corefunc.html#abs */ template - core_functions::core_function_t, core_functions::abs_string, T> abs(T t) { + internal::core_function_t, internal::abs_string, T> abs(T t) { std::tuple args{std::forward(t)}; - return {std::move(args)}; + return {move(args)}; } /** * LOWER(x) function https://sqlite.org/lang_corefunc.html#lower */ template - core_functions::core_function_t lower(T t) { + internal::core_function_t lower(T t) { std::tuple args{std::forward(t)}; - return {std::move(args)}; + return {move(args)}; } /** * UPPER(x) function https://sqlite.org/lang_corefunc.html#upper */ template - core_functions::core_function_t upper(T t) { + internal::core_function_t upper(T t) { std::tuple args{std::forward(t)}; - return {std::move(args)}; + return {move(args)}; } /** * CHANGES() function https://sqlite.org/lang_corefunc.html#changes */ - inline core_functions::core_function_t changes() { + inline internal::core_function_t changes() { return {{}}; } @@ -233,54 +365,117 @@ namespace sqlite_orm { * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim */ template - core_functions::core_function_t trim(T t) { + internal::core_function_t trim(T t) { std::tuple args{std::forward(t)}; - return {std::move(args)}; + return {move(args)}; } /** * TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim */ template - core_functions::core_function_t trim(X x, Y y) { + internal::core_function_t trim(X x, Y y) { std::tuple args{std::forward(x), std::forward(y)}; - return {std::move(args)}; + return {move(args)}; } /** * LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim */ template - core_functions::core_function_t ltrim(X x) { + internal::core_function_t ltrim(X x) { std::tuple args{std::forward(x)}; - return {std::move(args)}; + return {move(args)}; } /** * LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim */ template - core_functions::core_function_t ltrim(X x, Y y) { + internal::core_function_t ltrim(X x, Y y) { std::tuple args{std::forward(x), std::forward(y)}; - return {std::move(args)}; + return {move(args)}; } /** * RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim */ template - core_functions::core_function_t rtrim(X x) { + internal::core_function_t rtrim(X x) { std::tuple args{std::forward(x)}; - return {std::move(args)}; + return {move(args)}; } /** * RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim */ template - core_functions::core_function_t rtrim(X x, Y y) { + internal::core_function_t rtrim(X x, Y y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {move(args)}; + } + + /** + * HEX(X) function https://sqlite.org/lang_corefunc.html#hex + */ + template + internal::core_function_t hex(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * QUOTE(X) function https://sqlite.org/lang_corefunc.html#quote + */ + template + internal::core_function_t quote(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * RANDOMBLOB(X) function https://sqlite.org/lang_corefunc.html#randomblob + */ + template + internal::core_function_t, internal::randomblob_string, X> randomblob(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * INSTR(X) function https://sqlite.org/lang_corefunc.html#instr + */ + template + internal::core_function_t instr(X x, Y y) { std::tuple args{std::forward(x), std::forward(y)}; - return {std::move(args)}; + return {move(args)}; + } + + /** + * REPLACE(X) function https://sqlite.org/lang_corefunc.html#replace + */ + template + internal::core_function_t replace(X x, Y y, Z z) { + std::tuple args{std::forward(x), std::forward(y), std::forward(z)}; + return {move(args)}; + } + + /** + * ROUND(X) function https://sqlite.org/lang_corefunc.html#round + */ + template + internal::core_function_t round(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * ROUND(X, Y) function https://sqlite.org/lang_corefunc.html#round + */ + template + internal::core_function_t round(X x, Y y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {move(args)}; } #if SQLITE_VERSION_NUMBER >= 3007016 @@ -289,14 +484,14 @@ namespace sqlite_orm { * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char */ template - core_functions::core_function_t char_(Args... args) { + internal::core_function_t char_(Args... args) { return {std::make_tuple(std::forward(args)...)}; } /** * RANDOM() function https://www.sqlite.org/lang_corefunc.html#random */ - inline core_functions::core_function_t random() { + inline internal::core_function_t random() { return {{}}; } @@ -306,7 +501,7 @@ namespace sqlite_orm { * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce */ template - core_functions::core_function_t coalesce(Args... args) { + internal::core_function_t coalesce(Args... args) { return {std::make_tuple(std::forward(args)...)}; } @@ -314,54 +509,171 @@ namespace sqlite_orm { * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - core_functions::core_function_t date(Args... args) { + internal::core_function_t date(Args... args) { std::tuple t{std::forward(args)...}; - return {std::move(t)}; + return {move(t)}; + } + + /** + * TIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::core_function_t time(Args... args) { + std::tuple t{std::forward(args)...}; + return {move(t)}; } /** * DATETIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - core_functions::core_function_t datetime(Args... args) { + internal::core_function_t datetime(Args... args) { std::tuple t{std::forward(args)...}; - return {std::move(t)}; + return {move(t)}; } /** * JULIANDAY(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - core_functions::core_function_t julianday(Args... args) { + internal::core_function_t julianday(Args... args) { + std::tuple t{std::forward(args)...}; + return {move(t)}; + } + + /** + * STRFTIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::core_function_t strftime(Args... args) { std::tuple t{std::forward(args)...}; - return {std::move(t)}; + return {move(t)}; } /** * ZEROBLOB(N) function https://www.sqlite.org/lang_corefunc.html#zeroblob */ template - core_functions::core_function_t, core_functions::zeroblob_string, N> zeroblob(N n) { + internal::core_function_t, internal::zeroblob_string, N> zeroblob(N n) { std::tuple args{std::forward(n)}; - return {std::move(args)}; + return {move(args)}; } /** * SUBSTR(X,Y) function https://www.sqlite.org/lang_corefunc.html#substr */ template - core_functions::core_function_t substr(X x, Y y) { + internal::core_function_t substr(X x, Y y) { std::tuple args{std::forward(x), std::forward(y)}; - return {std::move(args)}; + return {move(args)}; } /** * SUBSTR(X,Y,Z) function https://www.sqlite.org/lang_corefunc.html#substr */ template - core_functions::core_function_t substr(X x, Y y, Z z) { + internal::core_function_t substr(X x, Y y, Z z) { std::tuple args{std::forward(x), std::forward(y), std::forward(z)}; - return {std::move(args)}; + return {move(args)}; + } + +#ifdef SQLITE_SOUNDEX + /** + * SOUNDEX(X) function https://www.sqlite.org/lang_corefunc.html#soundex + */ + template + internal::core_function_t soundex(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } +#endif + + /** + * TOTAL(X) aggregate function. + */ + template + internal::core_function_t total(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * SUM(X) aggregate function. + */ + template + internal::core_function_t, internal::sum_string, X> sum(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * COUNT(X) aggregate function. + */ + template + internal::core_function_t count(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * 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::core_function_t avg(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * MAX(X) aggregate function. + */ + template + internal::core_function_t, internal::max_string, X> max(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * MIN(X) aggregate function. + */ + template + internal::core_function_t, internal::min_string, X> min(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * GROUP_CONCAT(X) aggregate function. + */ + template + internal::core_function_t group_concat(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * GROUP_CONCAT(X, Y) aggregate function. + */ + template + internal::core_function_t group_concat(X x, Y y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {move(args)}; } template // std::stringstream #include "constraints.h" +#include "serializator_context.h" namespace sqlite_orm { @@ -26,7 +27,8 @@ namespace sqlite_orm { template std::unique_ptr operator()(const constraints::default_t &t) { - return std::make_unique(serialize(t.value)); + serializator_context_base context; + return std::make_unique(serialize(t.value, context)); } }; diff --git a/dev/error_code.h b/dev/error_code.h index a862ab754..706bc4d26 100644 --- a/dev/error_code.h +++ b/dev/error_code.h @@ -21,6 +21,8 @@ namespace sqlite_orm { incorrect_journal_mode_string, invalid_collate_argument_enum, failed_to_init_a_backup, + unknown_member_value, + incorrect_order, }; } @@ -57,6 +59,10 @@ namespace sqlite_orm { 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"; default: return "unknown error"; } diff --git a/dev/get_prepared_statement.h b/dev/get_prepared_statement.h index d4c9dc442..1b2ee1e08 100644 --- a/dev/get_prepared_statement.h +++ b/dev/get_prepared_statement.h @@ -134,8 +134,8 @@ namespace sqlite_orm { ++index; } if(index == N) { - internal::static_if{}>([](auto &result, auto &node) { - result = const_cast::type>(&node); + internal::static_if{}>([](auto &r, auto &n) { + r = const_cast::type>(&n); })(result, node); } }); @@ -157,8 +157,8 @@ namespace sqlite_orm { ++index; } if(index == N) { - internal::static_if{}>([](auto &result, auto &node) { - result = const_cast::type>(&node); + internal::static_if{}>([](auto &r, auto &n) { + r = const_cast::type>(&n); })(result, node); } }); diff --git a/dev/getter_traits.h b/dev/getter_traits.h new file mode 100644 index 000000000..07b1b7585 --- /dev/null +++ b/dev/getter_traits.h @@ -0,0 +1,184 @@ +#pragma once + +namespace sqlite_orm { + + namespace internal { + + template + struct is_field_member_pointer : std::false_type {}; + + template + struct is_field_member_pointer::value && + !std::is_member_function_pointer::value>::type> + : std::true_type {}; + + template + struct field_member_traits; + + template + struct field_member_traits::value>::type> { + using object_type = O; + using field_type = F; + }; + + /** + * Getters aliases + */ + template + using getter_by_value_const = T (O::*)() const; + + template + using getter_by_value = T (O::*)(); + + template + using getter_by_ref_const = T &(O::*)() const; + + template + using getter_by_ref = T &(O::*)(); + + template + using getter_by_const_ref_const = const T &(O::*)() const; + + template + using getter_by_const_ref = const T &(O::*)(); + + /** + * Setters aliases + */ + template + using setter_by_value = void (O::*)(T); + + template + using setter_by_ref = void (O::*)(T &); + + template + using setter_by_const_ref = void (O::*)(const T &); + + template + struct is_getter : std::false_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_getter> : std::true_type {}; + + template + struct is_setter : std::false_type {}; + + template + struct is_setter> : std::true_type {}; + + template + struct is_setter> : std::true_type {}; + + template + struct is_setter> : std::true_type {}; + + template + struct getter_traits; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = false; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = false; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template + struct getter_traits> { + using object_type = O; + using field_type = T; + + static constexpr const bool returns_lvalue = true; + }; + + template + struct setter_traits; + + template + struct setter_traits> { + using object_type = O; + using field_type = T; + }; + + template + struct setter_traits> { + using object_type = O; + using field_type = T; + }; + + template + struct setter_traits> { + using object_type = O; + using field_type = T; + }; + + template + struct member_traits; + + template + struct member_traits::value>::type> { + using object_type = typename field_member_traits::object_type; + using field_type = typename field_member_traits::field_type; + }; + + template + struct member_traits::value>::type> { + using object_type = typename getter_traits::object_type; + using field_type = typename getter_traits::field_type; + }; + + template + struct member_traits::value>::type> { + using object_type = typename setter_traits::object_type; + using field_type = typename setter_traits::field_type; + }; + } +} diff --git a/dev/index.h b/dev/index.h index ba6c3e0fd..b509ede47 100644 --- a/dev/index.h +++ b/dev/index.h @@ -4,31 +4,38 @@ #include // std::string #include // std::forward +#include "indexed_column.h" + namespace sqlite_orm { namespace internal { + struct index_base { + std::string name; + bool unique = false; + }; + template - struct index_t { + struct index_t : index_base { using columns_type = std::tuple; using object_type = void; - std::string name; - bool unique; - columns_type columns; + index_t(std::string name_, bool unique_, columns_type columns_) : + index_base{move(name_), unique_}, columns(move(columns_)) {} - template - void for_each_column_with_constraints(const L &) {} + columns_type columns; }; } template - internal::index_t make_index(const std::string &name, Cols... cols) { - return {name, false, std::make_tuple(std::forward(cols)...)}; + internal::index_t::type...> make_index(const std::string &name, + Cols... cols) { + return {name, false, std::make_tuple(internal::make_indexed_column(cols)...)}; } template - internal::index_t make_unique_index(const std::string &name, Cols... cols) { - return {name, true, std::make_tuple(std::forward(cols)...)}; + internal::index_t::type...> make_unique_index(const std::string &name, + Cols... cols) { + return {name, true, std::make_tuple(internal::make_indexed_column(cols)...)}; } } diff --git a/dev/indexed_column.h b/dev/indexed_column.h new file mode 100644 index 000000000..7b19e3b43 --- /dev/null +++ b/dev/indexed_column.h @@ -0,0 +1,71 @@ +#pragma once + +#include // std::string + +namespace sqlite_orm { + + namespace internal { + + template + struct indexed_column_t { + using column_type = C; + + 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 = 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 + struct indexed_column_maker { + using type = indexed_column_t; + + indexed_column_t operator()(C col) const { + return {std::move(col)}; + } + }; + + template + struct indexed_column_maker> { + using type = indexed_column_t; + + indexed_column_t operator()(indexed_column_t col) const { + return std::move(col); + } + }; + + template + auto make_indexed_column(C col) { + indexed_column_maker maker; + return maker(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/dev/iterator.h b/dev/iterator.h index 8451d1697..187d082e9 100644 --- a/dev/iterator.h +++ b/dev/iterator.h @@ -12,6 +12,7 @@ #include "row_extractor.h" #include "statement_finalizer.h" #include "error_code.h" +#include "object_from_column_builder.h" namespace sqlite_orm { @@ -42,17 +43,8 @@ namespace sqlite_orm { temp = std::make_unique(); auto &storage = this->view.storage; auto &impl = storage.template get_impl(); - auto index = 0; - impl.table.for_each_column([&index, &temp, this](auto &c) { - using field_type = typename std::decay::type::field_type; - auto value = row_extractor().extract(*this->stmt, index++); - if(c.member_pointer) { - auto member_pointer = c.member_pointer; - (*temp).*member_pointer = std::move(value); - } else { - ((*temp).*(c.setter))(std::move(value)); - } - }); + object_from_column_builder builder{*temp, *this->stmt}; + impl.table.for_each_column(builder); } public: diff --git a/dev/join_iterator.h b/dev/join_iterator.h index 1e5fd7e00..4d9777d1a 100644 --- a/dev/join_iterator.h +++ b/dev/join_iterator.h @@ -35,9 +35,9 @@ namespace sqlite_orm { }; template - struct join_iterator, Tail...> : public join_iterator { + struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; - using join_type = conditions::cross_join_t; + using join_type = cross_join_t; template void operator()(const L &l) { @@ -47,9 +47,9 @@ namespace sqlite_orm { }; template - struct join_iterator, Tail...> : public join_iterator { + struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; - using join_type = conditions::natural_join_t; + using join_type = natural_join_t; template void operator()(const L &l) { @@ -59,9 +59,9 @@ namespace sqlite_orm { }; template - struct join_iterator, Tail...> : public join_iterator { + struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; - using join_type = conditions::left_join_t; + using join_type = left_join_t; template void operator()(const L &l) { @@ -71,9 +71,9 @@ namespace sqlite_orm { }; template - struct join_iterator, Tail...> : public join_iterator { + struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; - using join_type = conditions::join_t; + using join_type = join_t; template void operator()(const L &l) { @@ -83,9 +83,9 @@ namespace sqlite_orm { }; template - struct join_iterator, Tail...> : public join_iterator { + struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; - using join_type = conditions::left_outer_join_t; + using join_type = left_outer_join_t; template void operator()(const L &l) { @@ -95,9 +95,9 @@ namespace sqlite_orm { }; template - struct join_iterator, Tail...> : public join_iterator { + struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; - using join_type = conditions::inner_join_t; + using join_type = inner_join_t; template void operator()(const L &l) { diff --git a/dev/journal_mode.h b/dev/journal_mode.h index dd6b3a032..6226a7d81 100644 --- a/dev/journal_mode.h +++ b/dev/journal_mode.h @@ -4,7 +4,7 @@ #include // std::unique_ptr #include // std::array #include // std::transform -#include // std::toupper +#include // std::toupper namespace sqlite_orm { @@ -39,15 +39,17 @@ namespace sqlite_orm { 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), ::toupper); - static std::array all = { + 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); diff --git a/dev/mapped_row_extractor.h b/dev/mapped_row_extractor.h new file mode 100644 index 000000000..5a50e7d14 --- /dev/null +++ b/dev/mapped_row_extractor.h @@ -0,0 +1,37 @@ +#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 `row_extractor_builder` class + * Type arguments: + * V is value type just like regular `row_extractor` has + * T is table info class `table_t` + */ + template + struct mapped_row_extractor { + using table_info_t = T; + + mapped_row_extractor(const table_info_t &tableInfo_) : tableInfo(tableInfo_) {} + + V extract(sqlite3_stmt *stmt, int /*columnIndex*/) { + V res; + object_from_column_builder builder{res, stmt}; + this->tableInfo.for_each_column(builder); + return res; + } + + const table_info_t &tableInfo; + }; + + } + +} diff --git a/dev/negatable.h b/dev/negatable.h new file mode 100644 index 000000000..8bdb7de6b --- /dev/null +++ b/dev/negatable.h @@ -0,0 +1,7 @@ +#pragma once + +namespace sqlite_orm { + namespace internal { + struct negatable_t {}; + } +} diff --git a/dev/node_tuple.h b/dev/node_tuple.h index 5ffd2e10f..66e34d478 100644 --- a/dev/node_tuple.h +++ b/dev/node_tuple.h @@ -31,14 +31,13 @@ namespace sqlite_orm { }; template - struct node_tuple, void> { - using node_type = conditions::where_t; + struct node_tuple, void> { + using node_type = where_t; using type = typename node_tuple::type; }; template - struct node_tuple::value>::type> { + struct node_tuple::value>::type> { using node_type = T; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; @@ -64,8 +63,8 @@ namespace sqlite_orm { }; template - struct node_tuple, void> { - using node_type = conditions::in_t; + struct node_tuple, void> { + using node_type = in_t; using left_tuple = typename node_tuple::type; using right_tuple = typename node_tuple::type; using type = typename conc_tuple::type; @@ -89,9 +88,9 @@ namespace sqlite_orm { using type = typename conc_tuple::type; }; - template - struct node_tuple, void> { - using node_type = get_all_t; + template + struct node_tuple, void> { + using node_type = get_all_t; using type = typename conc_tuple::type...>::type; }; @@ -124,20 +123,20 @@ namespace sqlite_orm { }; template - struct node_tuple, void> { - using node_type = conditions::having_t; + struct node_tuple, void> { + using node_type = having_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::cast_t; + struct node_tuple, void> { + using node_type = cast_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::exists_t; + struct node_tuple, void> { + using node_type = exists_t; using type = typename node_tuple::type; }; @@ -154,8 +153,8 @@ namespace sqlite_orm { }; template - struct node_tuple, void> { - using node_type = conditions::like_t; + struct node_tuple, void> { + using node_type = like_t; using arg_tuple = typename node_tuple::type; using pattern_tuple = typename node_tuple::type; using escape_tuple = typename node_tuple::type; @@ -163,16 +162,16 @@ namespace sqlite_orm { }; template - struct node_tuple, void> { - using node_type = conditions::glob_t; + struct node_tuple, void> { + using node_type = glob_t; using arg_tuple = typename node_tuple::type; using pattern_tuple = typename node_tuple::type; using type = typename conc_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::between_t; + struct node_tuple, void> { + using node_type = between_t; using expression_tuple = typename node_tuple::type; using lower_tuple = typename node_tuple::type; using upper_tuple = typename node_tuple::type; @@ -180,62 +179,62 @@ namespace sqlite_orm { }; template - struct node_tuple, void> { - using node_type = conditions::named_collate; + struct node_tuple, void> { + using node_type = named_collate; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::is_null_t; + struct node_tuple, void> { + using node_type = is_null_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::is_not_null_t; + struct node_tuple, void> { + using node_type = is_not_null_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::negated_condition_t; + struct node_tuple, void> { + using node_type = negated_condition_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = core_functions::core_function_t; + struct node_tuple, void> { + using node_type = core_function_t; using type = typename conc_tuple::type...>::type; }; template - struct node_tuple, void> { - using node_type = conditions::left_join_t; + struct node_tuple, void> { + using node_type = left_join_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::on_t; + struct node_tuple, void> { + using node_type = on_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::join_t; + struct node_tuple, void> { + using node_type = join_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::left_outer_join_t; + struct node_tuple, void> { + using node_type = left_outer_join_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::inner_join_t; + struct node_tuple, void> { + using node_type = inner_join_t; using type = typename node_tuple::type; }; @@ -263,20 +262,20 @@ namespace sqlite_orm { }; template - struct node_tuple, void> { - using node_type = conditions::limit_t; + struct node_tuple, void> { + using node_type = limit_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::limit_t; + struct node_tuple, void> { + using node_type = limit_t; using type = typename conc_tuple::type, typename node_tuple::type>::type; }; template - struct node_tuple, void> { - using node_type = conditions::limit_t; + struct node_tuple, void> { + using node_type = limit_t; using type = typename conc_tuple::type, typename node_tuple::type>::type; }; } diff --git a/dev/object_from_column_builder.h b/dev/object_from_column_builder.h new file mode 100644 index 000000000..6c45e2739 --- /dev/null +++ b/dev/object_from_column_builder.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include "row_extractor.h" + +namespace sqlite_orm { + + namespace internal { + + struct object_from_column_builder_base { + sqlite3_stmt *stmt = nullptr; + mutable int index = 0; + }; + + /** + * 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 C &c) const { + using field_type = typename C::field_type; + auto value = row_extractor().extract(this->stmt, this->index++); + if(c.member_pointer) { + this->object.*c.member_pointer = std::move(value); + } else { + ((this->object).*(c.setter))(std::move(value)); + } + } + }; + + } +} diff --git a/dev/operators.h b/dev/operators.h index da06aa7e7..9136e6a2c 100644 --- a/dev/operators.h +++ b/dev/operators.h @@ -2,6 +2,8 @@ #include // std::false_type, std::true_type +#include "negatable.h" + namespace sqlite_orm { namespace internal { @@ -44,7 +46,7 @@ namespace sqlite_orm { * Result of addition + operator */ template - using add_t = binary_operator; + using add_t = binary_operator; struct sub_string { operator std::string() const { @@ -56,7 +58,7 @@ namespace sqlite_orm { * Result of substitute - operator */ template - using sub_t = binary_operator; + using sub_t = binary_operator; struct mul_string { operator std::string() const { @@ -68,7 +70,7 @@ namespace sqlite_orm { * Result of multiply * operator */ template - using mul_t = binary_operator; + using mul_t = binary_operator; struct div_string { operator std::string() const { @@ -80,7 +82,7 @@ namespace sqlite_orm { * Result of divide / operator */ template - using div_t = binary_operator; + using div_t = binary_operator; struct mod_string { operator std::string() const { @@ -92,14 +94,79 @@ namespace sqlite_orm { * Result of mod % operator */ template - using mod_t = binary_operator; + using mod_t = binary_operator; + + struct bitwise_shift_left_string { + operator std::string() const { + return "<<"; + } + }; + + /** + * Result of bitwise shift left << operator + */ + template + using bitwise_shift_left_t = binary_operator; + + struct bitwise_shift_right_string { + operator std::string() const { + return ">>"; + } + }; + + /** + * Result of bitwise shift right >> operator + */ + template + using bitwise_shift_right_t = binary_operator; + + struct bitwise_and_string { + operator std::string() const { + return "&"; + } + }; + + /** + * Result of bitwise and & operator + */ + template + using bitwise_and_t = binary_operator; + + struct bitwise_or_string { + operator std::string() const { + return "|"; + } + }; + + /** + * Result of bitwise or | operator + */ + template + using bitwise_or_t = binary_operator; + + struct bitwise_not_string { + operator std::string() const { + return "~"; + } + }; + + /** + * Result of bitwise not ~ operator + */ + template + struct 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 { operator std::string() const { return "="; } }; - /** * Result of assign = operator */ @@ -188,6 +255,31 @@ namespace sqlite_orm { 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/dev/order_by_serializator.h b/dev/order_by_serializator.h new file mode 100644 index 000000000..082b61647 --- /dev/null +++ b/dev/order_by_serializator.h @@ -0,0 +1,87 @@ +#pragma once + +#include // std::string +#include // std::vector +#include // std::stringstream + +namespace sqlite_orm { + + namespace internal { + + template + struct order_by_serializator; + + template + std::string serialize_order_by(const T &t, const C &context) { + order_by_serializator serializator; + return serializator(t, context); + } + + template + struct order_by_serializator, void> { + using statement_type = order_by_t; + + template + std::string operator()(const statement_type &orderBy, const C &context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + auto columnName = serialize(orderBy.o, newContext); + ss << columnName << " "; + if(orderBy._collate_argument.length()) { + 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_serializator, void> { + using statement_type = dynamic_order_by_t; + + template + std::string operator()(const statement_type &orderBy, const C &) const { + std::vector expressions; + for(auto &entry: orderBy) { + std::string entryString; + { + std::stringstream 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; + } + entryString = ss.str(); + } + expressions.push_back(move(entryString)); + }; + std::stringstream ss; + ss << static_cast(orderBy) << " "; + for(size_t i = 0; i < expressions.size(); ++i) { + ss << expressions[i]; + if(i < expressions.size() - 1) { + ss << ", "; + } + } + ss << " "; + return ss.str(); + } + }; + + } +} diff --git a/dev/pragma.h b/dev/pragma.h index 059fbe45e..8e456c581 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -21,6 +21,14 @@ namespace sqlite_orm { 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"); } @@ -69,7 +77,7 @@ namespace sqlite_orm { T get_pragma(const std::string &name) { auto connection = this->get_connection(); auto query = "PRAGMA " + name; - T res; + T result; auto db = connection.get(); auto rc = sqlite3_exec( db, @@ -81,10 +89,10 @@ namespace sqlite_orm { } return 0; }, - &res, + &result, nullptr); if(rc == SQLITE_OK) { - return res; + return result; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); diff --git a/dev/prepared_statement.h b/dev/prepared_statement.h index 28b4ded94..630a1d27b 100644 --- a/dev/prepared_statement.h +++ b/dev/prepared_statement.h @@ -72,22 +72,33 @@ namespace sqlite_orm { expression_type t; - prepared_statement_t(T t_, sqlite3_stmt *stmt, connection_ref con_) : - prepared_statement_base{stmt, std::move(con_)}, t(std::move(t_)) {} + prepared_statement_t(T t_, sqlite3_stmt *stmt_, connection_ref con_) : + prepared_statement_base{stmt_, std::move(con_)}, t(std::move(t_)) {} }; - template + template + struct is_prepared_statement : std::false_type {}; + + template + struct is_prepared_statement> : std::true_type {}; + + /** + * 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 + template struct get_all_pointer_t { using type = T; + using return_type = R; using conditions_type = std::tuple; @@ -95,9 +106,10 @@ namespace sqlite_orm { }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template + template struct get_all_optional_t { using type = T; + using return_type = R; using conditions_type = std::tuple; @@ -226,8 +238,8 @@ namespace sqlite_orm { * Create a replace statement. * T is an object type mapped to a storage. * Usage: storage.replace(myUserInstance); - * Parameter obj is accepted by value. Is you want to accept it by ref - * the use std::ref function: storage.replace(std::ref(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) { @@ -238,8 +250,8 @@ namespace sqlite_orm { * Create an insert statement. * T is an object type mapped to a storage. * Usage: storage.insert(myUserInstance); - * Parameter obj is accepted by value. Is you want to accept it by ref - * the use std::ref function: storage.insert(std::ref(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) { @@ -251,8 +263,8 @@ namespace sqlite_orm { * 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. Is you want to accept it by ref - * the use std::ref function: storage.insert(std::ref(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) { @@ -274,8 +286,8 @@ namespace sqlite_orm { * Create an update statement. * T is an object type mapped to a storage. * Usage: storage.update(myUserInstance); - * Parameter obj is accepted by value. Is you want to accept it by ref - * the use std::ref function: storage.update(std::ref(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) { @@ -336,7 +348,21 @@ namespace sqlite_orm { * Usage: storage.get_all(...); */ template - internal::get_all_t get_all(Args... args) { + internal::get_all_t, Args...> get_all(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {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)...}; @@ -361,8 +387,23 @@ namespace sqlite_orm { * Usage: storage.get_all_pointer(...); */ template - internal::get_all_pointer_t get_all_pointer(Args... args) { - std::tuple conditions{std::forward(args)...}; + 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 {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 {move(conditions)}; } @@ -373,8 +414,24 @@ namespace sqlite_orm { * Usage: storage.get_all_optional(...); */ template - internal::get_all_optional_t get_all_optional(Args... args) { - std::tuple conditions{std::forward(args)...}; + 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 {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 {move(conditions)}; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED diff --git a/dev/row_extractor.h b/dev/row_extractor.h index 2108415e3..41027d0ee 100644 --- a/dev/row_extractor.h +++ b/dev/row_extractor.h @@ -77,8 +77,8 @@ namespace sqlite_orm { /** * Specialization for std::string. */ - template - struct row_extractor::value>> { + template<> + struct row_extractor { std::string extract(const char *row_value) { if(row_value) { return row_value; @@ -100,8 +100,8 @@ namespace sqlite_orm { /** * Specialization for std::wstring. */ - template - struct row_extractor::value>> { + template<> + struct row_extractor { std::wstring extract(const char *row_value) { if(row_value) { std::wstring_convert> converter; @@ -122,38 +122,6 @@ namespace sqlite_orm { } }; #endif // SQLITE_ORM_OMITS_CODECVT - /** - * Specialization for std::vector. - */ - template - struct row_extractor>::value>> { - std::vector extract(const char *row_value) { - if(row_value) { - auto len = ::strlen(row_value); - return this->go(row_value, len); - } else { - return {}; - } - } - - std::vector extract(sqlite3_stmt *stmt, int columnIndex) { - auto bytes = static_cast(sqlite3_column_blob(stmt, columnIndex)); - auto len = sqlite3_column_bytes(stmt, columnIndex); - return this->go(bytes, len); - } - - protected: - std::vector go(const char *bytes, size_t len) { - if(len) { - std::vector res; - res.reserve(len); - std::copy(bytes, bytes + len, std::back_inserter(res)); - return res; - } else { - return {}; - } - } - }; template struct row_extractor::value>> { @@ -277,8 +245,8 @@ namespace sqlite_orm { /** * Specialization for journal_mode. */ - template - struct row_extractor::value>> { + template<> + struct row_extractor { journal_mode extract(const char *row_value) { if(row_value) { if(auto res = internal::journal_mode_from_string(row_value)) { diff --git a/dev/row_extractor_builder.h b/dev/row_extractor_builder.h new file mode 100644 index 000000000..20c20b764 --- /dev/null +++ b/dev/row_extractor_builder.h @@ -0,0 +1,43 @@ +#pragma once + +#include "row_extractor.h" +#include "mapped_row_extractor.h" + +namespace sqlite_orm { + + namespace internal { + + /** + * This builder is used to construct different row extractors depending on type. + * It has two specializations: for mapped to storage types (e.g. User, Visit etc) and + * for non-mapped (e.g. std::string, QString, int etc). For non mapped its operator() returns + * generic `row_extractor`, for mapped it returns `mapped_row_extractor` instance. + */ + template + struct row_extractor_builder; + + template + struct row_extractor_builder { + + row_extractor operator()(const I * /*tableInfo*/) const { + return {}; + } + }; + + template + struct row_extractor_builder { + + mapped_row_extractor operator()(const I *tableInfo) const { + return {*tableInfo}; + } + }; + + template + auto make_row_extractor(const I *tableInfo) { + using builder_t = row_extractor_builder; + return builder_t{}(tableInfo); + } + + } + +} diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 0c2a2559c..406ded4b1 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -123,8 +123,8 @@ namespace sqlite_orm { using left_type = typename compound_operator::left_type; using right_type = typename compound_operator::right_type; - union_t(left_type l, right_type r, decltype(all) all) : - compound_operator(std::move(l), std::move(r)), union_base{all} {} + union_t(left_type l, right_type r, decltype(all) 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) {} }; @@ -179,6 +179,11 @@ namespace sqlite_orm { using type = T; }; + template + struct object_t { + using type = T; + }; + template struct then_t { using expression_type = T; @@ -217,9 +222,9 @@ namespace sqlite_orm { template simple_case_builder> when(W w, then_t t) { using result_args_type = std::tuple>; - result_args_type result_args; - move_tuple::value>(result_args, this->args); std::pair newPair{std::move(w), std::move(t.expression)}; + result_args_type result_args = + std::tuple_cat(std::move(this->args), std::move(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)}; } @@ -236,14 +241,10 @@ namespace sqlite_orm { 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 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"); } } @@ -351,8 +352,27 @@ namespace sqlite_orm { return {std::move(lhs), std::move(rhs), true}; } + /** + * SELECT * FROM T function. + * T is typed mapped to a storage. + * Example: auto rows = storage.select(asterisk()); + * // decltype(rows) is std::vector> + * If you need to fetch result as objects not tuple please use `object` instead. + */ template internal::asterisk_t asterisk() { return {}; } + + /** + * SELECT * FROM T function. + * T is typed mapped to a storage. + * Example: auto rows = storage.select(object()); + * // decltype(rows) is std::vector + * If you need to fetch result as tuples not objects please use `asterisk` instead. + */ + template + internal::object_t object() { + return {}; + } } diff --git a/dev/serializator_context.h b/dev/serializator_context.h new file mode 100644 index 000000000..e568eed75 --- /dev/null +++ b/dev/serializator_context.h @@ -0,0 +1,48 @@ +#pragma once + +namespace sqlite_orm { + + namespace internal { + + struct serializator_context_base { + bool replace_bindable_with_question = false; + bool skip_table_name = true; + bool use_parentheses = true; + + template + std::string column_name(F O::*) const { + return {}; + } + }; + + template + struct serializator_context : serializator_context_base { + using impl_type = I; + + const impl_type &impl; + + serializator_context(const impl_type &impl_) : impl(impl_) {} + + template + std::string column_name(F O::*m) const { + return this->impl.column_name(m); + } + }; + + template + struct serializator_context_builder { + using storage_type = S; + using impl_type = typename storage_type::impl_type; + + serializator_context_builder(const storage_type &storage_) : storage(storage_) {} + + serializator_context operator()() const { + return {this->storage.impl}; + } + + const storage_type &storage; + }; + + } + +} diff --git a/dev/sqlite_type.h b/dev/sqlite_type.h deleted file mode 100644 index ce51889a6..000000000 --- a/dev/sqlite_type.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -#include // std::map -#include // std::string -#include // std::regex, std::regex_match -#include // std::make_unique, std::unique_ptr -#include // std::vector -#include // std::toupper - -namespace sqlite_orm { - using int64 = sqlite_int64; - using uint64 = sqlite_uint64; - - // numeric and real are the same for c++ - enum class sqlite_type { - INTEGER, - TEXT, - BLOB, - REAL, - }; - - /** - * @param str case doesn't matter - it is uppercased before comparing. - */ - inline std::unique_ptr to_sqlite_type(const std::string &str) { - auto asciiStringToUpper = [](std::string &s) { - std::transform(s.begin(), s.end(), s.begin(), [](char c) { - return std::toupper(c); - }); - }; - auto upperStr = str; - asciiStringToUpper(upperStr); - - static std::map> typeMap = { - {sqlite_type::INTEGER, - { - std::regex("INT"), - std::regex("INT.*"), - std::regex("TINYINT"), - std::regex("SMALLINT"), - std::regex("MEDIUMINT"), - std::regex("BIGINT"), - std::regex("UNSIGNED BIG INT"), - std::regex("INT2"), - std::regex("INT8"), - }}, - {sqlite_type::TEXT, - { - std::regex("CHARACTER\\([[:digit:]]+\\)"), - std::regex("VARCHAR\\([[:digit:]]+\\)"), - std::regex("VARYING CHARACTER\\([[:digit:]]+\\)"), - std::regex("NCHAR\\([[:digit:]]+\\)"), - std::regex("NATIVE CHARACTER\\([[:digit:]]+\\)"), - std::regex("NVARCHAR\\([[:digit:]]+\\)"), - std::regex("CLOB"), - std::regex("TEXT"), - }}, - {sqlite_type::BLOB, - { - std::regex("BLOB"), - }}, - {sqlite_type::REAL, - { - std::regex("REAL"), - std::regex("DOUBLE"), - std::regex("DOUBLE PRECISION"), - std::regex("FLOAT"), - std::regex("NUMERIC"), - std::regex("DECIMAL\\([[:digit:]]+,[[:digit:]]+\\)"), - std::regex("BOOLEAN"), - std::regex("DATE"), - std::regex("DATETIME"), - }}, - }; - for(auto &p: typeMap) { - for(auto &r: p.second) { - if(std::regex_match(upperStr, r)) { - return std::make_unique(p.first); - } - } - } - - return {}; - } -} diff --git a/dev/statement_binder.h b/dev/statement_binder.h index 4ca80ca95..b9895702e 100644 --- a/dev/statement_binder.h +++ b/dev/statement_binder.h @@ -4,11 +4,12 @@ #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type #include // std::string, std::wstring #ifndef SQLITE_ORM_OMITS_CODECVT -#include // std::wstring_convert, std::codecvt_utf8_utf16 +#include // std::codecvt_utf8_utf16 #endif // SQLITE_ORM_OMITS_CODECVT #include // std::vector #include // std::nullptr_t #include // std::declval +#include // std::wstring_convert #include "is_std_ptr.h" diff --git a/dev/statement_serializator.h b/dev/statement_serializator.h index ac0a18342..e43801bac 100644 --- a/dev/statement_serializator.h +++ b/dev/statement_serializator.h @@ -1,44 +1,94 @@ #pragma once -#include +#include // std::stringstream #include // std::string +#include // std::enable_if +#include // std::vector +#include // std::iter_swap #include "core_functions.h" #include "constraints.h" +#include "conditions.h" +#include "column.h" +#include "rowid.h" +#include "type_printer.h" +#include "table_name_collector.h" +#include "column_names_getter.h" +#include "order_by_serializator.h" +#include "values.h" +#include "table_type.h" +#include "indexed_column.h" namespace sqlite_orm { namespace internal { template - struct statement_serializator { + struct statement_serializator; + + template + std::string serialize(const T &t, const C &context) { + statement_serializator serializator; + return serializator(t, context); + } + + template + struct statement_serializator::value>::type> { using statement_type = T; - std::string operator()(const statement_type &t) const { - std::stringstream ss; - ss << t; - return ss.str(); + template + std::string operator()(const statement_type &statement, const C &context) { + if(context.replace_bindable_with_question) { + return "?"; + } else { + return field_printer{}(statement); + } } }; template - std::string serialize(const T &t) { - statement_serializator serializator; - return serializator(t); - } + struct statement_serializator, void> { + using statement_type = std::reference_wrapper; + + template + std::string operator()(const statement_type &s, const C &context) { + return serialize(s.get(), context); + } + }; + + template<> + struct statement_serializator { + using statement_type = std::nullptr_t; + + template + std::string operator()(const statement_type &, const C &) { + return "?"; + } + }; + + template + struct statement_serializator, void> { + using statement_type = alias_holder; + + template + std::string operator()(const statement_type &, const C &) { + return T::get(); + } + }; template - struct statement_serializator, void> { - using statement_type = core_functions::core_function_t; + struct statement_serializator, void> { + using statement_type = core_function_t; - std::string operator()(const statement_type &c) const { + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << static_cast(c) << "("; std::vector args; using args_type = typename std::decay::type::args_type; args.reserve(std::tuple_size::value); - iterate_tuple(c.args, [&args](auto &v) { - args.push_back(serialize(v)); + iterate_tuple(c.args, [&args, &context](auto &v) { + args.push_back(serialize(v, context)); }); for(size_t i = 0; i < args.size(); ++i) { ss << args[i]; @@ -51,11 +101,482 @@ namespace sqlite_orm { } }; + template + struct statement_serializator, void> { + using statement_type = as_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto tableAliasString = alias_extractor::get(); + return serialize(c.expression, context) + " AS " + tableAliasString; + } + }; + + template + struct statement_serializator, void> { + using statement_type = alias_column_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + if(!context.skip_table_name) { + ss << "'" << T::get() << "'."; + } + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(c.column, newContext); + return ss.str(); + } + }; + + template<> + struct statement_serializator { + using statement_type = std::string; + + template + std::string operator()(const statement_type &c, const C &context) const { + if(context.replace_bindable_with_question) { + return "?"; + } else { + return "\"" + c + "\""; + } + } + }; + + template<> + struct statement_serializator { + using statement_type = const char *; + + template + std::string operator()(const char *c, const C &context) const { + if(context.replace_bindable_with_question) { + return "?"; + } else { + return std::string("'") + c + "'"; + } + } + }; + + template + struct statement_serializator { + using statement_type = F O::*; + + template + std::string operator()(const statement_type &m, const C &context) const { + std::stringstream ss; + if(!context.skip_table_name) { + ss << "\"" << context.impl.find_table_name(typeid(O)) << "\"."; + } + ss << "\"" << context.column_name(m) << "\""; + return ss.str(); + } + }; + + template<> + struct statement_serializator { + using statement_type = rowid_t; + + template + std::string operator()(const statement_type &s, const C &) { + return static_cast(s); + } + }; + + template<> + struct statement_serializator { + using statement_type = oid_t; + + template + std::string operator()(const statement_type &s, const C &) { + return static_cast(s); + } + }; + + template<> + struct statement_serializator<_rowid_t, void> { + using statement_type = _rowid_t; + + template + std::string operator()(const statement_type &s, const C &) { + return static_cast(s); + } + }; + + template + struct statement_serializator, void> { + using statement_type = table_rowid_t; + + template + std::string operator()(const statement_type &s, const C &context) { + std::stringstream ss; + if(!context.skip_table_name) { + ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; + } + ss << static_cast(s); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = table_oid_t; + + template + std::string operator()(const statement_type &s, const C &context) { + std::stringstream ss; + if(!context.skip_table_name) { + ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; + } + ss << static_cast(s); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = table__rowid_t; + + template + std::string operator()(const statement_type &s, const C &context) { + std::stringstream ss; + if(!context.skip_table_name) { + ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; + } + ss << static_cast(s); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = binary_operator; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto lhs = serialize(c.lhs, context); + auto rhs = serialize(c.rhs, context); + std::stringstream ss; + ss << "(" << lhs << " " << static_cast(c) << " " << rhs << ")"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = count_asterisk_t; + + template + std::string operator()(const statement_type &, const C &context) const { + return serialize(count_asterisk_without_type{}, context); + } + }; + + template<> + struct statement_serializator { + using statement_type = count_asterisk_without_type; + + template + std::string operator()(const statement_type &c, const C &) const { + std::stringstream ss; + ss << static_cast(c) << "(*)"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = distinct_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + auto expr = serialize(c.t, context); + ss << static_cast(c) << "(" << expr << ")"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = all_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + auto expr = serialize(c.t, context); + ss << static_cast(c) << "(" << expr << ")"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = column_pointer; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + if(!context.skip_table_name) { + ss << "'" << context.impl.find_table_name(typeid(T)) << "'."; + } + ss << "\"" << context.impl.column_name_simple(c.field) << "\""; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = cast_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << static_cast(c) << " ("; + ss << serialize(c.expression, context) << " AS " << type_printer().print() << ")"; + return ss.str(); + } + }; + + template + struct statement_serializator::value>::type> { + using statement_type = T; + + template + std::string operator()(const statement_type &c, const C &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_serializator, void> { + using statement_type = simple_case_t; + + template + std::string operator()(const statement_type &c, const C &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_serializator, void> { + using statement_type = is_null_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << serialize(c.t, context) << " " << static_cast(c); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = is_not_null_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << serialize(c.t, context) << " " << static_cast(c); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = bitwise_not_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << static_cast(c) << " "; + auto cString = serialize(c.argument, context); + ss << " (" << cString << " )"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = negated_condition_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << static_cast(c) << " "; + auto cString = serialize(c.c, context); + ss << " (" << cString << " )"; + return ss.str(); + } + }; + + template + struct statement_serializator::value>::type> { + using statement_type = T; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto leftString = serialize(c.l, context); + auto rightString = serialize(c.r, context); + std::stringstream ss; + if(context.use_parentheses) { + ss << "("; + } + ss << leftString << " " << static_cast(c) << " " << rightString; + if(context.use_parentheses) { + ss << ")"; + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = named_collate; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto newContext = context; + newContext.use_parentheses = false; + auto res = serialize(c.expr, newContext); + return res + " " + static_cast(c); + } + }; + + template + struct statement_serializator, void> { + using statement_type = collate_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto newContext = context; + newContext.use_parentheses = false; + auto res = serialize(c.expr, newContext); + return res + " " + static_cast(c); + } + }; + + template + struct statement_serializator, void> { + using statement_type = in_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + auto leftString = serialize(c.l, context); + ss << leftString << " " << static_cast(c) << " "; + auto newContext = context; + newContext.use_parentheses = true; + ss << serialize(c.arg, newContext); + return ss.str(); + } + }; + + template + struct statement_serializator>, void> { + using statement_type = in_t>; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + auto leftString = serialize(c.l, context); + ss << leftString << " " << static_cast(c) << " ( "; + for(size_t index = 0; index < c.arg.size(); ++index) { + auto &value = c.arg[index]; + ss << " " << serialize(value, context); + if(index < c.arg.size() - 1) { + ss << ", "; + } + } + ss << " )"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = like_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << serialize(c.arg, context) << " "; + ss << static_cast(c) << " "; + ss << serialize(c.pattern, context); + c.arg3.apply([&ss, &context](auto &value) { + ss << " ESCAPE " << serialize(value, context); + }); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = glob_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << serialize(c.arg, context) << " "; + ss << static_cast(c) << " "; + ss << serialize(c.pattern, context); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = between_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + auto expr = serialize(c.expr, context); + ss << expr << " " << static_cast(c) << " "; + ss << serialize(c.b1, context); + ss << " AND "; + ss << serialize(c.b2, context); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = exists_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << static_cast(c) << " "; + ss << serialize(c.t, context); + return ss.str(); + } + }; + template<> struct statement_serializator { using statement_type = constraints::autoincrement_t; - std::string operator()(const statement_type &c) const { + template + std::string operator()(const statement_type &c, const C &) const { return static_cast(c); } }; @@ -64,17 +585,49 @@ namespace sqlite_orm { struct statement_serializator, void> { using statement_type = constraints::primary_key_t; - std::string operator()(const statement_type &c) const { - return static_cast(c); + template + std::string operator()(const statement_type &c, const C &context) const { + auto res = static_cast(c); + using columns_tuple = typename statement_type::columns_tuple; + auto columnsCount = std::tuple_size::value; + if(columnsCount) { + res += "("; + decltype(columnsCount) columnIndex = 0; + iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto &column) { + res += context.column_name(column); + if(columnIndex < columnsCount - 1) { + res += ", "; + } + ++columnIndex; + }); + res += ")"; + } + return res; } }; - template<> - struct statement_serializator { - using statement_type = constraints::unique_t; + template + struct statement_serializator, void> { + using statement_type = constraints::unique_t; - std::string operator()(const statement_type &c) const { - return static_cast(c); + template + std::string operator()(const statement_type &c, const C &context) const { + auto res = static_cast(c); + using columns_tuple = typename statement_type::columns_tuple; + auto columnsCount = std::tuple_size::value; + if(columnsCount) { + res += "("; + decltype(columnsCount) columnIndex = 0; + iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto &column) { + res += context.column_name(column); + if(columnIndex < columnsCount - 1) { + res += ", "; + } + ++columnIndex; + }); + res += ")"; + } + return res; } }; @@ -82,7 +635,8 @@ namespace sqlite_orm { struct statement_serializator { using statement_type = constraints::collate_t; - std::string operator()(const statement_type &c) const { + template + std::string operator()(const statement_type &c, const C &) const { return static_cast(c); } }; @@ -91,26 +645,1072 @@ namespace sqlite_orm { struct statement_serializator, void> { using statement_type = constraints::default_t; - std::string operator()(const statement_type &c) const { - return static_cast(c) + " (" + serialize(c.value) + ")"; + template + std::string operator()(const statement_type &c, const C &context) const { + return static_cast(c) + " (" + serialize(c.value, context) + ")"; } }; - template<> - struct statement_serializator { - using statement_type = std::string; - - std::string operator()(const statement_type &c) const { - return "\"" + c + "\""; - } - }; + template + struct statement_serializator, std::tuple>, void> { + using statement_type = constraints::foreign_key_t, std::tuple>; - template<> - struct statement_serializator { - using statement_type = const char *; + template + std::string operator()(const statement_type &fk, const C &context) const { + std::stringstream ss; + std::vector columnNames; + using columns_type_t = typename std::decay::type::columns_type; + constexpr const size_t columnsCount = std::tuple_size::value; + columnNames.reserve(columnsCount); + iterate_tuple(fk.columns, [&columnNames, &context](auto &v) { + columnNames.push_back(context.impl.column_name(v)); + }); + ss << "FOREIGN KEY("; + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << "'" << columnNames[i] << "'"; + if(i < columnNames.size() - 1) { + ss << ", "; + } + } + ss << ") REFERENCES "; + std::vector referencesNames; + using references_type_t = typename std::decay::type::references_type; + constexpr const size_t referencesCount = std::tuple_size::value; + referencesNames.reserve(referencesCount); + { + using first_reference_t = typename std::tuple_element<0, references_type_t>::type; + using first_reference_mapped_type = typename internal::table_type::type; + auto refTableName = context.impl.find_table_name(typeid(first_reference_mapped_type)); + ss << '\'' << refTableName << '\''; + } + iterate_tuple(fk.references, [&referencesNames, &context](auto &v) { + referencesNames.push_back(context.impl.column_name(v)); + }); + ss << "("; + for(size_t i = 0; i < referencesNames.size(); ++i) { + ss << "'" << referencesNames[i] << "'"; + if(i < referencesNames.size() - 1) { + ss << ", "; + } + } + ss << ")"; + 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_serializator, void> { + using statement_type = constraints::check_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + return static_cast(c) + " " + serialize(c.expression, context); + } + }; + + template + struct statement_serializator, void> { + using statement_type = column_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << "'" << c.name << "' "; + using column_type = typename std::decay::type; + using field_type = typename column_type::field_type; + using constraints_type = typename column_type::constraints_type; + ss << type_printer().print() << " "; + { + std::vector constraintsStrings; + constexpr const size_t constraintsCount = std::tuple_size::value; + constraintsStrings.reserve(constraintsCount); + int primaryKeyIndex = -1; + int autoincrementIndex = -1; + int tupleIndex = 0; + iterate_tuple( + c.constraints, + [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context](auto &v) { + using constraint_type = typename std::decay::type; + constraintsStrings.push_back(serialize(v, context)); + if(is_primary_key::value) { + primaryKeyIndex = tupleIndex; + } else if(std::is_same::value) { + 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(c.not_null()) { + ss << "NOT NULL "; + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = remove_all_t; + + template + std::string operator()(const statement_type &rem, const C &context) const { + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "DELETE FROM '" << tImpl.table.name << "' "; + iterate_tuple(rem.conditions, [&context, &ss](auto &v) { + ss << serialize(v, context); + }); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = replace_t; + + template + std::string operator()(const statement_type &rep, const C &context) const { + using expression_type = typename std::decay::type; + using object_type = typename expression_object_type::type; + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "REPLACE INTO '" << tImpl.table.name << "' ("; + auto columnNames = tImpl.table.column_names(); + auto columnNamesCount = columnNames.size(); + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNamesCount - 1) { + ss << ","; + } else { + ss << ")"; + } + ss << " "; + } + ss << "VALUES("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "?"; + if(i < columnNamesCount - 1) { + ss << ", "; + } else { + ss << ")"; + } + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = insert_explicit; + + template + std::string operator()(const statement_type &ins, const C &context) const { + constexpr const size_t colsCount = std::tuple_size>::value; + static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); + using expression_type = typename std::decay::type; + using object_type = typename expression_object_type::type; + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "INSERT INTO '" << tImpl.table.name << "' "; + std::vector columnNames; + columnNames.reserve(colsCount); + { + auto columnsContext = context; + columnsContext.skip_table_name = true; + iterate_tuple(ins.columns.columns, [&columnNames, &columnsContext](auto &m) { + auto columnName = serialize(m, columnsContext); + if(!columnName.empty()) { + columnNames.push_back(columnName); + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } + }); + } + ss << "("; + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << columnNames[i]; + if(i < columnNames.size() - 1) { + ss << ","; + } else { + ss << ")"; + } + ss << " "; + } + ss << "VALUES ("; + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << "?"; + if(i < columnNames.size() - 1) { + ss << ","; + } else { + ss << ")"; + } + ss << " "; + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = update_t; + + template + std::string operator()(const statement_type &upd, const C &context) const { + using expression_type = typename std::decay::type; + using object_type = typename expression_object_type::type; + auto &tImpl = context.impl.template get_impl(); + + std::stringstream ss; + ss << "UPDATE '" << tImpl.table.name << "' SET "; + std::vector setColumnNames; + tImpl.table.for_each_column([&setColumnNames](auto &c) { + if(!c.template has>()) { + setColumnNames.emplace_back(c.name); + } + }); + for(size_t i = 0; i < setColumnNames.size(); ++i) { + ss << "\"" << setColumnNames[i] << "\"" + << " = ?"; + if(i < setColumnNames.size() - 1) { + ss << ","; + } + ss << " "; + } + ss << "WHERE "; + auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); + for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { + ss << "\"" << primaryKeyColumnNames[i] << "\"" + << " = ?"; + if(i < primaryKeyColumnNames.size() - 1) { + ss << " AND"; + } + ss << " "; + } + return ss.str(); + } + }; + + template + struct statement_serializator, Wargs...>, void> { + using statement_type = update_all_t, Wargs...>; + + template + std::string operator()(const statement_type &upd, const C &context) const { + std::stringstream ss; + ss << "UPDATE "; + table_name_collector collector{[&context](std::type_index ti) { + return context.impl.find_table_name(ti); + }}; + iterate_ast(upd.set.assigns, collector); + if(!collector.table_names.empty()) { + if(collector.table_names.size() == 1) { + ss << " '" << collector.table_names.begin()->first << "' "; + ss << static_cast(upd.set) << " "; + std::vector setPairs; + auto leftContext = context; + leftContext.skip_table_name = true; + iterate_tuple(upd.set.assigns, [&context, &leftContext, &setPairs](auto &asgn) { + std::stringstream sss; + sss << serialize(asgn.lhs, leftContext); + sss << " " << static_cast(asgn) << " "; + sss << serialize(asgn.rhs, context) << " "; + setPairs.push_back(sss.str()); + }); + auto setPairsCount = setPairs.size(); + for(size_t i = 0; i < setPairsCount; ++i) { + ss << setPairs[i] << " "; + if(i < setPairsCount - 1) { + ss << ", "; + } + } + iterate_tuple(upd.conditions, [&context, &ss](auto &v) { + ss << serialize(v, context); + }); + return ss.str(); + } else { + throw std::system_error(std::make_error_code(orm_error_code::too_many_tables_specified)); + } + } else { + throw std::system_error(std::make_error_code(orm_error_code::incorrect_set_fields_specified)); + } + } + }; + + template + struct statement_serializator, void> { + using statement_type = insert_t; + + template + std::string operator()(const statement_type &, const C &context) const { + using object_type = typename expression_object_type::type; + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "INSERT INTO '" << tImpl.table.name << "' "; + std::vector columnNames; + auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); + + tImpl.table.for_each_column([&tImpl, &columnNames, &compositeKeyColumnNames](auto &c) { + if(tImpl.table._without_rowid || !c.template has>()) { + auto it = find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); + if(it == compositeKeyColumnNames.end()) { + columnNames.emplace_back(c.name); + } + } + }); + + auto columnNamesCount = columnNames.size(); + if(columnNamesCount) { + ss << "("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNamesCount - 1) { + ss << ","; + } else { + ss << ")"; + } + ss << " "; + } + } else { + ss << "DEFAULT "; + } + ss << "VALUES "; + if(columnNamesCount) { + ss << "("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "?"; + if(i < columnNamesCount - 1) { + ss << ", "; + } else { + ss << ")"; + } + } + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = remove_t; + + template + std::string operator()(const statement_type &, const C &context) const { + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "DELETE FROM '" << tImpl.table.name << "' "; + ss << "WHERE "; + auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); + for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { + ss << "\"" << primaryKeyColumnNames[i] << "\"" + << " = ? "; + if(i < primaryKeyColumnNames.size() - 1) { + ss << "AND "; + } + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = replace_range_t; + + template + std::string operator()(const statement_type &rep, const C &context) const { + using expression_type = typename std::decay::type; + using object_type = typename expression_type::object_type; + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "REPLACE INTO '" << tImpl.table.name << "' ("; + auto columnNames = tImpl.table.column_names(); + auto columnNamesCount = columnNames.size(); + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNamesCount - 1) { + ss << ", "; + } else { + ss << ") "; + } + } + ss << "VALUES "; + auto valuesString = [columnNamesCount] { + std::stringstream ss_; + ss_ << "("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss_ << "?"; + if(i < columnNamesCount - 1) { + ss_ << ", "; + } else { + ss_ << ")"; + } + } + return ss_.str(); + }(); + auto valuesCount = static_cast(std::distance(rep.range.first, rep.range.second)); + for(auto i = 0; i < valuesCount; ++i) { + ss << valuesString; + if(i < valuesCount - 1) { + ss << ","; + } + ss << " "; + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = insert_range_t; + + template + std::string operator()(const statement_type &statement, const C &context) const { + using expression_type = typename std::decay::type; + using object_type = typename expression_type::object_type; + auto &tImpl = context.impl.template get_impl(); + + std::stringstream ss; + ss << "INSERT INTO '" << tImpl.table.name << "' ("; + std::vector columnNames; + tImpl.table.for_each_column([&columnNames](auto &c) { + if(!c.template has>()) { + columnNames.emplace_back(c.name); + } + }); + + auto columnNamesCount = columnNames.size(); + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNamesCount - 1) { + ss << ","; + } else { + ss << ")"; + } + ss << " "; + } + ss << "VALUES "; + auto valuesString = [columnNamesCount] { + std::stringstream ss_; + ss_ << "("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss_ << "?"; + if(i < columnNamesCount - 1) { + ss_ << ", "; + } else { + ss_ << ")"; + } + } + return ss_.str(); + }(); + auto valuesCount = static_cast(std::distance(statement.range.first, statement.range.second)); + for(auto i = 0; i < valuesCount; ++i) { + ss << valuesString; + if(i < valuesCount - 1) { + ss << ","; + } + ss << " "; + } + return ss.str(); + } + }; + + template + std::string serialize_get_all_impl(const T &get, const C &context) { + using primary_type = typename T::type; + + table_name_collector collector; + collector.table_names.insert( + std::make_pair(context.impl.find_table_name(typeid(primary_type)), std::string{})); + iterate_ast(get.conditions, collector); + std::stringstream ss; + ss << "SELECT "; + auto &tImpl = context.impl.template get_impl(); + auto columnNames = tImpl.table.column_names(); + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << "\"" << tImpl.table.name << "\"." + << "\"" << columnNames[i] << "\""; + if(i < columnNames.size() - 1) { + ss << ", "; + } else { + ss << " "; + } + } + ss << "FROM "; + std::vector> tableNames(collector.table_names.begin(), + collector.table_names.end()); + for(size_t i = 0; i < tableNames.size(); ++i) { + auto &tableNamePair = tableNames[i]; + ss << "'" << tableNamePair.first << "' "; + if(!tableNamePair.second.empty()) { + ss << tableNamePair.second << " "; + } + if(int(i) < int(tableNames.size()) - 1) { + ss << ","; + } + ss << " "; + } + iterate_tuple(get.conditions, [&context, &ss](auto &v) { + ss << serialize(v, context); + }); + return ss.str(); + } + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializator, void> { + using statement_type = get_all_optional_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_all_impl(get, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct statement_serializator, void> { + using statement_type = get_all_pointer_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_all_impl(get, context); + } + }; + + template + struct statement_serializator, void> { + using statement_type = get_all_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_all_impl(get, context); + } + }; + + template + std::string serialize_get_impl(const T &, const C &context) { + using primary_type = typename T::type; + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "SELECT "; + auto columnNames = tImpl.table.column_names(); + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNames.size() - 1) { + ss << ","; + } + ss << " "; + } + ss << "FROM '" << tImpl.table.name << "' WHERE "; + auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); + if(!primaryKeyColumnNames.empty()) { + for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { + ss << "\"" << primaryKeyColumnNames[i] << "\"" + << " = ? "; + if(i < primaryKeyColumnNames.size() - 1) { + ss << "AND"; + } + ss << ' '; + } + return ss.str(); + } else { + throw std::system_error(std::make_error_code(orm_error_code::table_has_no_primary_key_column)); + } + } + + template + struct statement_serializator, void> { + using statement_type = get_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_impl(get, context); + } + }; + + template + struct statement_serializator, void> { + using statement_type = get_pointer_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_impl(get, context); + } + }; - std::string operator()(const char *c) const { - return std::string("'") + c + "'"; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializator, void> { + using statement_type = get_optional_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_impl(get, context); + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializator, void> { + using statement_type = select_t; + + template + std::string operator()(const statement_type &sel, const C &context) const { + std::stringstream ss; + if(!is_base_of_template::value) { + if(!sel.highest_level) { + ss << "( "; + } + ss << "SELECT "; + } + if(get_distinct(sel.col)) { + ss << static_cast(distinct(0)) << " "; + } + auto columnNames = get_column_names(sel.col, context); + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << columnNames[i]; + if(i < columnNames.size() - 1) { + ss << ","; + } + ss << " "; + } + table_name_collector collector{[&context](std::type_index ti) { + return context.impl.find_table_name(ti); + }}; + iterate_ast(sel.col, collector); + iterate_ast(sel.conditions, collector); + internal::join_iterator()([&collector, &context](const auto &c) { + using original_join_type = typename std::decay::type::join_type::type; + using cross_join_type = typename internal::mapped_type_proxy::type; + auto crossJoinedTableName = context.impl.find_table_name(typeid(cross_join_type)); + auto tableAliasString = alias_extractor::get(); + std::pair tableNameWithAlias(std::move(crossJoinedTableName), + std::move(tableAliasString)); + collector.table_names.erase(tableNameWithAlias); + }); + if(!collector.table_names.empty()) { + ss << "FROM "; + std::vector> tableNames(collector.table_names.begin(), + collector.table_names.end()); + for(size_t i = 0; i < tableNames.size(); ++i) { + auto &tableNamePair = tableNames[i]; + ss << "'" << tableNamePair.first << "' "; + if(!tableNamePair.second.empty()) { + ss << tableNamePair.second << " "; + } + if(int(i) < int(tableNames.size()) - 1) { + ss << ","; + } + ss << " "; + } + } + iterate_tuple(sel.conditions, [&context, &ss](auto &v) { + ss << serialize(v, context); + }); + if(!is_base_of_template::value) { + if(!sel.highest_level) { + ss << ") "; + } + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = indexed_column_t; + + template + std::string operator()(const statement_type &statement, const C &context) const { + std::stringstream ss; + ss << serialize(statement.column_or_expression, context); + if(!statement._collation_name.empty()) { + 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(std::make_error_code(orm_error_code::incorrect_order)); + } + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = index_t; + + template + std::string operator()(const statement_type &statement, const C &context) const { + std::stringstream ss; + ss << "CREATE "; + if(statement.unique) { + ss << "UNIQUE "; + } + using columns_type = typename std::decay::type::columns_type; + using head_t = typename std::tuple_element<0, columns_type>::type::column_type; + using indexed_type = typename table_type::type; + ss << "INDEX IF NOT EXISTS '" << statement.name << "' ON '" + << context.impl.find_table_name(typeid(indexed_type)) << "' ("; + std::vector columnNames; + iterate_tuple(statement.columns, [&columnNames, &context](auto &v) { + columnNames.push_back(context.column_name(v.column_or_expression)); + }); + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << "'" << columnNames[i] << "'"; + if(i < columnNames.size() - 1) { + ss << ", "; + } + } + ss << ")"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = where_t; + + template + std::string operator()(const statement_type &w, const C &context) const { + std::stringstream ss; + ss << static_cast(w) << " "; + auto whereString = serialize(w.c, context); + ss << "( " << whereString << ") "; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = order_by_t; + + template + std::string operator()(const statement_type &orderBy, const C &context) const { + std::stringstream ss; + ss << static_cast(orderBy) << " "; + auto orderByString = serialize_order_by(orderBy, context); + ss << orderByString << " "; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = dynamic_order_by_t; + + template + std::string operator()(const statement_type &orderBy, const CC &context) const { + return serialize_order_by(orderBy, context); + } + }; + + template + struct statement_serializator, void> { + using statement_type = multi_order_by_t; + + template + std::string operator()(const statement_type &orderBy, const C &context) const { + std::stringstream ss; + std::vector expressions; + iterate_tuple(orderBy.args, [&expressions, &context](auto &v) { + auto expression = serialize_order_by(v, context); + expressions.push_back(move(expression)); + }); + ss << static_cast(orderBy) << " "; + for(size_t i = 0; i < expressions.size(); ++i) { + ss << expressions[i]; + if(i < expressions.size() - 1) { + ss << ", "; + } + } + ss << " "; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = cross_join_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << static_cast(c) << " "; + ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = inner_join_t; + + template + std::string operator()(const statement_type &l, const C &context) const { + std::stringstream ss; + ss << static_cast(l) << " "; + auto aliasString = alias_extractor::get(); + ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) << "' "; + if(aliasString.length()) { + ss << "'" << aliasString << "' "; + } + ss << serialize(l.constraint, context); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = on_t; + + template + std::string operator()(const statement_type &t, const C &context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << static_cast(t) << " " << serialize(t.arg, newContext) << " "; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = join_t; + + template + std::string operator()(const statement_type &l, const C &context) const { + std::stringstream ss; + ss << static_cast(l) << " "; + ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + ss << serialize(l.constraint, context); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = left_join_t; + + template + std::string operator()(const statement_type &l, const C &context) const { + std::stringstream ss; + ss << static_cast(l) << " "; + ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + ss << serialize(l.constraint, context); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = left_outer_join_t; + + template + std::string operator()(const statement_type &l, const C &context) const { + std::stringstream ss; + ss << static_cast(l) << " "; + ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + ss << serialize(l.constraint, context); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = natural_join_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << static_cast(c) << " "; + ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = group_by_t; + + template + std::string operator()(const statement_type &groupBy, const C &context) const { + std::stringstream ss; + std::vector expressions; + auto newContext = context; + newContext.skip_table_name = false; + iterate_tuple(groupBy.args, [&expressions, &newContext](auto &v) { + auto expression = serialize(v, newContext); + expressions.push_back(expression); + }); + ss << static_cast(groupBy) << " "; + for(size_t i = 0; i < expressions.size(); ++i) { + ss << expressions[i]; + if(i < expressions.size() - 1) { + ss << ", "; + } + } + ss << " "; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = having_t; + + template + std::string operator()(const statement_type &hav, const C &context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << static_cast(hav) << " "; + ss << serialize(hav.t, newContext) << " "; + return ss.str(); + } + }; + + /** + * HO - has offset + * OI - offset is implicit + */ + template + struct statement_serializator, void> { + using statement_type = limit_t; + + template + std::string operator()(const statement_type &limt, const C &context) const { + auto newContext = context; + newContext.skip_table_name = false; + std::stringstream ss; + ss << static_cast(limt) << " "; + if(HO) { + if(OI) { + limt.off.apply([&newContext, &ss](auto &value) { + 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_serializator, void> { + using statement_type = using_t; + + template + std::string operator()(const statement_type &statement, const C &context) const { + auto newContext = context; + newContext.skip_table_name = true; + return static_cast(statement) + " (" + serialize(statement.column, newContext) + " )"; + } + }; + + template + struct statement_serializator, void> { + using statement_type = std::tuple; + + template + std::string operator()(const statement_type &statement, const C &context) const { + std::stringstream ss; + ss << '('; + auto index = 0; + using TupleSize = std::tuple_size; + iterate_tuple(statement, [&context, &index, &ss](auto &value) { + ss << serialize(value, context); + if(index < TupleSize::value - 1) { + ss << ", "; + } + ++index; + }); + ss << ')'; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = values_t; + + template + std::string operator()(const statement_type &statement, const C &context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << "VALUES "; + { + auto index = 0; + auto &tuple = statement.tuple; + using tuple_type = typename std::decay::type; + using TupleSize = std::tuple_size; + iterate_tuple(tuple, [&context, &index, &ss](auto &value) { + ss << serialize(value, context); + if(index < TupleSize::value - 1) { + ss << ", "; + } + ++index; + }); + } + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = dynamic_values_t; + + template + std::string operator()(const statement_type &statement, const C &context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << "VALUES "; + { + auto vectorSize = statement.vector.size(); + for(decltype(vectorSize) index = 0; index < vectorSize; ++index) { + auto &value = statement.vector[index]; + ss << serialize(value, context); + if(index < vectorSize - 1) { + ss << ", "; + } + } + } + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); } }; diff --git a/dev/static_magic.h b/dev/static_magic.h index 198f42297..23fe1b84d 100644 --- a/dev/static_magic.h +++ b/dev/static_magic.h @@ -8,25 +8,33 @@ namespace sqlite_orm { // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co namespace internal { + static inline decltype(auto) empty_callable() { + static auto res = [](auto &&...) {}; + return (res); + } + template - auto static_if(std::true_type, T t, F) { - return std::move(t); + decltype(auto) static_if(std::true_type, const T &t, const F &) { + return (t); } template - auto static_if(std::false_type, T, F f) { - return std::move(f); + decltype(auto) static_if(std::false_type, const T &, const F &f) { + return (f); } template - auto static_if(T t, F f) { - return static_if(std::integral_constant{}, std::move(t), std::move(f)); + decltype(auto) static_if(const T &t, const F &f) { + return static_if(std::integral_constant{}, t, f); } template - auto static_if(T t) { - return static_if(std::integral_constant{}, t, [](auto &&...) {}); + decltype(auto) static_if(const T &t) { + return static_if(std::integral_constant{}, t, empty_callable()); } + + template + using static_not = std::integral_constant; } } diff --git a/dev/storage.h b/dev/storage.h index 2544642d8..e85255df1 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -12,7 +12,6 @@ #include // std::vector #include // std::tuple_size, std::tuple, std::make_tuple #include // std::forward, std::pair -#include // std::set #include // std::find #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -20,16 +19,14 @@ #endif // SQLITE_ORM_OPTIONAL_SUPPORTED #include "alias.h" -#include "row_extractor.h" +#include "row_extractor_builder.h" #include "error_code.h" #include "type_printer.h" #include "tuple_helper.h" #include "constraints.h" -#include "table_type.h" #include "type_is_nullable.h" #include "field_printer.h" #include "rowid.h" -#include "aggregate_functions.h" #include "operators.h" #include "select_constraints.h" #include "core_functions.h" @@ -47,20 +44,17 @@ #include "storage_base.h" #include "prepared_statement.h" #include "expression_object_type.h" +#include "statement_serializator.h" +#include "table_name_collector.h" +#include "object_from_column_builder.h" namespace sqlite_orm { - namespace conditions { - - template - struct dynamic_order_by_t; - } - namespace internal { /** * Storage class itself. Create an instanse to use it as an interfacto to sqlite db by calling `make_storage` - * function. + * function. */ template struct storage_t : storage_base { @@ -83,1257 +77,92 @@ namespace sqlite_orm { friend struct view_t; template - friend struct conditions::dynamic_order_by_t; - - template - friend struct iterator_t; - - template - std::string serialize_column_schema(const internal::column_t &c) { - std::stringstream ss; - ss << "'" << c.name << "' "; - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - using constraints_type = typename column_type::constraints_type; - ss << type_printer().print() << " "; - { - std::vector constraintsStrings; - constexpr const size_t constraintsCount = std::tuple_size::value; - constraintsStrings.reserve(constraintsCount); - int primaryKeyIndex = -1; - int autoincrementIndex = -1; - int tupleIndex = 0; - iterate_tuple(c.constraints, - [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex](auto &v) { - using constraint_type = typename std::decay::type; - constraintsStrings.push_back(serialize(v)); - if(is_primary_key::value) { - primaryKeyIndex = tupleIndex; - } else if(std::is_same::value) { - 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(c.not_null()) { - ss << "NOT NULL "; - } - return ss.str(); - } - - template - std::string serialize_column_schema(const constraints::primary_key_t &fk) { - std::stringstream ss; - ss << static_cast(fk) << " ("; - std::vector columnNames; - columnNames.reserve(std::tuple_size::value); - iterate_tuple(fk.columns, [&columnNames, this](auto &c) { - columnNames.push_back(this->impl.column_name(c)); - }); - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << columnNames[i]; - if(i < columnNames.size() - 1) { - ss << ", "; - } - } - ss << ") "; - return ss.str(); - } - -#if SQLITE_VERSION_NUMBER >= 3006019 - - template - std::string - serialize_column_schema(const constraints::foreign_key_t, std::tuple> &fk) { - std::stringstream ss; - std::vector columnNames; - using columns_type_t = typename std::decay::type::columns_type; - constexpr const size_t columnsCount = std::tuple_size::value; - columnNames.reserve(columnsCount); - iterate_tuple(fk.columns, [&columnNames, this](auto &v) { - columnNames.push_back(this->impl.column_name(v)); - }); - ss << "FOREIGN KEY( "; - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << columnNames[i]; - if(i < columnNames.size() - 1) { - ss << ","; - } - ss << " "; - } - ss << ") REFERENCES "; - std::vector referencesNames; - using references_type_t = typename std::decay::type::references_type; - constexpr const size_t referencesCount = std::tuple_size::value; - referencesNames.reserve(referencesCount); - { - using first_reference_t = typename std::tuple_element<0, references_type_t>::type; - using first_reference_mapped_type = typename internal::table_type::type; - auto refTableName = this->impl.template find_table_name(); - ss << refTableName << " "; - } - iterate_tuple(fk.references, [&referencesNames, this](auto &v) { - referencesNames.push_back(this->impl.column_name(v)); - }); - ss << "( "; - for(size_t i = 0; i < referencesNames.size(); ++i) { - ss << referencesNames[i]; - if(i < referencesNames.size() - 1) { - ss << ","; - } - ss << " "; - } - ss << ") "; - 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(); - } -#endif - - template - void create_table(sqlite3 *db, const std::string &tableName, I *impl) { - std::stringstream ss; - ss << "CREATE TABLE '" << tableName << "' ( "; - auto columnsCount = impl->table.columns_count; - auto index = 0; - impl->table.for_each_column_with_constraints([columnsCount, &index, &ss, this](auto &c) { - ss << this->serialize_column_schema(c); - if(index < columnsCount - 1) { - ss << ", "; - } - index++; - }); - ss << ") "; - if(impl->table._without_rowid) { - ss << "WITHOUT ROWID "; - } - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - - template - void backup_table(sqlite3 *db, I *impl) { - - // 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 = impl->table.name + "_backup"; - if(impl->table_exists(backupTableName, db)) { - int suffix = 1; - do { - std::stringstream stream; - stream << suffix; - auto anotherBackupTableName = backupTableName + stream.str(); - if(!impl->table_exists(anotherBackupTableName, db)) { - backupTableName = anotherBackupTableName; - break; - } - ++suffix; - } while(true); - } - - this->create_table(db, backupTableName, impl); - - impl->copy_table(db, backupTableName); - - this->drop_table_internal(impl->table.name, db); - - impl->rename_table(db, backupTableName, impl->table.name); - } - - template - void assert_mapped_type() const { - using mapped_types_tuples = std::tuple; - static_assert(tuple_helper::has_type::value, "type is not mapped to a storage"); - } - - template - auto &get_impl() const { - return this->impl.template get_impl(); - } - - template - typename std::enable_if::value, std::string>::type - string_from_expression(const T &, bool /*noTableName*/) const { - return "?"; - } - - template - std::string string_from_expression(std::reference_wrapper ref, bool noTableName) const { - return this->string_from_expression(ref.get(), noTableName); - } - - std::string string_from_expression(std::nullptr_t, bool /*noTableName*/) const { - return "?"; - } - - template - std::string string_from_expression(const alias_holder &, bool /*noTableName*/) const { - return T::get(); - } - - template - std::string string_from_expression(const core_functions::core_function_t &c, - bool noTableName) const { - std::stringstream ss; - ss << static_cast(c) << "("; - std::vector args; - using args_type = typename std::decay::type::args_type; - args.reserve(std::tuple_size::value); - iterate_tuple(c.args, [&args, this, noTableName](auto &v) { - args.push_back(this->string_from_expression(v, noTableName)); - }); - for(size_t i = 0; i < args.size(); ++i) { - ss << args[i]; - if(i < args.size() - 1) { - ss << ", "; - } - } - ss << ")"; - return ss.str(); - } - - template - std::string string_from_expression(const as_t &als, bool noTableName) const { - auto tableAliasString = alias_extractor::get(); - return this->string_from_expression(als.expression, noTableName) + " AS " + tableAliasString; - } - - template - std::string string_from_expression(const alias_column_t &als, bool noTableName) const { - std::stringstream ss; - if(!noTableName) { - ss << "'" << T::get() << "'."; - } - ss << this->string_from_expression(als.column, true); - return ss.str(); - } - - std::string string_from_expression(const std::string &, bool /*noTableName*/) const { - return "?"; - } - - std::string string_from_expression(const char *, bool /*noTableName*/) const { - return "?"; - } - - template - std::string string_from_expression(F O::*m, bool noTableName) const { - std::stringstream ss; - if(!noTableName) { - ss << "'" << this->impl.template find_table_name() << "'."; - } - ss << "\"" << this->impl.column_name(m) << "\""; - return ss.str(); - } - - std::string string_from_expression(const rowid_t &rid, bool /*noTableName*/) const { - return static_cast(rid); - } - - std::string string_from_expression(const oid_t &rid, bool /*noTableName*/) const { - return static_cast(rid); - } - - std::string string_from_expression(const _rowid_t &rid, bool /*noTableName*/) const { - return static_cast(rid); - } - - template - std::string string_from_expression(const table_rowid_t &rid, bool noTableName) const { - std::stringstream ss; - if(!noTableName) { - ss << "'" << this->impl.template find_table_name() << "'."; - } - ss << static_cast(rid); - return ss.str(); - } - - template - std::string string_from_expression(const table_oid_t &rid, bool noTableName) const { - std::stringstream ss; - if(!noTableName) { - ss << "'" << this->impl.template find_table_name() << "'."; - } - ss << static_cast(rid); - return ss.str(); - } - - template - std::string string_from_expression(const table__rowid_t &rid, bool noTableName) const { - std::stringstream ss; - if(!noTableName) { - ss << "'" << this->impl.template find_table_name() << "'."; - } - ss << static_cast(rid); - return ss.str(); - } - - template - std::string string_from_expression(const aggregate_functions::group_concat_double_t &f, - bool noTableName) const { - std::stringstream ss; - auto expr = this->string_from_expression(f.arg, noTableName); - auto expr2 = this->string_from_expression(f.y, noTableName); - ss << static_cast(f) << "(" << expr << ", " << expr2 << ")"; - return ss.str(); - } - - template - std::string string_from_expression(const aggregate_functions::group_concat_single_t &f, - bool noTableName) const { - std::stringstream ss; - auto expr = this->string_from_expression(f.arg, noTableName); - ss << static_cast(f) << "(" << expr << ")"; - return ss.str(); - } - - template - std::string string_from_expression(const binary_operator &f, bool noTableName) const { - std::stringstream ss; - auto lhs = this->string_from_expression(f.lhs, noTableName); - auto rhs = this->string_from_expression(f.rhs, noTableName); - ss << "(" << lhs << " " << static_cast(f) << " " << rhs << ")"; - return ss.str(); - } - - template - std::string string_from_expression(const aggregate_functions::min_t &f, bool noTableName) const { - std::stringstream ss; - auto expr = this->string_from_expression(f.arg, noTableName); - ss << static_cast(f) << "(" << expr << ")"; - return ss.str(); - } - - template - std::string string_from_expression(const aggregate_functions::max_t &f, bool noTableName) const { - std::stringstream ss; - auto expr = this->string_from_expression(f.arg, noTableName); - ss << static_cast(f) << "(" << expr << ")"; - return ss.str(); - } - - template - std::string string_from_expression(const aggregate_functions::total_t &f, bool noTableName) const { - std::stringstream ss; - auto expr = this->string_from_expression(f.arg, noTableName); - ss << static_cast(f) << "(" << expr << ")"; - return ss.str(); - } - - template - std::string string_from_expression(const aggregate_functions::sum_t &f, bool noTableName) const { - std::stringstream ss; - auto expr = this->string_from_expression(f.arg, noTableName); - ss << static_cast(f) << "(" << expr << ")"; - return ss.str(); - } - - template - std::string string_from_expression(const aggregate_functions::count_asterisk_t &, - bool noTableName) const { - return this->string_from_expression(aggregate_functions::count_asterisk_without_type{}, noTableName); - } - - std::string string_from_expression(const aggregate_functions::count_asterisk_without_type &f, - bool /*noTableName*/) const { - std::stringstream ss; - ss << static_cast(f) << "(*)"; - return ss.str(); - } - - template - std::string string_from_expression(const aggregate_functions::count_t &f, bool noTableName) const { - std::stringstream ss; - auto expr = this->string_from_expression(f.arg, noTableName); - ss << static_cast(f) << "(" << expr << ")"; - return ss.str(); - } - - template - std::string string_from_expression(const aggregate_functions::avg_t &a, bool noTableName) const { - std::stringstream ss; - auto expr = this->string_from_expression(a.arg, noTableName); - ss << static_cast(a) << "(" << expr << ") "; - return ss.str(); - } - - template - std::string string_from_expression(const distinct_t &f, bool noTableName) const { - std::stringstream ss; - auto expr = this->string_from_expression(f.t, noTableName); - ss << static_cast(f) << "(" << expr << ") "; - return ss.str(); - } - - template - std::string string_from_expression(const all_t &f, bool noTableName) const { - std::stringstream ss; - auto expr = this->string_from_expression(f.t, noTableName); - ss << static_cast(f) << "(" << expr << ") "; - return ss.str(); - } - - template - std::string string_from_expression(const column_pointer &c, bool noTableName) const { - std::stringstream ss; - if(!noTableName) { - ss << "'" << this->impl.template find_table_name() << "'."; - } - auto &impl = this->get_impl(); - ss << "\"" << impl.column_name_simple(c.field) << "\""; - return ss.str(); - } - - template - std::vector get_column_names(const T &t) const { - auto columnName = this->string_from_expression(t, false); - if(columnName.length()) { - return {columnName}; - } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); - } - } - - template - std::vector get_column_names(std::reference_wrapper r) const { - return this->get_column_names(r.get()); - } - - template - std::vector get_column_names(const internal::asterisk_t &) const { - std::vector res; - res.push_back("*"); - return res; - } - - template - std::vector get_column_names(const internal::columns_t &cols) const { - std::vector columnNames; - columnNames.reserve(static_cast(cols.count)); - iterate_tuple(cols.columns, [&columnNames, this](auto &m) { - auto columnName = this->string_from_expression(m, false); - if(columnName.length()) { - columnNames.push_back(columnName); - } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); - } - }); - return columnNames; - } - - /** - * Takes select_t object and returns SELECT query string - */ - template - std::string string_from_expression(const internal::select_t &sel, bool /*noTableName*/) const { - std::stringstream ss; - if(!is_base_of_template::value) { - if(!sel.highest_level) { - ss << "( "; - } - ss << "SELECT "; - } - if(get_distinct(sel.col)) { - ss << static_cast(distinct(0)) << " "; - } - auto columnNames = this->get_column_names(sel.col); - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << columnNames[i]; - if(i < columnNames.size() - 1) { - ss << ","; - } - ss << " "; - } - auto tableNamesSet = this->parse_table_name(sel.col); - internal::join_iterator()([&tableNamesSet, this](const auto &c) { - using original_join_type = typename std::decay::type::join_type::type; - using cross_join_type = typename internal::mapped_type_proxy::type; - auto crossJoinedTableName = this->impl.template find_table_name(); - auto tableAliasString = alias_extractor::get(); - std::pair tableNameWithAlias(std::move(crossJoinedTableName), - std::move(tableAliasString)); - tableNamesSet.erase(tableNameWithAlias); - }); - if(!tableNamesSet.empty()) { - ss << "FROM "; - std::vector> tableNames(tableNamesSet.begin(), - tableNamesSet.end()); - for(size_t i = 0; i < tableNames.size(); ++i) { - auto &tableNamePair = tableNames[i]; - ss << "'" << tableNamePair.first << "' "; - if(!tableNamePair.second.empty()) { - ss << tableNamePair.second << " "; - } - if(int(i) < int(tableNames.size()) - 1) { - ss << ","; - } - ss << " "; - } - } - iterate_tuple(sel.conditions, [&ss, this](auto &v) { - this->process_single_condition(ss, v); - }); - if(!is_base_of_template::value) { - if(!sel.highest_level) { - ss << ") "; - } - } - return ss.str(); - } - - // Common code for statements returning the whole content of a table: get_all_t, get_all_pointer_t, - // get_all_optional_t. - template - std::stringstream string_from_expression_impl_get_all(bool /*noTableName*/) const { - std::stringstream ss; - ss << "SELECT "; - auto &impl = this->get_impl(); - auto columnNames = impl.table.column_names(); - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "\"" << impl.table.name << "\"." - << "\"" << columnNames[i] << "\""; - if(i < columnNames.size() - 1) { - ss << ", "; - } else { - ss << " "; - } - } - ss << "FROM '" << impl.table.name << "' "; - return ss; - } - - template - std::string string_from_expression(const get_all_t &get, bool noTableName) const { - std::stringstream ss = this->string_from_expression_impl_get_all(noTableName); - this->process_conditions(ss, get.conditions); - return ss.str(); - } - - template - std::string string_from_expression(const get_all_pointer_t &get, bool noTableName) const { - std::stringstream ss = this->string_from_expression_impl_get_all(noTableName); - this->process_conditions(ss, get.conditions); - return ss.str(); - } - -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - std::string string_from_expression(const get_all_optional_t &get, bool noTableName) const { - std::stringstream ss = this->string_from_expression_impl_get_all(noTableName); - this->process_conditions(ss, get.conditions); - return ss.str(); - } -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - - template - std::string string_from_expression(const update_all_t, Wargs...> &upd, - bool /*noTableName*/) const { - std::stringstream ss; - ss << "UPDATE "; - std::set> tableNamesSet; - iterate_tuple(upd.set.assigns, [this, &tableNamesSet](auto &asgn) { - auto tableName = this->parse_table_name(asgn.lhs); - tableNamesSet.insert(tableName.begin(), tableName.end()); - }); - if(!tableNamesSet.empty()) { - if(tableNamesSet.size() == 1) { - ss << " '" << tableNamesSet.begin()->first << "' "; - ss << static_cast(upd.set) << " "; - std::vector setPairs; - iterate_tuple(upd.set.assigns, [this, &setPairs](auto &asgn) { - std::stringstream sss; - sss << this->string_from_expression(asgn.lhs, true); - sss << " " << static_cast(asgn) << " "; - sss << this->string_from_expression(asgn.rhs, false) << " "; - setPairs.push_back(sss.str()); - }); - auto setPairsCount = setPairs.size(); - for(size_t i = 0; i < setPairsCount; ++i) { - ss << setPairs[i] << " "; - if(i < setPairsCount - 1) { - ss << ", "; - } - } - this->process_conditions(ss, upd.conditions); - return ss.str(); - } else { - throw std::system_error(std::make_error_code(orm_error_code::too_many_tables_specified)); - } - } else { - throw std::system_error(std::make_error_code(orm_error_code::incorrect_set_fields_specified)); - } - } - - template - std::string string_from_expression(const remove_all_t &rem, bool /*noTableName*/) const { - auto &impl = this->get_impl(); - std::stringstream ss; - ss << "DELETE FROM '" << impl.table.name << "' "; - this->process_conditions(ss, rem.conditions); - return ss.str(); - } - - // Common code for statements with conditions: get_t, get_pointer_t, get_optional_t. - template - std::string string_from_expression_impl_get(bool /*noTableName*/) const { - auto &impl = this->get_impl(); - std::stringstream ss; - ss << "SELECT "; - auto columnNames = impl.table.column_names(); - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNames.size() - 1) { - ss << ","; - } - ss << " "; - } - ss << "FROM '" << impl.table.name << "' WHERE "; - auto primaryKeyColumnNames = impl.table.primary_key_column_names(); - if(!primaryKeyColumnNames.empty()) { - for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { - ss << "\"" << primaryKeyColumnNames[i] << "\"" - << " = ? "; - if(i < primaryKeyColumnNames.size() - 1) { - ss << "AND "; - } - ss << ' '; - } - return ss.str(); - } else { - throw std::system_error(std::make_error_code(orm_error_code::table_has_no_primary_key_column)); - } - } - - template - std::string string_from_expression(const get_t &, bool noTableName) const { - return this->string_from_expression_impl_get(noTableName); - } - - template - std::string string_from_expression(const get_pointer_t &, bool noTableName) const { - return this->string_from_expression_impl_get(noTableName); - } - -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - std::string string_from_expression(const get_optional_t &, bool noTableName) const { - return this->string_from_expression_impl_get(noTableName); - } -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - - template - std::string string_from_expression(const update_t &upd, bool /*noTableName*/) const { - using expression_type = typename std::decay::type; - using object_type = typename expression_object_type::type; - auto &impl = this->get_impl(); - - std::stringstream ss; - ss << "UPDATE '" << impl.table.name << "' SET "; - std::vector setColumnNames; - impl.table.for_each_column([&setColumnNames](auto &c) { - if(!c.template has>()) { - setColumnNames.emplace_back(c.name); - } - }); - for(size_t i = 0; i < setColumnNames.size(); ++i) { - ss << "\"" << setColumnNames[i] << "\"" - << " = ?"; - if(i < setColumnNames.size() - 1) { - ss << ","; - } - ss << " "; - } - ss << "WHERE "; - auto primaryKeyColumnNames = impl.table.primary_key_column_names(); - for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { - ss << "\"" << primaryKeyColumnNames[i] << "\"" - << " = ?"; - if(i < primaryKeyColumnNames.size() - 1) { - ss << " AND"; - } - ss << " "; - } - return ss.str(); - } - - template - std::string string_from_expression(const remove_t &, bool /*noTableName*/) const { - auto &impl = this->get_impl(); - std::stringstream ss; - ss << "DELETE FROM '" << impl.table.name << "' "; - ss << "WHERE "; - auto primaryKeyColumnNames = impl.table.primary_key_column_names(); - for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { - ss << "\"" << primaryKeyColumnNames[i] << "\"" - << " = ? "; - if(i < primaryKeyColumnNames.size() - 1) { - ss << "AND "; - } - } - return ss.str(); - } - - template - std::string string_from_expression(const insert_explicit &ins, bool /*noTableName*/) const { - constexpr const size_t colsCount = std::tuple_size>::value; - static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); - using expression_type = typename std::decay::type; - using object_type = typename expression_object_type::type; - this->assert_mapped_type(); - auto &impl = this->get_impl(); - std::stringstream ss; - ss << "INSERT INTO '" << impl.table.name << "' "; - std::vector columnNames; - columnNames.reserve(colsCount); - iterate_tuple(ins.columns.columns, [&columnNames, this](auto &m) { - auto columnName = this->string_from_expression(m, true); - if(!columnName.empty()) { - columnNames.push_back(columnName); - } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); - } - }); - ss << "("; - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << columnNames[i]; - if(i < columnNames.size() - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - ss << "VALUES ("; - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "?"; - if(i < columnNames.size() - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - return ss.str(); - } - - template - std::string string_from_expression(const insert_t &ins, bool /*noTableName*/) const { - using expression_type = typename std::decay::type; - using object_type = typename expression_object_type::type; - this->assert_mapped_type(); - auto &impl = this->get_impl(); - std::stringstream ss; - ss << "INSERT INTO '" << impl.table.name << "' "; - std::vector columnNames; - auto compositeKeyColumnNames = impl.table.composite_key_columns_names(); - - impl.table.for_each_column([&impl, &columnNames, &compositeKeyColumnNames](auto &c) { - if(impl.table._without_rowid || !c.template has>()) { - auto it = std::find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); - if(it == compositeKeyColumnNames.end()) { - columnNames.emplace_back(c.name); - } - } - }); - - auto columnNamesCount = columnNames.size(); - if(columnNamesCount) { - ss << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - } else { - ss << "DEFAULT "; - } - ss << "VALUES "; - if(columnNamesCount) { - ss << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "?"; - if(i < columnNamesCount - 1) { - ss << ", "; - } else { - ss << ")"; - } - } - } - return ss.str(); - } - - template - std::string string_from_expression(const replace_t &rep, bool /*noTableName*/) const { - using expression_type = typename std::decay::type; - using object_type = typename expression_object_type::type; - this->assert_mapped_type(); - auto &impl = this->get_impl(); - std::stringstream ss; - ss << "REPLACE INTO '" << impl.table.name << "' ("; - auto columnNames = impl.table.column_names(); - auto columnNamesCount = columnNames.size(); - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - ss << "VALUES("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "?"; - if(i < columnNamesCount - 1) { - ss << ", "; - } else { - ss << ")"; - } - } - return ss.str(); - } - - template - std::string string_from_expression(const replace_range_t &rep, bool /*noTableName*/) const { - using expression_type = typename std::decay::type; - using object_type = typename expression_type::object_type; - auto &impl = this->get_impl(); - std::stringstream ss; - ss << "REPLACE INTO '" << impl.table.name << "' ("; - auto columnNames = impl.table.column_names(); - auto columnNamesCount = columnNames.size(); - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ", "; - } else { - ss << ") "; - } - } - ss << "VALUES "; - auto valuesString = [columnNamesCount] { - std::stringstream ss; - ss << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "?"; - if(i < columnNamesCount - 1) { - ss << ", "; - } else { - ss << ")"; - } - } - return ss.str(); - }(); - auto valuesCount = static_cast(std::distance(rep.range.first, rep.range.second)); - for(auto i = 0; i < valuesCount; ++i) { - ss << valuesString; - if(i < valuesCount - 1) { - ss << ","; - } - ss << " "; - } - return ss.str(); - } - - template - std::string string_from_expression(const insert_range_t &ins, bool /*noTableName*/) const { - using expression_type = typename std::decay::type; - using object_type = typename expression_type::object_type; - auto &impl = this->get_impl(); - - std::stringstream ss; - ss << "INSERT INTO '" << impl.table.name << "' ("; - std::vector columnNames; - impl.table.for_each_column([&columnNames](auto &c) { - if(!c.template has>()) { - columnNames.emplace_back(c.name); - } - }); - - auto columnNamesCount = columnNames.size(); - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - ss << "VALUES "; - auto valuesString = [columnNamesCount] { - std::stringstream ss; - ss << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "?"; - if(i < columnNamesCount - 1) { - ss << ", "; - } else { - ss << ")"; - } - } - return ss.str(); - }(); - auto valuesCount = static_cast(std::distance(ins.range.first, ins.range.second)); - for(auto i = 0; i < valuesCount; ++i) { - ss << valuesString; - if(i < valuesCount - 1) { - ss << ","; - } - ss << " "; - } - return ss.str(); - } - - template - std::string string_from_expression(const conditions::cast_t &c, bool noTableName) const { - std::stringstream ss; - ss << static_cast(c) << " ("; - ss << this->string_from_expression(c.expression, noTableName) << " AS " << type_printer().print() - << ")"; - return ss.str(); - } - - template - typename std::enable_if::value, std::string>::type - string_from_expression(const T &op, bool noTableName) const { - std::stringstream ss; - ss << this->string_from_expression(op.left, noTableName) << " "; - ss << static_cast(op) << " "; - ss << this->string_from_expression(op.right, noTableName); - return ss.str(); - } - - template - std::string string_from_expression(const internal::simple_case_t &c, - bool noTableName) const { - std::stringstream ss; - ss << "CASE "; - c.case_expression.apply([&ss, this, noTableName](auto &c) { - ss << this->string_from_expression(c, noTableName) << " "; - }); - iterate_tuple(c.args, [&ss, this, noTableName](auto &pair) { - ss << "WHEN " << this->string_from_expression(pair.first, noTableName) << " "; - ss << "THEN " << this->string_from_expression(pair.second, noTableName) << " "; - }); - c.else_expression.apply([&ss, this, noTableName](auto &el) { - ss << "ELSE " << this->string_from_expression(el, noTableName) << " "; - }); - ss << "END"; - return ss.str(); - } - - template - std::string string_from_expression(const conditions::is_null_t &c, bool noTableName) const { - std::stringstream ss; - ss << this->string_from_expression(c.t, noTableName) << " " << static_cast(c) << " "; - return ss.str(); - } - - template - std::string string_from_expression(const conditions::is_not_null_t &c, bool noTableName) const { - std::stringstream ss; - ss << this->string_from_expression(c.t, noTableName) << " " << static_cast(c) << " "; - return ss.str(); - } - - template - std::string string_from_expression(const conditions::negated_condition_t &c, bool noTableName) const { - std::stringstream ss; - ss << static_cast(c) << " "; - auto cString = this->string_from_expression(c.c, noTableName); - ss << " (" << cString << " ) "; - return ss.str(); - } - - template - std::string string_from_expression(const conditions::is_equal_t &c, bool noTableName) const { - auto leftString = this->string_from_expression(c.l, noTableName); - auto rightString = this->string_from_expression(c.r, noTableName); - std::stringstream ss; - ss << leftString << " " << static_cast(c) << " " << rightString; - return ss.str(); - } - - template - typename std::enable_if::value, std::string>::type - string_from_expression(const C &c, bool noTableName) const { - auto leftString = this->string_from_expression(c.l, noTableName); - auto rightString = this->string_from_expression(c.r, noTableName); - std::stringstream ss; - ss << "(" << leftString << " " << static_cast(c) << " " << rightString << ")"; - return ss.str(); - } - - template - std::string string_from_expression(const conditions::named_collate &col, bool noTableName) const { - auto res = this->string_from_expression(col.expr, noTableName); - return res + " " + static_cast(col); - } - - template - std::string string_from_expression(const conditions::collate_t &col, bool noTableName) const { - auto res = this->string_from_expression(col.expr, noTableName); - return res + " " + static_cast(col); - } - - template - std::string string_from_expression(const conditions::in_t &inCondition, bool noTableName) const { - std::stringstream ss; - auto leftString = this->string_from_expression(inCondition.l, noTableName); - ss << leftString << " " << static_cast(inCondition) << " "; - ss << this->string_from_expression(inCondition.arg, noTableName); - return ss.str(); - } - - template - std::string string_from_expression(const conditions::in_t> &inCondition, - bool noTableName) const { - std::stringstream ss; - auto leftString = this->string_from_expression(inCondition.l, noTableName); - ss << leftString << " " << static_cast(inCondition) << " ( "; - for(size_t index = 0; index < inCondition.arg.size(); ++index) { - auto &value = inCondition.arg[index]; - ss << " " << this->string_from_expression(value, noTableName); - if(index < inCondition.arg.size() - 1) { - ss << ", "; - } - } - ss << " )"; - return ss.str(); - } - - template - std::string string_from_expression(const conditions::like_t &l, bool noTableName) const { - std::stringstream ss; - ss << this->string_from_expression(l.arg, noTableName) << " "; - ss << static_cast(l) << " "; - ss << this->string_from_expression(l.pattern, noTableName); - l.arg3.apply([&ss, this, noTableName](auto &value) { - ss << " ESCAPE " << this->string_from_expression(value, noTableName); - }); - return ss.str(); - } - - template - std::string string_from_expression(const conditions::glob_t &l, bool noTableName) const { - std::stringstream ss; - ss << this->string_from_expression(l.arg, noTableName) << " "; - ss << static_cast(l) << " "; - ss << this->string_from_expression(l.pattern, noTableName); - return ss.str(); - } - - template - std::string string_from_expression(const conditions::between_t &bw, bool noTableName) const { - std::stringstream ss; - auto expr = this->string_from_expression(bw.expr, noTableName); - ss << expr << " " << static_cast(bw) << " "; - ss << this->string_from_expression(bw.b1, noTableName); - ss << " AND "; - ss << this->string_from_expression(bw.b2, noTableName); - return ss.str(); - } - - template - std::string string_from_expression(const conditions::exists_t &e, bool noTableName) const { - std::stringstream ss; - ss << static_cast(e) << " "; - ss << this->string_from_expression(e.t, noTableName); - return ss.str(); - } - - template - std::string process_order_by(const conditions::order_by_t &orderBy) const { - std::stringstream ss; - auto columnName = this->string_from_expression(orderBy.o, false); - ss << columnName << " "; - if(orderBy._collate_argument.length()) { - ss << "COLLATE " << orderBy._collate_argument << " "; - } - switch(orderBy.asc_desc) { - case 1: - ss << "ASC"; - break; - case -1: - ss << "DESC"; - break; - } - return ss.str(); - } + friend struct dynamic_order_by_t; - template - void process_join_constraint(std::stringstream &ss, const conditions::on_t &t) const { - ss << static_cast(t) << " " << this->string_from_expression(t.arg, false); - } + template + friend struct iterator_t; - template - void process_join_constraint(std::stringstream &ss, const conditions::using_t &u) const { - ss << static_cast(u) << " (" << this->string_from_expression(u.column, true) << " )"; - } + template + friend struct serializator_context_builder; - /** - * HO - has offset - * OI - offset is implicit - */ - template - void process_single_condition(std::stringstream &ss, const conditions::limit_t &limt) const { - ss << static_cast(limt) << " "; - if(HO) { - if(OI) { - limt.off.apply([this, &ss](auto &value) { - ss << this->string_from_expression(value, false); - }); + template + void create_table(sqlite3 *db, const std::string &tableName, const I &tableImpl) { + std::stringstream ss; + ss << "CREATE TABLE '" << tableName << "' ( "; + auto columnsCount = tableImpl.table.columns_count; + auto index = 0; + using context_t = serializator_context; + context_t context{this->impl}; + iterate_tuple(tableImpl.table.columns, [columnsCount, &index, &ss, &context](auto &c) { + ss << serialize(c, context); + if(index < columnsCount - 1) { ss << ", "; - ss << this->string_from_expression(limt.lim, false); + } + index++; + }); + ss << ") "; + if(tableImpl.table._without_rowid) { + ss << "WITHOUT ROWID "; + } + auto query = ss.str(); + sqlite3_stmt *stmt; + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + statement_finalizer finalizer{stmt}; + if(sqlite3_step(stmt) == SQLITE_DONE) { + // done.. } else { - ss << this->string_from_expression(limt.lim, false) << " OFFSET "; - limt.off.apply([this, &ss](auto &value) { - ss << this->string_from_expression(value, false); - }); + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); } } else { - ss << this->string_from_expression(limt.lim, false); + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); } } - template - void process_single_condition(std::stringstream &ss, const conditions::cross_join_t &c) const { - ss << static_cast(c) << " "; - ss << " '" << this->impl.template find_table_name() << "'"; - } + template + void backup_table(sqlite3 *db, const I &tableImpl, const std::vector &columnsToIgnore) { - template - void process_single_condition(std::stringstream &ss, const conditions::natural_join_t &c) const { - ss << static_cast(c) << " "; - ss << " '" << this->impl.template find_table_name() << "'"; - } - - template - void process_single_condition(std::stringstream &ss, const conditions::inner_join_t &l) const { - ss << static_cast(l) << " "; - auto aliasString = alias_extractor::get(); - ss << " '" << this->impl.template find_table_name::type>() << "' "; - if(aliasString.length()) { - ss << "'" << aliasString << "' "; + // 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 = tableImpl.table.name + "_backup"; + if(tableImpl.table_exists(backupTableName, db)) { + int suffix = 1; + do { + std::stringstream stream; + stream << suffix; + auto anotherBackupTableName = backupTableName + stream.str(); + if(!tableImpl.table_exists(anotherBackupTableName, db)) { + backupTableName = anotherBackupTableName; + break; + } + ++suffix; + } while(true); } - this->process_join_constraint(ss, l.constraint); - } - template - void process_single_condition(std::stringstream &ss, const conditions::left_outer_join_t &l) const { - ss << static_cast(l) << " "; - ss << " '" << this->impl.template find_table_name() << "' "; - this->process_join_constraint(ss, l.constraint); - } + this->create_table(db, backupTableName, tableImpl); - template - void process_single_condition(std::stringstream &ss, const conditions::left_join_t &l) const { - ss << static_cast(l) << " "; - ss << " '" << this->impl.template find_table_name() << "' "; - this->process_join_constraint(ss, l.constraint); - } + tableImpl.copy_table(db, backupTableName, columnsToIgnore); - template - void process_single_condition(std::stringstream &ss, const conditions::join_t &l) const { - ss << static_cast(l) << " "; - ss << " '" << this->impl.template find_table_name() << "' "; - this->process_join_constraint(ss, l.constraint); - } + this->drop_table_internal(tableImpl.table.name, db); - template - void process_single_condition(std::stringstream &ss, const conditions::where_t &w) const { - ss << static_cast(w) << " "; - auto whereString = this->string_from_expression(w.c, false); - ss << "( " << whereString << ") "; + tableImpl.rename_table(db, backupTableName, tableImpl.table.name); } template - void process_single_condition(std::stringstream &ss, const conditions::order_by_t &orderBy) const { - ss << static_cast(orderBy) << " "; - auto orderByString = this->process_order_by(orderBy); - ss << orderByString << " "; - } - - template - void process_single_condition(std::stringstream &ss, - const conditions::multi_order_by_t &orderBy) const { - std::vector expressions; - iterate_tuple(orderBy.args, [&expressions, this](auto &v) { - auto expression = this->process_order_by(v); - expressions.push_back(std::move(expression)); - }); - ss << static_cast(orderBy) << " "; - for(size_t i = 0; i < expressions.size(); ++i) { - ss << expressions[i]; - if(i < expressions.size() - 1) { - ss << ", "; - } - } - ss << " "; - } - - template - void process_single_condition(std::stringstream &ss, - const conditions::dynamic_order_by_t &orderBy) const { - ss << this->storage_base::process_order_by(orderBy) << " "; - } - - template - void process_single_condition(std::stringstream &ss, const conditions::group_by_t &groupBy) const { - std::vector expressions; - iterate_tuple(groupBy.args, [&expressions, this](auto &v) { - auto expression = this->string_from_expression(v, false); - expressions.push_back(expression); - }); - ss << static_cast(groupBy) << " "; - for(size_t i = 0; i < expressions.size(); ++i) { - ss << expressions[i]; - if(i < expressions.size() - 1) { - ss << ", "; - } - } - ss << " "; + void assert_mapped_type() const { + using mapped_types_tuples = std::tuple; + static_assert(tuple_helper::has_type::value, "type is not mapped to a storage"); } - template - void process_single_condition(std::stringstream &ss, const conditions::having_t &hav) const { - ss << static_cast(hav) << " "; - ss << this->string_from_expression(hav.t, false) << " "; + template + auto &get_impl() const { + return this->impl.template get_impl(); } - template - void process_conditions(std::stringstream &ss, const std::tuple &args) const { - iterate_tuple(args, [this, &ss](auto &v) { - this->process_single_condition(ss, v); - }); + template + auto &get_impl() { + return this->impl.template get_impl(); } public: @@ -1345,6 +174,13 @@ namespace sqlite_orm { 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(); @@ -1384,293 +220,6 @@ namespace sqlite_orm { } protected: - template - std::set> parse_table_name(const T &) const { - return {}; - } - - template - std::set> parse_table_name(F O::*, std::string alias = {}) const { - return {std::make_pair(this->impl.template find_table_name(), std::move(alias))}; - } - - template - std::set> - parse_table_name(const aggregate_functions::min_t &f) const { - return this->parse_table_name(f.arg); - } - - template - std::set> - parse_table_name(const aggregate_functions::max_t &f) const { - return this->parse_table_name(f.arg); - } - - template - std::set> - parse_table_name(const aggregate_functions::sum_t &f) const { - return this->parse_table_name(f.arg); - } - - template - std::set> - parse_table_name(const aggregate_functions::total_t &f) const { - return this->parse_table_name(f.arg); - } - - template - std::set> - parse_table_name(const aggregate_functions::group_concat_double_t &f) const { - auto res = this->parse_table_name(f.arg); - auto secondSet = this->parse_table_name(f.y); - res.insert(secondSet.begin(), secondSet.end()); - return res; - } - - template - std::set> - parse_table_name(const aggregate_functions::group_concat_single_t &f) const { - return this->parse_table_name(f.arg); - } - - template - std::set> - parse_table_name(const aggregate_functions::count_t &f) const { - return this->parse_table_name(f.arg); - } - - template - std::set> - parse_table_name(const aggregate_functions::avg_t &a) const { - return this->parse_table_name(a.arg); - } - - template - std::set> - parse_table_name(const core_functions::core_function_t &f) const { - std::set> res; - iterate_tuple(f.args, [&res, this](auto &v) { - auto tableNames = this->parse_table_name(v); - res.insert(tableNames.begin(), tableNames.end()); - }); - return res; - } - - template - std::set> parse_table_name(const distinct_t &f) const { - return this->parse_table_name(f.t); - } - - template - std::set> parse_table_name(const all_t &f) const { - return this->parse_table_name(f.t); - } - - template - std::set> - parse_table_name(const binary_operator &f) const { - std::set> res; - auto leftSet = this->parse_table_name(f.lhs); - res.insert(leftSet.begin(), leftSet.end()); - auto rightSet = this->parse_table_name(f.rhs); - res.insert(rightSet.begin(), rightSet.end()); - return res; - } - - template - std::set> parse_table_name(const column_pointer &) const { - std::set> res; - res.insert({this->impl.template find_table_name(), ""}); - return res; - } - - template - std::set> parse_table_name(const alias_column_t &a) const { - return this->parse_table_name(a.column, alias_extractor::get()); - } - - template - std::set> - parse_table_name(const aggregate_functions::count_asterisk_t &) const { - auto tableName = this->impl.template find_table_name(); - if(!tableName.empty()) { - return {std::make_pair(std::move(tableName), "")}; - } else { - return {}; - } - } - - std::set> - parse_table_name(const aggregate_functions::count_asterisk_without_type &) const { - return {}; - } - - template - std::set> parse_table_name(const asterisk_t &) const { - auto tableName = this->impl.template find_table_name(); - return {std::make_pair(std::move(tableName), "")}; - } - - template - std::set> parse_table_name(const conditions::cast_t &c) const { - return this->parse_table_name(c.expression); - } - - template - std::set> - parse_table_name(const simple_case_t &c) const { - std::set> res; - c.case_expression.apply([this, &res](auto &c) { - auto caseExpressionSet = this->parse_table_name(c); - res.insert(caseExpressionSet.begin(), caseExpressionSet.end()); - }); - iterate_tuple(c.args, [this, &res](auto &pair) { - auto leftSet = this->parse_table_name(pair.first); - res.insert(leftSet.begin(), leftSet.end()); - auto rightSet = this->parse_table_name(pair.second); - res.insert(rightSet.begin(), rightSet.end()); - }); - c.else_expression.apply([this, &res](auto &el) { - auto tableNames = this->parse_table_name(el); - res.insert(tableNames.begin(), tableNames.end()); - }); - return res; - } - - template - std::set> - parse_table_name(const conditions::and_condition_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; - } - - template - std::set> - parse_table_name(const conditions::or_condition_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; - } - - template - std::set> - parse_table_name(const conditions::is_equal_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; - } - - template - std::set> - parse_table_name(const conditions::is_not_equal_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; - } - - template - std::set> - parse_table_name(const conditions::greater_than_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; - } - - template - std::set> - parse_table_name(const conditions::greater_or_equal_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; - } - - template - std::set> - parse_table_name(const conditions::lesser_than_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; - } - - template - std::set> - parse_table_name(const conditions::lesser_or_equal_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; - } - - template - std::set> parse_table_name(const conditions::like_t &l) const { - std::set> res; - auto argTableNames = this->parse_table_name(l.arg); - res.insert(argTableNames.begin(), argTableNames.end()); - auto patternTableNames = this->parse_table_name(l.pattern); - res.insert(patternTableNames.begin(), patternTableNames.end()); - l.arg3.apply([&res, this](auto &value) { - auto escapeTableNames = this->parse_table_name(value); - res.insert(escapeTableNames.begin(), escapeTableNames.end()); - }); - return res; - } - - template - std::set> parse_table_name(const conditions::glob_t &l) const { - std::set> res; - auto argTableNames = this->parse_table_name(l.arg); - res.insert(argTableNames.begin(), argTableNames.end()); - auto patternTableNames = this->parse_table_name(l.pattern); - res.insert(patternTableNames.begin(), patternTableNames.end()); - return res; - } - - template - std::set> - parse_table_name(const conditions::negated_condition_t &c) const { - return this->parse_table_name(c.c); - } - - template - std::set> parse_table_name(const as_t &a) const { - return this->parse_table_name(a.expression); - } - - template - std::set> - parse_table_name(const internal::columns_t &cols) const { - std::set> res; - iterate_tuple(cols.columns, [&res, this](auto &m) { - auto tableName = this->parse_table_name(m); - res.insert(tableName.begin(), tableName.end()); - }); - return res; - } - template std::string group_concat_internal(F O::*m, std::unique_ptr y, Args &&... args) { this->assert_mapped_type(); @@ -1689,29 +238,65 @@ namespace sqlite_orm { public: /** - * Select * with no conditions routine. + * 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. + * @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, class... Args> - C get_all(Args &&... args) { + 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 * with no conditions routine. + * SELECT * routine. * O is an object type to be extracted. Must be specified explicitly. - * @return All objects of type O as std::unique_ptr stored in database at the moment. + * 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>, class... Args> - C get_all_pointer(Args &&... args) { + 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, orm_error_category) if object not found with given @@ -1941,14 +526,49 @@ namespace sqlite_orm { return this->execute(statement); } + template + typename std::enable_if::value, std::string>::type + dump(const T &preparedStatement) const { + using context_t = serializator_context; + context_t context{this->impl}; + return serialize(preparedStatement.t, context); + } + /** * Returns a string representation of object of a class mapped to the storage. * Type of string has json-like style. */ template - std::string dump(const O &o) { - this->assert_mapped_type(); - return this->impl.dump(o); + typename std::enable_if::value, std::string>::type + dump(const O &o) { + auto &tImpl = this->get_impl(); + std::stringstream ss; + ss << "{ "; + using pair = std::pair; + std::vector pairs; + tImpl.table.for_each_column([&pairs, &o](auto &c) { + using column_type = typename std::decay::type; + using field_type = typename column_type::field_type; + pair p{c.name, std::string()}; + if(c.member_pointer) { + p.second = field_printer()(o.*c.member_pointer); + } else { + using getter_type = typename column_type::getter_type; + field_value_holder valueHolder{((o).*(c.getter))()}; + p.second = field_printer()(valueHolder.value); + } + pairs.push_back(move(p)); + }); + for(size_t i = 0; i < pairs.size(); ++i) { + auto &p = pairs[i]; + ss << p.first << " : '" << p.second << "'"; + if(i < pairs.size() - 1) { + ss << ", "; + } else { + ss << " }"; + } + } + return ss.str(); } /** @@ -2009,33 +629,37 @@ namespace sqlite_orm { 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 &tImpl = this->get_impl(); + tImpl.table.name = 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 &tImpl = this->get_impl(); + return tImpl.table.name; + } + protected: template - sync_schema_result sync_table(storage_impl, Tss...> *impl, sqlite3 *db, bool) { + sync_schema_result sync_table(const storage_impl, Tss...> &tableImpl, sqlite3 *db, bool) { auto res = sync_schema_result::already_in_sync; - std::stringstream ss; - ss << "CREATE "; - if(impl->table.unique) { - ss << "UNIQUE "; - } - using columns_type = typename decltype(impl->table)::columns_type; - using head_t = typename std::tuple_element<0, columns_type>::type; - using indexed_type = typename internal::table_type::type; - ss << "INDEX IF NOT EXISTS '" << impl->table.name << "' ON '" - << this->impl.template find_table_name() << "' ( "; - std::vector columnNames; - iterate_tuple(impl->table.columns, [&columnNames, this](auto &v) { - columnNames.push_back(this->impl.column_name(v)); - }); - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "'" << columnNames[i] << "'"; - if(i < columnNames.size() - 1) { - ss << ","; - } - ss << " "; - } - ss << ") "; - auto query = ss.str(); + using context_t = serializator_context; + context_t context{this->impl}; + auto query = serialize(tableImpl.table, context); auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); if(rc != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), @@ -2045,13 +669,14 @@ namespace sqlite_orm { } template - sync_schema_result sync_table(storage_impl, Tss...> *impl, sqlite3 *db, bool preserve) { + sync_schema_result + sync_table(const storage_impl, Tss...> &tImpl, sqlite3 *db, bool preserve) { auto res = sync_schema_result::already_in_sync; - auto schema_stat = impl->schema_status(db, preserve); + auto schema_stat = tImpl.schema_status(db, preserve); if(schema_stat != decltype(schema_stat)::already_in_sync) { if(schema_stat == decltype(schema_stat)::new_table_created) { - this->create_table(db, impl->table.name, impl); + this->create_table(db, tImpl.table.name, tImpl); res = decltype(res)::new_table_created; } else { if(schema_stat == sync_schema_result::old_columns_removed || @@ -2059,26 +684,26 @@ namespace sqlite_orm { schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { // get table info provided in `make_table` call.. - auto storageTableInfo = impl->table.get_table_info(); + auto storageTableInfo = tImpl.table.get_table_info(); // now get current table info from db using `PRAGMA table_info` query.. - auto dbTableInfo = impl->get_table_info(impl->table.name, db); + auto dbTableInfo = tImpl.get_table_info(tImpl.table.name, db); // this vector will contain pointers to columns that gotta be added.. std::vector columnsToAdd; - impl->get_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + tImpl.get_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); if(schema_stat == sync_schema_result::old_columns_removed) { // extra table columns than storage columns - this->backup_table(db, impl); + this->backup_table(db, tImpl, {}); res = decltype(res)::old_columns_removed; } if(schema_stat == sync_schema_result::new_columns_added) { for(auto columnPointer: columnsToAdd) { - impl->add_column(*columnPointer, db); + tImpl.add_column(*columnPointer, db); } res = decltype(res)::new_columns_added; } @@ -2086,15 +711,12 @@ namespace sqlite_orm { if(schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { // remove extra columns - this->backup_table(db, impl); - for(auto columnPointer: columnsToAdd) { - impl->add_column(*columnPointer, db); - } + this->backup_table(db, tImpl, columnsToAdd); res = decltype(res)::new_columns_added_and_old_columns_removed; } } else if(schema_stat == sync_schema_result::dropped_and_recreated) { - this->drop_table_internal(impl->table.name, db); - this->create_table(db, impl->table.name, impl); + this->drop_table_internal(tImpl.table.name, db); + this->create_table(db, tImpl.table.name, tImpl); res = decltype(res)::dropped_and_recreated; } } @@ -2134,9 +756,9 @@ namespace sqlite_orm { auto con = this->get_connection(); std::map result; auto db = con.get(); - this->impl.for_each([&result, db, preserve, this](auto impl) { - auto res = this->sync_table(impl, db, preserve); - result.insert({impl->table.name, res}); + this->impl.for_each([&result, db, preserve, this](auto &tableImpl) { + auto res = this->sync_table(tableImpl, db, preserve); + result.insert({tableImpl.table.name, res}); }); return result; } @@ -2150,8 +772,8 @@ namespace sqlite_orm { auto con = this->get_connection(); std::map result; auto db = con.get(); - this->impl.for_each([&result, db, preserve](auto impl) { - result.insert({impl->table.name, impl->schema_status(db, preserve)}); + this->impl.for_each([&result, db, preserve](auto tableImpl) { + result.insert({tableImpl.table.name, tableImpl.schema_status(db, preserve)}); }); return result; } @@ -2172,7 +794,11 @@ namespace sqlite_orm { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(sel, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(sel, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(sel), stmt, con}; } else { @@ -2182,13 +808,17 @@ namespace sqlite_orm { } template - prepared_statement_t> prepare(get_all_t get) { + prepared_statement_t> prepare(get_all_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(get, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get), stmt, con}; + return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -2196,13 +826,17 @@ namespace sqlite_orm { } template - prepared_statement_t> prepare(get_all_pointer_t get) { + prepared_statement_t> prepare(get_all_pointer_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(get, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get), stmt, con}; + return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -2210,14 +844,18 @@ namespace sqlite_orm { } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - prepared_statement_t> prepare(get_all_optional_t get) { + template + prepared_statement_t> prepare(get_all_optional_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(get, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get), stmt, con}; + return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -2231,7 +869,11 @@ namespace sqlite_orm { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(upd, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(upd, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(upd), stmt, con}; } else { @@ -2245,7 +887,11 @@ namespace sqlite_orm { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(rem, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(rem, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(rem), stmt, std::move(con)}; } else { @@ -2255,13 +901,17 @@ namespace sqlite_orm { } template - prepared_statement_t> prepare(get_t g) { + prepared_statement_t> prepare(get_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(g, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(g), stmt, con}; + return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -2269,13 +919,17 @@ namespace sqlite_orm { } template - prepared_statement_t> prepare(get_pointer_t g) { + prepared_statement_t> prepare(get_pointer_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(g, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(g), stmt, con}; + return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -2284,13 +938,17 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - prepared_statement_t> prepare(get_optional_t g) { + prepared_statement_t> prepare(get_optional_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(g, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(g), stmt, con}; + return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -2303,7 +961,11 @@ namespace sqlite_orm { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(upd, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(upd, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(upd), stmt, con}; } else { @@ -2317,7 +979,11 @@ namespace sqlite_orm { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(rem, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(rem, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(rem), stmt, con}; } else { @@ -2328,10 +994,16 @@ namespace sqlite_orm { template prepared_statement_t> prepare(insert_t ins) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(ins, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(ins, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(ins), stmt, con}; } else { @@ -2344,8 +1016,14 @@ namespace sqlite_orm { prepared_statement_t> prepare(replace_t rep) { auto con = this->get_connection(); sqlite3_stmt *stmt; + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); auto db = con.get(); - auto query = this->string_from_expression(rep, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(rep, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(rep), stmt, con}; } else { @@ -2355,13 +1033,17 @@ namespace sqlite_orm { } template - prepared_statement_t> prepare(insert_range_t ins) { + prepared_statement_t> prepare(insert_range_t statement) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(ins, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(statement, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(ins), stmt, con}; + return {std::move(statement), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -2373,7 +1055,11 @@ namespace sqlite_orm { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(rep, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(rep, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(rep), stmt, con}; } else { @@ -2384,10 +1070,16 @@ namespace sqlite_orm { template prepared_statement_t> prepare(insert_explicit ins) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(ins, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(ins, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(ins), stmt, con}; } else { @@ -2405,13 +1097,13 @@ namespace sqlite_orm { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); auto &o = statement.t.obj; sqlite3_reset(stmt); - iterate_tuple(statement.t.columns.columns, [&o, &index, &stmt, &impl, db](auto &m) { + iterate_tuple(statement.t.columns.columns, [&o, &index, &stmt, &tImpl, db](auto &m) { using column_type = typename std::decay::type; using field_type = typename column_result_t::type; - const field_type *value = impl.table.template get_object_field_pointer(o, m); + const field_type *value = tImpl.table.template get_object_field_pointer(o, m); if(SQLITE_OK != statement_binder().bind(stmt, index++, *value)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -2430,7 +1122,7 @@ namespace sqlite_orm { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; using object_type = typename expression_type::object_type; - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); auto index = 1; auto con = this->get_connection(); auto db = con.get(); @@ -2438,7 +1130,7 @@ namespace sqlite_orm { sqlite3_reset(stmt); for(auto it = statement.t.range.first; it != statement.t.range.second; ++it) { auto &o = *it; - impl.table.for_each_column([&o, &index, &stmt, db](auto &c) { + tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; if(c.member_pointer) { @@ -2475,11 +1167,11 @@ namespace sqlite_orm { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); sqlite3_reset(stmt); for(auto it = statement.t.range.first; it != statement.t.range.second; ++it) { auto &o = *it; - impl.table.for_each_column([&o, &index, &stmt, db](auto &c) { + tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { if(!c.template has>()) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; @@ -2520,9 +1212,9 @@ namespace sqlite_orm { auto stmt = statement.stmt; auto index = 1; auto &o = get_object(statement.t); - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); sqlite3_reset(stmt); - impl.table.for_each_column([&o, &index, &stmt, db](auto &c) { + tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; if(c.member_pointer) { @@ -2557,12 +1249,12 @@ namespace sqlite_orm { auto db = con.get(); auto stmt = statement.stmt; auto index = 1; - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); auto &o = get_object(statement.t); - auto compositeKeyColumnNames = impl.table.composite_key_columns_names(); + auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); sqlite3_reset(stmt); - impl.table.for_each_column([&o, &index, &stmt, &impl, &compositeKeyColumnNames, db](auto &c) { - if(impl.table._without_rowid || !c.template has>()) { + tImpl.table.for_each_column([&o, &index, &stmt, &tImpl, &compositeKeyColumnNames, db](auto &c) { + if(tImpl.table._without_rowid || !c.template has>()) { auto it = std::find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); if(it == compositeKeyColumnNames.end()) { using column_type = typename std::decay::type; @@ -2624,12 +1316,12 @@ namespace sqlite_orm { using object_type = typename expression_object_type::type; auto con = this->get_connection(); auto db = con.get(); - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); auto stmt = statement.stmt; auto index = 1; auto &o = get_object(statement.t); sqlite3_reset(stmt); - impl.table.for_each_column([&o, stmt, &index, db](auto &c) { + tImpl.table.for_each_column([&o, stmt, &index, db](auto &c) { if(!c.template has>()) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; @@ -2651,7 +1343,7 @@ namespace sqlite_orm { } } }); - impl.table.for_each_column([&o, stmt, &index, db](auto &c) { + tImpl.table.for_each_column([&o, stmt, &index, db](auto &c) { if(c.template has>()) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; @@ -2682,7 +1374,7 @@ namespace sqlite_orm { template std::unique_ptr execute(const prepared_statement_t> &statement) { - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; @@ -2699,16 +1391,8 @@ namespace sqlite_orm { switch(stepRes) { case SQLITE_ROW: { auto res = std::make_unique(); - index = 0; - impl.table.for_each_column([&index, &res, stmt](auto &c) { - using field_type = typename std::decay::type::field_type; - auto value = row_extractor().extract(stmt, index++); - if(c.member_pointer) { - (*res).*c.member_pointer = std::move(value); - } else { - ((*res).*(c.setter))(std::move(value)); - } - }); + object_from_column_builder builder{*res, stmt}; + tImpl.table.for_each_column(builder); return res; } break; case SQLITE_DONE: { @@ -2724,7 +1408,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template std::optional execute(const prepared_statement_t> &statement) { - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; @@ -2741,16 +1425,8 @@ namespace sqlite_orm { switch(stepRes) { case SQLITE_ROW: { auto res = std::make_optional(); - index = 0; - impl.table.for_each_column([&index, &res, stmt](auto &c) { - using field_type = typename std::decay::type::field_type; - auto value = row_extractor().extract(stmt, index++); - if(c.member_pointer) { - (*res).*c.member_pointer = std::move(value); - } else { - ((*res).*(c.setter))(std::move(value)); - } - }); + object_from_column_builder builder{res.value(), stmt}; + tImpl.table.for_each_column(builder); return res; } break; case SQLITE_DONE: { @@ -2766,7 +1442,7 @@ namespace sqlite_orm { template T execute(const prepared_statement_t> &statement) { - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; @@ -2783,17 +1459,8 @@ namespace sqlite_orm { switch(stepRes) { case SQLITE_ROW: { T res; - index = 0; - impl.table.for_each_column([&index, &res, stmt](auto &c) { - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - auto value = row_extractor().extract(stmt, index++); - if(c.member_pointer) { - res.*c.member_pointer = std::move(value); - } else { - ((res).*(c.setter))(std::move(value)); - } - }); + object_from_column_builder builder{res, stmt}; + tImpl.table.for_each_column(builder); return res; } break; case SQLITE_DONE: { @@ -2878,12 +1545,18 @@ namespace sqlite_orm { } }); std::vector res; + auto tableInfoPointer = this->impl.template find_table(); int stepRes; do { stepRes = sqlite3_step(stmt); switch(stepRes) { case SQLITE_ROW: { - res.push_back(row_extractor().extract(stmt, 0)); + using table_info_pointer_t = typename std::remove_pointer::type; + using table_info_t = typename std::decay::type; + row_extractor_builder::value, table_info_t> + builder; + auto rowExtractor = builder(tableInfoPointer); + res.push_back(rowExtractor.extract(stmt, 0)); } break; case SQLITE_DONE: break; @@ -2896,9 +1569,9 @@ namespace sqlite_orm { return res; } - template - std::vector execute(const prepared_statement_t> &statement) { - auto &impl = this->get_impl(); + template + R execute(const prepared_statement_t> &statement) { + auto &tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; @@ -2912,23 +1585,15 @@ namespace sqlite_orm { sqlite3_errmsg(db)); } }); - std::vector res; + R res; int stepRes; do { stepRes = sqlite3_step(stmt); switch(stepRes) { case SQLITE_ROW: { T obj; - auto index = 0; - impl.table.for_each_column([&index, &obj, stmt](auto &c) { - using field_type = typename std::decay::type::field_type; - auto value = row_extractor().extract(stmt, index++); - if(c.member_pointer) { - obj.*c.member_pointer = std::move(value); - } else { - ((obj).*(c.setter))(std::move(value)); - } - }); + object_from_column_builder builder{obj, stmt}; + tImpl.table.for_each_column(builder); res.push_back(std::move(obj)); } break; case SQLITE_DONE: @@ -2942,10 +1607,9 @@ namespace sqlite_orm { return res; } - template - std::vector> - execute(const prepared_statement_t> &statement) { - auto &impl = this->get_impl(); + template + R execute(const prepared_statement_t> &statement) { + auto &tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; @@ -2959,24 +1623,16 @@ namespace sqlite_orm { sqlite3_errmsg(db)); } }); - std::vector> res; + R res; int stepRes; do { stepRes = sqlite3_step(stmt); switch(stepRes) { case SQLITE_ROW: { auto obj = std::make_unique(); - auto index = 0; - impl.table.for_each_column([&index, &obj, stmt](auto &c) { - using field_type = typename std::decay::type::field_type; - auto value = row_extractor().extract(stmt, index++); - if(c.member_pointer) { - (*obj).*c.member_pointer = std::move(value); - } else { - ((*obj).*(c.setter))(std::move(value)); - } - }); - res.push_back(std::move(obj)); + object_from_column_builder builder{*obj, stmt}; + tImpl.table.for_each_column(builder); + res.push_back(move(obj)); } break; case SQLITE_DONE: break; @@ -2990,10 +1646,9 @@ namespace sqlite_orm { } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - std::vector> - execute(const prepared_statement_t> &statement) { - auto &impl = this->get_impl(); + template + R execute(const prepared_statement_t> &statement) { + auto &tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; @@ -3007,24 +1662,16 @@ namespace sqlite_orm { sqlite3_errmsg(db)); } }); - std::vector> res; + R res; int stepRes; do { stepRes = sqlite3_step(stmt); switch(stepRes) { case SQLITE_ROW: { auto obj = std::make_optional(); - auto index = 0; - impl.table.for_each_column([&index, &obj, stmt](auto &c) { - using field_type = typename std::decay::type::field_type; - auto value = row_extractor().extract(stmt, index++); - if(c.member_pointer) { - (*obj).*c.member_pointer = std::move(value); - } else { - ((*obj).*(c.setter))(std::move(value)); - } - }); - res.push_back(std::move(obj)); + object_from_column_builder builder{*obj, stmt}; + tImpl.table.for_each_column(builder); + res.push_back(move(obj)); } break; case SQLITE_DONE: break; diff --git a/dev/storage_base.h b/dev/storage_base.h index 6fcacab58..21dee1d52 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -88,6 +88,16 @@ namespace sqlite_orm { this->drop_table_internal(tableName, con.get()); } + /** + * Rename table named `from` to `to`. + */ + void rename_table(const std::string &from, const std::string &to) { + auto con = this->get_connection(); + std::stringstream ss; + ss << "ALTER TABLE '" << from << "' RENAME TO '" << to << "'"; + this->perform_query_without_result(ss.str(), con.get()); + } + /** * sqlite3_changes function. */ @@ -121,15 +131,13 @@ namespace sqlite_orm { return sqlite3_libversion(); } - bool transaction(std::function f) { + bool transaction(const std::function &f) { this->begin_transaction(); - auto con = this->get_connection(); - auto db = con.get(); auto shouldCommit = f(); if(shouldCommit) { - this->commit(db); + this->commit(); } else { - this->rollback(db); + this->rollback(); } return shouldCommit; } @@ -167,10 +175,10 @@ namespace sqlite_orm { db, sql.c_str(), [](void *data, int argc, char **argv, char * * /*columnName*/) -> int { - auto &tableNames = *(data_t *)data; + auto &tableNames_ = *(data_t *)data; for(int i = 0; i < argc; i++) { if(argv[i]) { - tableNames.push_back(argv[i]); + tableNames_.push_back(argv[i]); } } return 0; @@ -264,7 +272,8 @@ namespace sqlite_orm { backup_t make_backup_to(const std::string &filename) { auto holder = std::make_unique(filename); - return {connection_ref{*holder}, "main", this->get_connection(), "main", move(holder)}; + connection_ref conRef{*holder}; + return {conRef, "main", this->get_connection(), "main", move(holder)}; } backup_t make_backup_to(storage_base &other) { @@ -273,7 +282,8 @@ namespace sqlite_orm { backup_t make_backup_from(const std::string &filename) { auto holder = std::make_unique(filename); - return {this->get_connection(), "main", connection_ref{*holder}, "main", move(holder)}; + connection_ref conRef{*holder}; + return {this->get_connection(), "main", conRef, "main", move(holder)}; } backup_t make_backup_from(storage_base &other) { @@ -284,6 +294,27 @@ namespace sqlite_orm { 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; + } + + int busy_handler(std::function handler) { + _busy_handler = 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(const std::string &filename_, int foreignKeysCount) : pragma(std::bind(&storage_base::get_connection, this)), @@ -321,6 +352,7 @@ namespace sqlite_orm { std::unique_ptr connection; std::map collatingFunctions; const int cachedForeignKeysCount; + std::function _busy_handler; connection_ref get_connection() { connection_ref res{*this->connection}; @@ -345,7 +377,7 @@ namespace sqlite_orm { bool foreign_keys(sqlite3 *db) { std::string query = "PRAGMA foreign_keys"; - auto res = false; + auto result = false; auto rc = sqlite3_exec( db, query.c_str(), @@ -356,13 +388,13 @@ namespace sqlite_orm { } return 0; }, - &res, + &result, nullptr); if(rc != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } - return res; + return result; } #endif @@ -393,6 +425,10 @@ namespace sqlite_orm { sqlite3_limit(db, p.first, p.second); } + if(_busy_handler) { + sqlite3_busy_handler(this->connection->get(), busy_handler_callback, this); + } + if(this->on_open) { this->on_open(db); } @@ -456,7 +492,7 @@ namespace sqlite_orm { } std::string current_timestamp(sqlite3 *db) { - std::string res; + std::string result; std::stringstream ss; ss << "SELECT CURRENT_TIMESTAMP"; auto query = ss.str(); @@ -472,19 +508,22 @@ namespace sqlite_orm { } return 0; }, - &res, + &result, nullptr); if(rc != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } - return res; + return result; } void drop_table_internal(const std::string &tableName, sqlite3 *db) { std::stringstream ss; ss << "DROP TABLE '" << tableName + "'"; - auto query = ss.str(); + this->perform_query_without_result(ss.str(), db); + } + + void perform_query_without_result(const std::string &query, sqlite3 *db) { sqlite3_stmt *stmt; if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { statement_finalizer finalizer{stmt}; @@ -500,52 +539,26 @@ namespace sqlite_orm { } } - template - std::string process_order_by(const conditions::dynamic_order_by_t &orderBy) const { - std::vector expressions; - for(auto &entry: orderBy) { - std::string entryString; - { - std::stringstream 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; - } - entryString = ss.str(); - } - expressions.push_back(move(entryString)); - }; - std::stringstream ss; - ss << static_cast(orderBy) << " "; - for(size_t i = 0; i < expressions.size(); ++i) { - ss << expressions[i]; - if(i < expressions.size() - 1) { - ss << ", "; - } - } - ss << " "; - return 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; + } + } + // returns foreign keys count in storage definition template static int foreign_keys_count(T &storageImpl) { auto res = 0; - storageImpl.for_each([&res](auto impl) { - res += impl->foreign_keys_count(); + storageImpl.for_each([&res](auto &impl) { + res += impl.foreign_keys_count(); }); return res; } diff --git a/dev/storage_impl.h b/dev/storage_impl.h index 0be48cfe5..37aca6e25 100644 --- a/dev/storage_impl.h +++ b/dev/storage_impl.h @@ -10,6 +10,7 @@ #include // std::pair, std::make_pair #include // std::vector #include // std::find_if +#include // std::type_index #include "error_code.h" #include "statement_finalizer.h" @@ -19,7 +20,6 @@ #include "field_printer.h" #include "table_info.h" #include "sync_schema_result.h" -#include "sqlite_type.h" #include "field_value_holder.h" namespace sqlite_orm { @@ -28,8 +28,8 @@ namespace sqlite_orm { struct storage_impl_base { - bool table_exists(const std::string &tableName, sqlite3 *db) { - auto res = false; + bool table_exists(const std::string &tableName, sqlite3 *db) const { + auto result = false; std::stringstream ss; ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = '" << "table" @@ -45,16 +45,16 @@ namespace sqlite_orm { } return 0; }, - &res, + &result, nullptr); if(rc != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } - return res; + return result; } - void rename_table(sqlite3 *db, const std::string &oldName, const std::string &newName) { + void rename_table(sqlite3 *db, const std::string &oldName, const std::string &newName) const { std::stringstream ss; ss << "ALTER TABLE " << oldName << " RENAME TO " << newName; auto query = ss.str(); @@ -92,29 +92,19 @@ namespace sqlite_orm { }); if(dbColumnInfoIt != dbTableInfo.end()) { auto &dbColumnInfo = *dbColumnInfoIt; - auto dbColumnInfoType = to_sqlite_type(dbColumnInfo.type); - auto storageColumnInfoType = to_sqlite_type(storageColumnInfo.type); - if(dbColumnInfoType && storageColumnInfoType) { - auto columnsAreEqual = - dbColumnInfo.name == storageColumnInfo.name && - *dbColumnInfoType == *storageColumnInfoType && - dbColumnInfo.notnull == storageColumnInfo.notnull && - (dbColumnInfo.dflt_value.length() > 0) == (storageColumnInfo.dflt_value.length() > 0) && - dbColumnInfo.pk == storageColumnInfo.pk; - if(!columnsAreEqual) { - notEqual = true; - break; - } - dbTableInfo.erase(dbColumnInfoIt); - storageTableInfo.erase(storageTableInfo.begin() + - static_cast(storageColumnInfoIndex)); - --storageColumnInfoIndex; - } else { - - // undefined type/types + auto columnsAreEqual = + dbColumnInfo.name == storageColumnInfo.name && + dbColumnInfo.notnull == storageColumnInfo.notnull && + (dbColumnInfo.dflt_value.length() > 0) == (storageColumnInfo.dflt_value.length() > 0) && + dbColumnInfo.pk == storageColumnInfo.pk; + if(!columnsAreEqual) { notEqual = true; break; } + dbTableInfo.erase(dbColumnInfoIt); + storageTableInfo.erase(storageTableInfo.begin() + + static_cast(storageColumnInfoIndex)); + --storageColumnInfoIndex; } else { columnsToAdd.push_back(&storageColumnInfo); } @@ -122,8 +112,8 @@ namespace sqlite_orm { return notEqual; } - std::vector get_table_info(const std::string &tableName, sqlite3 *db) { - std::vector res; + std::vector get_table_info(const std::string &tableName, sqlite3 *db) const { + std::vector result; auto query = "PRAGMA table_info('" + tableName + "')"; auto rc = sqlite3_exec( db, @@ -143,13 +133,13 @@ namespace sqlite_orm { } return 0; }, - &res, + &result, nullptr); if(rc != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } - return res; + return result; } }; @@ -171,7 +161,7 @@ namespace sqlite_orm { template void for_each(const L &l) { this->super::for_each(l); - l(this); + l(*this); } #if SQLITE_VERSION_NUMBER >= 3006019 @@ -181,7 +171,7 @@ namespace sqlite_orm { */ int foreign_keys_count() { auto res = 0; - this->table.for_each_column_with_constraints([&res](auto &c) { + iterate_tuple(this->table.columns, [&res](auto &c) { if(internal::is_foreign_key::type>::value) { ++res; } @@ -201,22 +191,6 @@ namespace sqlite_orm { return this->table.find_column_name(m); } - /** - * Same thing as above for getter. - */ - template::value>::type> - std::string column_name_simple(T g) const { - return this->table.find_column_name(g); - } - - /** - * Same thing as above for setter. - */ - template::value>::type> - std::string column_name_simple(T s) const { - return this->table.find_column_name(s); - } - /** * Cute function used to find column name by its type and member pointer. Uses SFINAE to * skip inequal type O. @@ -236,44 +210,6 @@ namespace sqlite_orm { return this->super::column_name(m); } - /** - * Cute function used to find column name by its type and getter pointer. Uses SFINAE to - * skip inequal type O. - */ - template - std::string column_name(const F &(O::*g)() const, - typename std::enable_if::value>::type * = nullptr) const { - return this->table.find_column_name(g); - } - - /** - * Opposite version of function defined above. Just calls same function in superclass. - */ - template - std::string column_name(const F &(O::*g)() const, - typename std::enable_if::value>::type * = nullptr) const { - return this->super::column_name(g); - } - - /** - * Cute function used to find column name by its type and setter pointer. Uses SFINAE to - * skip inequal type O. - */ - template - std::string column_name(void (O::*s)(F), - typename std::enable_if::value>::type * = nullptr) const { - return this->table.find_column_name(s); - } - - /** - * Opposite version of function defined above. Just calls same function in superclass. - */ - template - std::string column_name(void (O::*s)(F), - typename std::enable_if::value>::type * = nullptr) const { - return this->super::column_name(s); - } - template std::string column_name(const column_pointer &c, typename std::enable_if::value>::type * = nullptr) const { @@ -287,62 +223,45 @@ namespace sqlite_orm { } template - auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { + const auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { return *this; } template - auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { + const auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { return this->super::template get_impl(); } template - std::string find_table_name(typename std::enable_if::value>::type * = nullptr) const { - return this->table.name; + auto &get_impl(typename std::enable_if::value>::type * = nullptr) { + return *this; } template - std::string find_table_name(typename std::enable_if::value>::type * = nullptr) const { - return this->super::template find_table_name(); + auto &get_impl(typename std::enable_if::value>::type * = nullptr) { + return this->super::template get_impl(); } template - std::string dump(const O &o, typename std::enable_if::value>::type * = nullptr) { - return this->super::dump(o, nullptr); + const auto *find_table(typename std::enable_if::value>::type * = nullptr) const { + return &this->table; } template - std::string dump(const O &o, typename std::enable_if::value>::type * = nullptr) { - std::stringstream ss; - ss << "{ "; - using pair = std::pair; - std::vector pairs; - this->table.for_each_column([&pairs, &o](auto &c) { - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - pair p{c.name, ""}; - if(c.member_pointer) { - p.second = field_printer()(o.*c.member_pointer); - } else { - using getter_type = typename column_type::getter_type; - field_value_holder valueHolder{((o).*(c.getter))()}; - p.second = field_printer()(valueHolder.value); - } - pairs.push_back(std::move(p)); - }); - for(size_t i = 0; i < pairs.size(); ++i) { - auto &p = pairs[i]; - ss << p.first << " : '" << p.second << "'"; - if(i < pairs.size() - 1) { - ss << ", "; - } else { - ss << " }"; - } + const auto *find_table(typename std::enable_if::value>::type * = nullptr) const { + return this->super::template find_table(); + } + + std::string find_table_name(std::type_index ti) const { + std::type_index thisTypeIndex{typeid(typename H::object_type)}; + if(thisTypeIndex == ti) { + return this->table.name; + } else { + return this->super::find_table_name(ti); } - return ss.str(); } - void add_column(const table_info &ti, sqlite3 *db) { + void add_column(const table_info &ti, sqlite3 *db) const { std::stringstream ss; ss << "ALTER TABLE " << this->table.name << " ADD COLUMN " << ti.name << " "; ss << ti.type << " "; @@ -376,11 +295,22 @@ namespace sqlite_orm { * Copies current table to another table with a given **name**. * Performs CREATE TABLE %name% AS SELECT %this->table.columns_names()% FROM &this->table.name%; */ - void copy_table(sqlite3 *db, const std::string &name) { + void + copy_table(sqlite3 *db, const std::string &name, const std::vector &columnsToIgnore) const { + std::ignore = columnsToIgnore; + std::stringstream ss; std::vector columnNames; - this->table.for_each_column([&columnNames](auto &c) { - columnNames.emplace_back(c.name); + this->table.for_each_column([&columnNames, &columnsToIgnore](auto &c) { + auto &columnName = c.name; + auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), + columnsToIgnore.end(), + [&columnName](auto tableInfoPointer) { + return columnName == tableInfoPointer->name; + }); + if(columnToIgnoreIt == columnsToIgnore.end()) { + columnNames.emplace_back(columnName); + } }); auto columnNamesCount = columnNames.size(); ss << "INSERT INTO " << name << " ("; @@ -417,7 +347,7 @@ namespace sqlite_orm { } } - sync_schema_result schema_status(sqlite3 *db, bool preserve) { + sync_schema_result schema_status(sqlite3 *db, bool preserve) const { auto res = sync_schema_result::already_in_sync; @@ -488,8 +418,7 @@ namespace sqlite_orm { template<> struct storage_impl<> : storage_impl_base { - template - std::string find_table_name() const { + std::string find_table_name(std::type_index) const { return {}; } @@ -501,8 +430,8 @@ namespace sqlite_orm { } template - std::string dump(const O &, sqlite3 *, std::nullptr_t) { - throw std::system_error(std::make_error_code(orm_error_code::type_is_not_mapped_to_storage)); + const void *find_table() const { + return nullptr; } }; diff --git a/dev/table.h b/dev/table.h index 05e0124ac..151238127 100644 --- a/dev/table.h +++ b/dev/table.h @@ -24,7 +24,7 @@ namespace sqlite_orm { /** * Table name. */ - const std::string name; + std::string name; bool _without_rowid = false; }; @@ -64,22 +64,22 @@ namespace sqlite_orm { // Make static_if have at least one input as a workaround for GCC bug: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095 if(!res) { - static_if{}>([&res, &obj, &col](const C &c) { - if(compare_any(col.member_pointer, c)) { + static_if{}>([&res, &obj, &col](const C &c_) { + if(compare_any(col.member_pointer, c_)) { res = &(obj.*col.member_pointer); } })(c); } if(!res) { - static_if{}>([&res, &obj, &col](const C &c) { - if(compare_any(col.getter, c)) { + static_if{}>([&res, &obj, &col](const C &c_) { + if(compare_any(col.getter, c_)) { res = &((obj).*(col.getter))(); } })(c); } if(!res) { - static_if{}>([&res, &obj, &col](const C &c) { - if(compare_any(col.setter, c)) { + static_if{}>([&res, &obj, &col](const C &c_) { + if(compare_any(col.setter, c_)) { res = &((obj).*(col.getter))(); } })(c); @@ -141,7 +141,7 @@ namespace sqlite_orm { } /** - * Searches column name by class member pointer passed as first argument. + * Searches column name by class member pointer passed as the first argument. * @return column name or empty string if nothing found. */ template void {} */ @@ -207,16 +207,12 @@ namespace sqlite_orm { }); } - template - void for_each_column_with_constraints(const L &l) const { - iterate_tuple(this->columns, l); - } - template void for_each_column_with_field_type(const L &l) const { iterate_tuple(this->columns, [&l](auto &column) { using column_type = typename std::decay::type; - static_if{}>(l)(column); + using field_type = typename column_field_type::type; + static_if{}>(l)(column); }); } @@ -231,11 +227,12 @@ namespace sqlite_orm { using tuple_helper::tuple_contains_type; iterate_tuple(this->columns, [&l](auto &column) { using column_type = typename std::decay::type; - static_if{}>(l)(column); + using constraints_type = typename column_constraints_type::type; + static_if{}>(l)(column); }); } - std::vector get_table_info() { + std::vector get_table_info() const { std::vector res; res.reserve(size_t(this->columns_count)); this->for_each_column([&res](auto &col) { diff --git a/dev/table_info.h b/dev/table_info.h index 2b362d1b4..8beccaea1 100644 --- a/dev/table_info.h +++ b/dev/table_info.h @@ -5,12 +5,12 @@ namespace sqlite_orm { struct table_info { - int cid; + int cid = 0; std::string name; std::string type; - bool notnull; + bool notnull = false; std::string dflt_value; - int pk; + int pk = 0; }; } diff --git a/dev/table_name_collector.h b/dev/table_name_collector.h new file mode 100644 index 000000000..20cf1a857 --- /dev/null +++ b/dev/table_name_collector.h @@ -0,0 +1,76 @@ +#pragma once + +#include // std::set +#include // std::string +#include // std::function +#include // std::type_index + +#include "select_constraints.h" +#include "alias.h" +#include "core_functions.h" + +namespace sqlite_orm { + + namespace internal { + + struct table_name_collector { + using table_name_set = std::set>; + using find_table_name_t = std::function; + + find_table_name_t find_table_name; + mutable table_name_set table_names; + + template + table_name_set operator()(const T &) const { + return {}; + } + + template + void operator()(F O::*, std::string alias = {}) const { + if(this->find_table_name) { + table_names.insert(std::make_pair(this->find_table_name(typeid(O)), move(alias))); + } + } + + template + void operator()(const column_pointer &) const { + if(this->find_table_name) { + table_names.insert({this->find_table_name(typeid(T)), ""}); + } + } + + template + void operator()(const alias_column_t &a) const { + (*this)(a.column, alias_extractor::get()); + } + + template + void operator()(const count_asterisk_t &) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + if(!tableName.empty()) { + table_names.insert(std::make_pair(move(tableName), "")); + } + } + } + + template + void operator()(const asterisk_t &) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + table_names.insert(std::make_pair(move(tableName), "")); + } + } + + template + void operator()(const object_t &) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + table_names.insert(std::make_pair(move(tableName), "")); + } + } + }; + + } + +} diff --git a/dev/tuple_helper.h b/dev/tuple_helper.h index eb67655cf..b1535521d 100644 --- a/dev/tuple_helper.h +++ b/dev/tuple_helper.h @@ -61,8 +61,8 @@ namespace sqlite_orm { template void move_tuple_impl(L &lhs, R &rhs) { std::get(lhs) = std::move(std::get(rhs)); - internal::static_if{}>([](auto &lhs, auto &rhs) { - move_tuple_impl(lhs, rhs); + internal::static_if{}>([](auto &l, auto &r) { + move_tuple_impl(l, r); })(lhs, rhs); } } @@ -72,8 +72,8 @@ namespace sqlite_orm { template void move_tuple(L &lhs, R &rhs) { using bool_type = std::integral_constant; - static_if([](auto &lhs, auto &rhs) { - tuple_helper::move_tuple_impl(lhs, rhs); + static_if([](auto &l, auto &r) { + tuple_helper::move_tuple_impl(l, r); })(lhs, rhs); } diff --git a/dev/values.h b/dev/values.h new file mode 100644 index 000000000..ed608738f --- /dev/null +++ b/dev/values.h @@ -0,0 +1,33 @@ +#pragma once + +#include // std::vector +#include +#include // std::tuple + +namespace sqlite_orm { + + namespace internal { + + template + struct values_t { + std::tuple tuple; + }; + + template + struct dynamic_values_t { + std::vector vector; + }; + + } + + template + internal::values_t values(Args... args) { + return {{std::forward(args)...}}; + } + + template + internal::dynamic_values_t values(std::vector vector) { + return {{move(vector)}}; + } + +} diff --git a/dev/view.h b/dev/view.h index afd868e79..5abddd4e5 100644 --- a/dev/view.h +++ b/dev/view.h @@ -27,7 +27,7 @@ namespace sqlite_orm { storage_type &storage; connection_ref connection; - get_all_t args; + 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_)...)} {} @@ -47,7 +47,11 @@ namespace sqlite_orm { iterator_t begin() { sqlite3_stmt *stmt = nullptr; auto db = this->connection.get(); - auto query = this->storage.string_from_expression(this->args, false); + using context_t = serializator_context; + context_t context{this->storage.impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(this->args, context); auto ret = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); if(ret == SQLITE_OK) { auto index = 1; diff --git a/examples/check.cpp b/examples/check.cpp new file mode 100644 index 000000000..865a6f16a --- /dev/null +++ b/examples/check.cpp @@ -0,0 +1,62 @@ +#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; + }; + + struct Product { + int id = 0; + std::string name; + float listPrice = 0; + float discount = 0; + }; + + 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/examples/collate.cpp b/examples/collate.cpp index 5491307df..e925f6f28 100644 --- a/examples/collate.cpp +++ b/examples/collate.cpp @@ -33,9 +33,9 @@ int main(int, char **) { storage.remove_all(); storage.remove_all(); - storage.insert(User{0, "Lil Kim", time(nullptr)}); - storage.insert(User{0, "lil kim", time(nullptr)}); - storage.insert(User{0, "Nicki Minaj", time(nullptr)}); + 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 diff --git a/examples/core_functions.cpp b/examples/core_functions.cpp index 121040cf9..0029fbe9d 100644 --- a/examples/core_functions.cpp +++ b/examples/core_functions.cpp @@ -13,6 +13,13 @@ struct MarvelHero { short points; }; +struct Contact { + int id = 0; + std::string firstName; + std::string lastName; + std::string phone; +}; + int main(int, char **argv) { cout << "path = " << argv[0] << endl; @@ -22,7 +29,12 @@ int main(int, char **argv) { make_column("id", &MarvelHero::id, primary_key()), make_column("name", &MarvelHero::name), make_column("abilities", &MarvelHero::abilities), - make_column("points", &MarvelHero::points))); + 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))); storage.sync_schema(); storage.remove_all(); @@ -42,6 +54,11 @@ int main(int, char **argv) { 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 @@ -139,11 +156,12 @@ int main(int, char **argv) { #if SQLITE_VERSION_NUMBER >= 3007016 - // SELECT CHAR(67,72,65,82) + // 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 + // 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) { @@ -257,5 +275,69 @@ int main(int, char **argv) { // 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 + auto rows = storage.select(columns(&MarvelHero::name, as(instr(&MarvelHero::abilities, "o"))), + where(greater_than(get(), 0))); + 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 return 0; } diff --git a/examples/date_time.cpp b/examples/date_time.cpp index 2fa68b9ee..63ca31c46 100644 --- a/examples/date_time.cpp +++ b/examples/date_time.cpp @@ -69,8 +69,29 @@ int main() { 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); + // 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/examples/fetch_content/CMakeLists.txt b/examples/fetch_content/CMakeLists.txt new file mode 100644 index 000000000..17419748a --- /dev/null +++ b/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/examples/fetch_content/dependencies/CMakeLists.txt b/examples/fetch_content/dependencies/CMakeLists.txt new file mode 100644 index 000000000..da0ecd397 --- /dev/null +++ b/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/examples/fetch_content/dependencies/sqlite_orm/CMakeLists.txt b/examples/fetch_content/dependencies/sqlite_orm/CMakeLists.txt new file mode 100644 index 000000000..705383adb --- /dev/null +++ b/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/examples/fetch_content/src/CMakeLists.txt b/examples/fetch_content/src/CMakeLists.txt new file mode 100644 index 000000000..95a90f48f --- /dev/null +++ b/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/examples/fetch_content/src/main.cpp b/examples/fetch_content/src/main.cpp new file mode 100644 index 000000000..e39a410d3 --- /dev/null +++ b/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/examples/find_package/CMakeLists.txt b/examples/find_package/CMakeLists.txt new file mode 100644 index 000000000..6c6e0182b --- /dev/null +++ b/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/examples/find_package/src/CMakeLists.txt b/examples/find_package/src/CMakeLists.txt new file mode 100644 index 000000000..95a90f48f --- /dev/null +++ b/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/examples/find_package/src/main.cpp b/examples/find_package/src/main.cpp new file mode 100644 index 000000000..e39a410d3 --- /dev/null +++ b/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/examples/in_memory.cpp b/examples/in_memory.cpp index 95c7a9ad8..f3f534aff 100644 --- a/examples/in_memory.cpp +++ b/examples/in_memory.cpp @@ -1,4 +1,3 @@ - #include #include diff --git a/examples/index.cpp b/examples/index.cpp index 71b541768..fd45ec6a1 100644 --- a/examples/index.cpp +++ b/examples/index.cpp @@ -17,13 +17,14 @@ 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), - make_unique_index("idx_contacts_email", &Contract::email), - make_table("contacts", - make_column("first_name", &Contract::firstName), - make_column("last_name", &Contract::lastName), - make_column("email", &Contract::email))); +auto storage = + make_storage("index.sqlite", + make_index("idx_contacts_name", &Contract::firstName, &Contract::lastName), + 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 **) { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index d908f3d98..2567a34f8 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -42,6 +42,8 @@ __pragma(push_macro("min")) incorrect_journal_mode_string, invalid_collate_argument_enum, failed_to_init_a_backup, + unknown_member_value, + incorrect_order, }; } @@ -77,6 +79,10 @@ namespace sqlite_orm { 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"; default: return "unknown error"; } @@ -130,91 +136,6 @@ namespace std { } #pragma once -#include // std::map -#include // std::string -#include // std::regex, std::regex_match -#include // std::make_unique, std::unique_ptr -#include // std::vector -#include // std::toupper - -namespace sqlite_orm { - using int64 = sqlite_int64; - using uint64 = sqlite_uint64; - - // numeric and real are the same for c++ - enum class sqlite_type { - INTEGER, - TEXT, - BLOB, - REAL, - }; - - /** - * @param str case doesn't matter - it is uppercased before comparing. - */ - inline std::unique_ptr to_sqlite_type(const std::string &str) { - auto asciiStringToUpper = [](std::string &s) { - std::transform(s.begin(), s.end(), s.begin(), [](char c) { - return std::toupper(c); - }); - }; - auto upperStr = str; - asciiStringToUpper(upperStr); - - static std::map> typeMap = { - {sqlite_type::INTEGER, - { - std::regex("INT"), - std::regex("INT.*"), - std::regex("TINYINT"), - std::regex("SMALLINT"), - std::regex("MEDIUMINT"), - std::regex("BIGINT"), - std::regex("UNSIGNED BIG INT"), - std::regex("INT2"), - std::regex("INT8"), - }}, - {sqlite_type::TEXT, - { - std::regex("CHARACTER\\([[:digit:]]+\\)"), - std::regex("VARCHAR\\([[:digit:]]+\\)"), - std::regex("VARYING CHARACTER\\([[:digit:]]+\\)"), - std::regex("NCHAR\\([[:digit:]]+\\)"), - std::regex("NATIVE CHARACTER\\([[:digit:]]+\\)"), - std::regex("NVARCHAR\\([[:digit:]]+\\)"), - std::regex("CLOB"), - std::regex("TEXT"), - }}, - {sqlite_type::BLOB, - { - std::regex("BLOB"), - }}, - {sqlite_type::REAL, - { - std::regex("REAL"), - std::regex("DOUBLE"), - std::regex("DOUBLE PRECISION"), - std::regex("FLOAT"), - std::regex("NUMERIC"), - std::regex("DECIMAL\\([[:digit:]]+,[[:digit:]]+\\)"), - std::regex("BOOLEAN"), - std::regex("DATE"), - std::regex("DATETIME"), - }}, - }; - for(auto &p: typeMap) { - for(auto &r: p.second) { - if(std::regex_match(upperStr, r)) { - return std::make_unique(p.first); - } - } - } - - return {}; - } -} -#pragma once - #include // std::tuple, std::get #include // std::false_type, std::true_type @@ -228,25 +149,33 @@ namespace sqlite_orm { // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co namespace internal { + static inline decltype(auto) empty_callable() { + static auto res = [](auto &&...) {}; + return (res); + } + template - auto static_if(std::true_type, T t, F) { - return std::move(t); + decltype(auto) static_if(std::true_type, const T &t, const F &) { + return (t); } template - auto static_if(std::false_type, T, F f) { - return std::move(f); + decltype(auto) static_if(std::false_type, const T &, const F &f) { + return (f); } template - auto static_if(T t, F f) { - return static_if(std::integral_constant{}, std::move(t), std::move(f)); + decltype(auto) static_if(const T &t, const F &f) { + return static_if(std::integral_constant{}, t, f); } template - auto static_if(T t) { - return static_if(std::integral_constant{}, t, [](auto &&...) {}); + decltype(auto) static_if(const T &t) { + return static_if(std::integral_constant{}, t, empty_callable()); } + + template + using static_not = std::integral_constant; } } @@ -307,8 +236,8 @@ namespace sqlite_orm { template void move_tuple_impl(L &lhs, R &rhs) { std::get(lhs) = std::move(std::get(rhs)); - internal::static_if{}>([](auto &lhs, auto &rhs) { - move_tuple_impl(lhs, rhs); + internal::static_if{}>([](auto &l, auto &r) { + move_tuple_impl(l, r); })(lhs, rhs); } } @@ -318,8 +247,8 @@ namespace sqlite_orm { template void move_tuple(L &lhs, R &rhs) { using bool_type = std::integral_constant; - static_if([](auto &lhs, auto &rhs) { - tuple_helper::move_tuple_impl(lhs, rhs); + static_if([](auto &l, auto &r) { + tuple_helper::move_tuple_impl(l, r); })(lhs, rhs); } @@ -531,13 +460,11 @@ namespace sqlite_orm { template struct primary_key_t : primary_key_base { using order_by = primary_key_base::order_by; + using columns_tuple = std::tuple; - std::tuple columns; - - primary_key_t(decltype(columns) c) : columns(std::move(c)) {} + columns_tuple columns; - using field_type = void; // for column iteration. Better be deleted - using constraints_type = std::tuple<>; + primary_key_t(decltype(columns) c) : columns(move(c)) {} primary_key_t asc() const { auto res = *this; @@ -552,14 +479,22 @@ namespace sqlite_orm { } }; + struct unique_base { + operator std::string() const { + return "UNIQUE"; + } + }; + /** * UNIQUE constraint class. */ - struct unique_t { + template + struct unique_t : unique_base { + using columns_tuple = std::tuple; - operator std::string() const { - return "UNIQUE"; - } + columns_tuple columns; + + unique_t(columns_tuple columns_) : columns(move(columns_)) {} }; /** @@ -642,8 +577,8 @@ namespace sqlite_orm { 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_) {} + 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; @@ -733,9 +668,6 @@ namespace sqlite_orm { return *this; } - using field_type = void; // for column iteration. Better be deleted - using constraints_type = std::tuple<>; - template void for_each_column(const L &) {} @@ -759,8 +691,8 @@ namespace sqlite_orm { foreign_key_intermediate_t(tuple_type columns_) : columns(std::move(columns_)) {} template - foreign_key_t, std::tuple> references(Rs... references) { - return {std::move(this->columns), std::make_tuple(std::forward(references)...)}; + foreign_key_t, std::tuple> references(Rs... refs) { + return {std::move(this->columns), std::make_tuple(std::forward(refs)...)}; } }; #endif @@ -788,6 +720,21 @@ namespace sqlite_orm { } }; + struct check_string { + operator std::string() const { + return "CHECK"; + } + }; + + template + struct check_t : check_string { + using expression_type = T; + + expression_type expression; + + check_t(expression_type expression_) : expression(std::move(expression_)) {} + }; + template struct is_constraint : std::false_type {}; @@ -797,8 +744,8 @@ namespace sqlite_orm { template struct is_constraint> : std::true_type {}; - template<> - struct is_constraint : std::true_type {}; + template + struct is_constraint> : std::true_type {}; template struct is_constraint> : std::true_type {}; @@ -809,6 +756,9 @@ namespace sqlite_orm { template<> struct is_constraint : std::true_type {}; + template + struct is_constraint> : std::true_type {}; + template struct constraints_size; @@ -838,8 +788,13 @@ namespace sqlite_orm { /** * UNIQUE constraint builder function. */ - inline constraints::unique_t unique() { - return {}; + template + constraints::unique_t unique(Args... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + inline constraints::unique_t<> unique() { + return {{}}; } inline constraints::autoincrement_t autoincrement() { @@ -847,9 +802,12 @@ namespace sqlite_orm { } template - inline constraints::primary_key_t primary_key(Cs... cs) { - using ret_type = constraints::primary_key_t; - return ret_type(std::make_tuple(cs...)); + constraints::primary_key_t primary_key(Cs... cs) { + return {std::make_tuple(std::forward(cs)...)}; + } + + inline constraints::primary_key_t<> primary_key() { + return {{}}; } template @@ -869,6 +827,11 @@ namespace sqlite_orm { return {internal::collate_argument::rtrim}; } + template + constraints::check_t check(T t) { + return {std::move(t)}; + } + namespace internal { /** @@ -962,6 +925,55 @@ namespace sqlite_orm { // #include "constraints.h" +// #include "serializator_context.h" + +namespace sqlite_orm { + + namespace internal { + + struct serializator_context_base { + bool replace_bindable_with_question = false; + bool skip_table_name = true; + bool use_parentheses = true; + + template + std::string column_name(F O::*) const { + return {}; + } + }; + + template + struct serializator_context : serializator_context_base { + using impl_type = I; + + const impl_type &impl; + + serializator_context(const impl_type &impl_) : impl(impl_) {} + + template + std::string column_name(F O::*m) const { + return this->impl.column_name(m); + } + }; + + template + struct serializator_context_builder { + using storage_type = S; + using impl_type = typename storage_type::impl_type; + + serializator_context_builder(const storage_type &storage_) : storage(storage_) {} + + serializator_context operator()() const { + return {this->storage.impl}; + } + + const storage_type &storage; + }; + + } + +} + namespace sqlite_orm { namespace internal { @@ -982,7 +994,8 @@ namespace sqlite_orm { template std::unique_ptr operator()(const constraints::default_t &t) { - return std::make_unique(serialize(t.value)); + serializator_context_base context; + return std::make_unique(serialize(t.value, context)); } }; @@ -993,6 +1006,14 @@ namespace sqlite_orm { #include // std::false_type, std::true_type +// #include "negatable.h" + +namespace sqlite_orm { + namespace internal { + struct negatable_t {}; + } +} + namespace sqlite_orm { namespace internal { @@ -1035,7 +1056,7 @@ namespace sqlite_orm { * Result of addition + operator */ template - using add_t = binary_operator; + using add_t = binary_operator; struct sub_string { operator std::string() const { @@ -1047,7 +1068,7 @@ namespace sqlite_orm { * Result of substitute - operator */ template - using sub_t = binary_operator; + using sub_t = binary_operator; struct mul_string { operator std::string() const { @@ -1059,7 +1080,7 @@ namespace sqlite_orm { * Result of multiply * operator */ template - using mul_t = binary_operator; + using mul_t = binary_operator; struct div_string { operator std::string() const { @@ -1071,7 +1092,7 @@ namespace sqlite_orm { * Result of divide / operator */ template - using div_t = binary_operator; + using div_t = binary_operator; struct mod_string { operator std::string() const { @@ -1083,14 +1104,79 @@ namespace sqlite_orm { * Result of mod % operator */ template - using mod_t = binary_operator; + using mod_t = binary_operator; + + struct bitwise_shift_left_string { + operator std::string() const { + return "<<"; + } + }; + + /** + * Result of bitwise shift left << operator + */ + template + using bitwise_shift_left_t = binary_operator; + + struct bitwise_shift_right_string { + operator std::string() const { + return ">>"; + } + }; + + /** + * Result of bitwise shift right >> operator + */ + template + using bitwise_shift_right_t = binary_operator; + + struct bitwise_and_string { + operator std::string() const { + return "&"; + } + }; + + /** + * Result of bitwise and & operator + */ + template + using bitwise_and_t = binary_operator; + + struct bitwise_or_string { + operator std::string() const { + return "|"; + } + }; + + /** + * Result of bitwise or | operator + */ + template + using bitwise_or_t = binary_operator; + + struct bitwise_not_string { + operator std::string() const { + return "~"; + } + }; + + /** + * Result of bitwise not ~ operator + */ + template + struct 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 { operator std::string() const { return "="; } }; - /** * Result of assign = operator */ @@ -1179,6 +1265,31 @@ namespace sqlite_orm { 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)}; @@ -1200,155 +1311,59 @@ namespace sqlite_orm { // #include "constraints.h" +// #include "getter_traits.h" + namespace sqlite_orm { namespace internal { - struct column_base { + template + struct is_field_member_pointer : std::false_type {}; - /** - * Column name. Specified during construction in `make_column`. - */ - const std::string name; + template + struct is_field_member_pointer::value && + !std::is_member_function_pointer::value>::type> + : std::true_type {}; + + template + struct field_member_traits; + + template + struct field_member_traits::value>::type> { + using object_type = O; + using field_type = F; }; /** - * This class stores single column info. column_t is a pair of [column_name:member_pointer] mapped to a storage - * O is a mapped class, e.g. User - * T is a mapped class'es field type, e.g. &User::name - * Op... is a constraints pack, e.g. primary_key_t, autoincrement_t etc - */ - template - struct column_t : column_base { - using object_type = O; - using field_type = T; - using constraints_type = std::tuple; - using member_pointer_t = field_type object_type::*; - using getter_type = G; - using setter_type = S; + * Getters aliases + */ + template + using getter_by_value_const = T (O::*)() const; - /** - * Member pointer used to read/write member - */ - member_pointer_t member_pointer /* = nullptr*/; + template + using getter_by_value = T (O::*)(); - /** - * Getter member function pointer to get a value. If member_pointer is null than - * `getter` and `setter` must be not null - */ - getter_type getter /* = nullptr*/; + template + using getter_by_ref_const = T &(O::*)() const; - /** - * Setter member function - */ - setter_type setter /* = nullptr*/; + template + using getter_by_ref = T &(O::*)(); - /** - * Constraints tuple - */ - constraints_type constraints; + template + using getter_by_const_ref_const = const T &(O::*)() const; - column_t(std::string name, - member_pointer_t member_pointer_, - getter_type getter_, - setter_type setter_, - constraints_type constraints_) : - column_base{std::move(name)}, - member_pointer(member_pointer_), getter(getter_), setter(setter_), - constraints(std::move(constraints_)) {} + template + using getter_by_const_ref = const T &(O::*)(); - /** - * Simplified interface for `NOT NULL` constraint - */ - bool not_null() const { - return !type_is_nullable::value; - } + /** + * Setters aliases + */ + template + using setter_by_value = void (O::*)(T); - template - constexpr bool has() const { - return tuple_helper::tuple_contains_type::value; - } - - template - constexpr bool has_every() const { - if(has() && has()) { - return true; - } else { - return has_every(); - } - } - - template - constexpr bool has_every() const { - return has(); - } - - /** - * Simplified interface for `DEFAULT` constraint - * @return string representation of default value if it exists otherwise nullptr - */ - std::unique_ptr default_value() const { - std::unique_ptr res; - iterate_tuple(this->constraints, [&res](auto &v) { - auto dft = internal::default_value_extractor()(v); - if(dft) { - res = std::move(dft); - } - }); - return res; - } - }; - - /** - * Column traits. Common case. - */ - template - struct is_column : public std::false_type {}; - - /** - * Column traits. Specialized case case. - */ - template - struct is_column> : public std::true_type {}; - - template - struct is_field_member_pointer : std::false_type {}; - - template - struct is_field_member_pointer::value && - !std::is_member_function_pointer::value>::type> - : std::true_type {}; - - /** - * Getters aliases - */ - template - using getter_by_value_const = T (O::*)() const; - - template - using getter_by_value = T (O::*)(); - - template - using getter_by_ref_const = T &(O::*)() const; - - template - using getter_by_ref = T &(O::*)(); - - template - using getter_by_const_ref_const = const T &(O::*)() const; - - template - using getter_by_const_ref = const T &(O::*)(); - - /** - * Setters aliases - */ - template - using setter_by_value = void (O::*)(T); - - template - using setter_by_ref = void (O::*)(T &); + template + using setter_by_ref = void (O::*)(T &); template using setter_by_const_ref = void (O::*)(const T &); @@ -1457,6 +1472,160 @@ namespace sqlite_orm { using object_type = O; using field_type = T; }; + + template + struct member_traits; + + template + struct member_traits::value>::type> { + using object_type = typename field_member_traits::object_type; + using field_type = typename field_member_traits::field_type; + }; + + template + struct member_traits::value>::type> { + using object_type = typename getter_traits::object_type; + using field_type = typename getter_traits::field_type; + }; + + template + struct member_traits::value>::type> { + using object_type = typename setter_traits::object_type; + using field_type = typename setter_traits::field_type; + }; + } +} + +namespace sqlite_orm { + + namespace internal { + + struct column_base { + + /** + * Column name. Specified during construction in `make_column`. + */ + const std::string name; + }; + + /** + * This class stores single column info. column_t is a pair of [column_name:member_pointer] mapped to a storage + * O is a mapped class, e.g. User + * T is a mapped class'es field type, e.g. &User::name + * Op... is a constraints pack, e.g. primary_key_t, autoincrement_t etc + */ + template + struct column_t : column_base { + using object_type = O; + using field_type = T; + using constraints_type = std::tuple; + using member_pointer_t = field_type object_type::*; + using getter_type = G; + using setter_type = S; + + /** + * Member pointer used to read/write member + */ + member_pointer_t member_pointer /* = nullptr*/; + + /** + * Getter member function pointer to get a value. If member_pointer is null than + * `getter` and `setter` must be not null + */ + getter_type getter /* = nullptr*/; + + /** + * Setter member function + */ + setter_type setter /* = nullptr*/; + + /** + * Constraints tuple + */ + constraints_type constraints; + + column_t(std::string name_, + member_pointer_t member_pointer_, + getter_type getter_, + setter_type setter_, + constraints_type constraints_) : + column_base{std::move(name_)}, + member_pointer(member_pointer_), getter(getter_), setter(setter_), constraints(move(constraints_)) {} + + /** + * Simplified interface for `NOT NULL` constraint + */ + bool not_null() const { + return !type_is_nullable::value; + } + + template + constexpr bool has() const { + return tuple_helper::tuple_contains_type::value; + } + + template + constexpr bool has_every() const { + if(has() && has()) { + return true; + } else { + return has_every(); + } + } + + template + constexpr bool has_every() const { + return has(); + } + + /** + * Simplified interface for `DEFAULT` constraint + * @return string representation of default value if it exists otherwise nullptr + */ + std::unique_ptr default_value() const { + std::unique_ptr res; + iterate_tuple(this->constraints, [&res](auto &v) { + auto dft = internal::default_value_extractor()(v); + if(dft) { + res = std::move(dft); + } + }); + return res; + } + }; + + /** + * Column traits. Common case. + */ + template + struct is_column : public std::false_type {}; + + /** + * Column traits. Specialized case case. + */ + template + struct is_column> : public std::true_type {}; + + template + struct column_field_type { + using type = void; + }; + + template + struct column_field_type> { + using type = typename column_t::field_type; + }; + + template + struct column_constraints_type { + using type = std::tuple<>; + }; + + template + struct column_constraints_type> { + using type = typename column_t::constraints_type; + }; + } /** @@ -1468,8 +1637,10 @@ namespace sqlite_orm { class... Op> internal::column_t make_column(const std::string &name, T O::*m, Op... constraints) { - static_assert(constraints::constraints_size::value == std::tuple_size>::value, + static_assert(constraints::template constraints_size::value == std::tuple_size>::value, "Incorrect constraints pack"); + static_assert(internal::is_field_member_pointer::value, + "second argument expected as a member field pointer, not member function pointer"); return {name, m, nullptr, nullptr, std::make_tuple(constraints...)}; } @@ -1490,7 +1661,7 @@ namespace sqlite_orm { static_assert(std::is_same::field_type, typename internal::getter_traits::field_type>::value, "Getter and setter must get and set same data type"); - static_assert(constraints::constraints_size::value == std::tuple_size>::value, + static_assert(constraints::template constraints_size::value == std::tuple_size>::value, "Incorrect constraints pack"); return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; } @@ -1513,7 +1684,7 @@ namespace sqlite_orm { static_assert(std::is_same::field_type, typename internal::getter_traits::field_type>::value, "Getter and setter must get and set same data type"); - static_assert(constraints::constraints_size::value == std::tuple_size>::value, + static_assert(constraints::template constraints_size::value == std::tuple_size>::value, "Incorrect constraints pack"); return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; } @@ -1687,13 +1858,15 @@ namespace sqlite_orm { } } +// #include "negatable.h" + namespace sqlite_orm { namespace internal { struct arithmetic_t; } - namespace conditions { + namespace internal { struct limit_string { operator std::string() const { @@ -1749,7 +1922,7 @@ namespace sqlite_orm { T expr; internal::collate_argument argument; - collate_t(T expr_, internal::collate_argument argument_) : expr(expr_), argument(argument_) {} + collate_t(T expr_, internal::collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} operator std::string() const { return constraints::collate_t{this->argument}; @@ -1848,15 +2021,11 @@ namespace sqlite_orm { * = and == operators object */ template - struct is_equal_t : binary_condition, is_equal_string { + struct is_equal_t : binary_condition, is_equal_string, internal::negatable_t { using self = is_equal_t; using binary_condition::binary_condition; - negated_condition_t operator!() const { - return {*this}; - } - collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } @@ -1884,15 +2053,11 @@ namespace sqlite_orm { * != operator object */ template - struct is_not_equal_t : binary_condition, is_not_equal_string { + struct is_not_equal_t : binary_condition, is_not_equal_string, internal::negatable_t { using self = is_not_equal_t; using binary_condition::binary_condition; - negated_condition_t operator!() const { - return {*this}; - } - collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } @@ -1916,15 +2081,11 @@ namespace sqlite_orm { * > operator object. */ template - struct greater_than_t : binary_condition, greater_than_string { + struct greater_than_t : binary_condition, greater_than_string, internal::negatable_t { using self = greater_than_t; using binary_condition::binary_condition; - negated_condition_t operator!() const { - return {*this}; - } - collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } @@ -1948,15 +2109,11 @@ namespace sqlite_orm { * >= operator object. */ template - struct greater_or_equal_t : binary_condition, greater_or_equal_string { + struct greater_or_equal_t : binary_condition, greater_or_equal_string, internal::negatable_t { using self = greater_or_equal_t; using binary_condition::binary_condition; - negated_condition_t operator!() const { - return {*this}; - } - collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } @@ -1980,15 +2137,11 @@ namespace sqlite_orm { * < operator object. */ template - struct lesser_than_t : binary_condition, lesser_than_string { + struct lesser_than_t : binary_condition, lesser_than_string, internal::negatable_t { using self = lesser_than_t; using binary_condition::binary_condition; - negated_condition_t operator!() const { - return {*this}; - } - collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } @@ -2012,15 +2165,11 @@ namespace sqlite_orm { * <= operator object. */ template - struct lesser_or_equal_t : binary_condition, lesser_or_equal_string { + struct lesser_or_equal_t : binary_condition, lesser_or_equal_string, internal::negatable_t { using self = lesser_or_equal_t; using binary_condition::binary_condition; - negated_condition_t> operator!() const { - return {*this}; - } - collate_t collate_binary() const { return {*this, internal::collate_argument::binary}; } @@ -2050,17 +2199,13 @@ namespace sqlite_orm { * IN operator object. */ template - struct in_t : condition_t, in_base { + struct in_t : condition_t, in_base, internal::negatable_t { using self = in_t; L l; // left expression A arg; // in arg - in_t(L l_, A arg_, bool negative) : in_base{negative}, l(l_), arg(std::move(arg_)) {} - - negated_condition_t operator!() const { - return {*this}; - } + in_t(L l_, A arg_, bool negative_) : in_base{negative_}, l(l_), arg(std::move(arg_)) {} }; struct is_null_string { @@ -2073,16 +2218,12 @@ namespace sqlite_orm { * IS NULL operator object. */ template - struct is_null_t : is_null_string { + struct is_null_t : is_null_string, internal::negatable_t { using self = is_null_t; T t; is_null_t(T t_) : t(std::move(t_)) {} - - negated_condition_t operator!() const { - return {*this}; - } }; struct is_not_null_string { @@ -2095,16 +2236,12 @@ namespace sqlite_orm { * IS NOT NULL operator object. */ template - struct is_not_null_t : is_not_null_string { + struct is_not_null_t : is_not_null_string, internal::negatable_t { using self = is_not_null_t; T t; is_not_null_t(T t_) : t(std::move(t_)) {} - - negated_condition_t operator!() const { - return {*this}; - } }; struct where_string { @@ -2204,27 +2341,29 @@ namespace sqlite_orm { 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_, move(collate_argument_)}, name(move(name_)) {} + }; + /** - * S - storage class + * C - serializator context class */ - template + template struct dynamic_order_by_t : order_by_string { - using storage_type = S; - - struct entry_t : order_by_base { - std::string name; - - entry_t(decltype(name) name_, int asc_desc, std::string collate_argument) : - order_by_base{asc_desc, move(collate_argument)}, name(move(name_)) {} - }; - + 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 storage_type &storage_) : storage(storage_) {} + dynamic_order_by_t(const context_t &context_) : context(context_) {} template void push_back(order_by_t order_by) { - auto columnName = this->storage.string_from_expression(order_by.o, true); + auto newContext = this->context; + newContext.skip_table_name = true; + auto columnName = serialize(order_by.o, newContext); entries.emplace_back(move(columnName), order_by.asc_desc, move(order_by._collate_argument)); } @@ -2242,7 +2381,7 @@ namespace sqlite_orm { protected: std::vector entries; - const storage_type &storage; + context_t context; }; template @@ -2254,8 +2393,8 @@ namespace sqlite_orm { template struct is_order_by> : std::true_type {}; - template - struct is_order_by> : std::true_type {}; + template + struct is_order_by> : std::true_type {}; struct group_by_string { operator std::string() const { @@ -2313,7 +2452,7 @@ namespace sqlite_orm { * LIKE operator object. */ template - struct like_t : condition_t, like_string { + struct like_t : condition_t, like_string, internal::negatable_t { using self = like_t; using arg_t = A; using pattern_t = T; @@ -2324,17 +2463,13 @@ namespace sqlite_orm { sqlite_orm::internal::optional_container arg3; // not escape cause escape exists as a function here - like_t(arg_t arg_, pattern_t pattern_, sqlite_orm::internal::optional_container escape) : - arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape)) {} + like_t(arg_t arg_, pattern_t pattern_, sqlite_orm::internal::optional_container escape_) : + arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {} template like_t escape(C c) const { - sqlite_orm::internal::optional_container arg3{std::move(c)}; - return {std::move(this->arg), std::move(this->pattern), std::move(arg3)}; - } - - negated_condition_t operator!() const { - return {*this}; + sqlite_orm::internal::optional_container newArg3{std::move(c)}; + return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; } }; @@ -2345,7 +2480,7 @@ namespace sqlite_orm { }; template - struct glob_t : condition_t, glob_string { + struct glob_t : condition_t, glob_string, internal::negatable_t { using self = glob_t; using arg_t = A; using pattern_t = T; @@ -2354,10 +2489,6 @@ namespace sqlite_orm { pattern_t pattern; glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {} - - negated_condition_t operator!() const { - return {*this}; - } }; struct cross_join_string { @@ -2512,17 +2643,13 @@ namespace sqlite_orm { }; template - struct exists_t : condition_t, exists_string { + struct exists_t : condition_t, exists_string, internal::negatable_t { using type = T; using self = exists_t; type t; exists_t(T t_) : t(std::move(t_)) {} - - negated_condition_t operator!() const { - return {*this}; - } }; struct having_string { @@ -2574,66 +2701,71 @@ namespace sqlite_orm { } + template::value>::type> + internal::negated_condition_t operator!(T arg) { + return {std::move(arg)}; + } + /** * Cute operators for columns */ template - conditions::lesser_than_t operator<(internal::expression_t expr, R r) { + internal::lesser_than_t operator<(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template - conditions::lesser_than_t operator<(L l, internal::expression_t expr) { + internal::lesser_than_t operator<(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template - conditions::lesser_or_equal_t operator<=(internal::expression_t expr, R r) { + internal::lesser_or_equal_t operator<=(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template - conditions::lesser_or_equal_t operator<=(L l, internal::expression_t expr) { + internal::lesser_or_equal_t operator<=(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template - conditions::greater_than_t operator>(internal::expression_t expr, R r) { + internal::greater_than_t operator>(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template - conditions::greater_than_t operator>(L l, internal::expression_t expr) { + internal::greater_than_t operator>(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template - conditions::greater_or_equal_t operator>=(internal::expression_t expr, R r) { + internal::greater_or_equal_t operator>=(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template - conditions::greater_or_equal_t operator>=(L l, internal::expression_t expr) { + internal::greater_or_equal_t operator>=(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template - conditions::is_equal_t operator==(internal::expression_t expr, R r) { + internal::is_equal_t operator==(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template - conditions::is_equal_t operator==(L l, internal::expression_t expr) { + internal::is_equal_t operator==(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } template - conditions::is_not_equal_t operator!=(internal::expression_t expr, R r) { + internal::is_not_equal_t operator!=(internal::expression_t expr, R r) { return {std::move(expr.t), std::move(r)}; } template - conditions::is_not_equal_t operator!=(L l, internal::expression_t expr) { + internal::is_not_equal_t operator!=(L l, internal::expression_t expr) { return {std::move(l), std::move(expr.t)}; } @@ -2728,184 +2860,184 @@ namespace sqlite_orm { } template - conditions::using_t using_(F O::*p) { + internal::using_t using_(F O::*p) { return {std::move(p)}; } template - conditions::on_t on(T t) { + internal::on_t on(T t) { return {std::move(t)}; } template - conditions::cross_join_t cross_join() { + internal::cross_join_t cross_join() { return {}; } template - conditions::natural_join_t natural_join() { + internal::natural_join_t natural_join() { return {}; } template - conditions::left_join_t left_join(O o) { + internal::left_join_t left_join(O o) { return {std::move(o)}; } template - conditions::join_t join(O o) { + internal::join_t join(O o) { return {std::move(o)}; } template - conditions::left_outer_join_t left_outer_join(O o) { + internal::left_outer_join_t left_outer_join(O o) { return {std::move(o)}; } template - conditions::inner_join_t inner_join(O o) { + internal::inner_join_t inner_join(O o) { return {std::move(o)}; } template - conditions::offset_t offset(T off) { + internal::offset_t offset(T off) { return {std::move(off)}; } template - conditions::limit_t limit(T lim) { + internal::limit_t limit(T lim) { return {std::move(lim)}; } template - typename std::enable_if::value, conditions::limit_t>::type - limit(O off, T lim) { + typename std::enable_if::value, internal::limit_t>::type limit(O off, + T lim) { return {std::move(lim), {std::move(off)}}; } template - conditions::limit_t limit(T lim, conditions::offset_t offt) { + internal::limit_t limit(T lim, internal::offset_t offt) { return {std::move(lim), {std::move(offt.off)}}; } template::value || - std::is_base_of::value>::type> - conditions::and_condition_t operator&&(L l, R r) { + typename = typename std::enable_if::value || + std::is_base_of::value>::type> + internal::and_condition_t operator&&(L l, R r) { return {std::move(l), std::move(r)}; } template::value || - std::is_base_of::value>::type> - conditions::or_condition_t operator||(L l, R r) { + typename = typename std::enable_if::value || + std::is_base_of::value>::type> + internal::or_condition_t operator||(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::is_not_null_t is_not_null(T t) { + internal::is_not_null_t is_not_null(T t) { return {std::move(t)}; } template - conditions::is_null_t is_null(T t) { + internal::is_null_t is_null(T t) { return {std::move(t)}; } template - conditions::in_t> in(L l, std::vector values) { + internal::in_t> in(L l, std::vector values) { return {std::move(l), std::move(values), false}; } template - conditions::in_t> in(L l, std::initializer_list values) { + internal::in_t> in(L l, std::initializer_list values) { return {std::move(l), std::move(values), false}; } template - conditions::in_t in(L l, A arg) { + internal::in_t in(L l, A arg) { return {std::move(l), std::move(arg), false}; } template - conditions::in_t> not_in(L l, std::vector values) { + internal::in_t> not_in(L l, std::vector values) { return {std::move(l), std::move(values), true}; } template - conditions::in_t> not_in(L l, std::initializer_list values) { + internal::in_t> not_in(L l, std::initializer_list values) { return {std::move(l), std::move(values), true}; } template - conditions::in_t not_in(L l, A arg) { + internal::in_t not_in(L l, A arg) { return {std::move(l), std::move(arg), true}; } template - conditions::is_equal_t is_equal(L l, R r) { + internal::is_equal_t is_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::is_equal_t eq(L l, R r) { + internal::is_equal_t eq(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::is_not_equal_t is_not_equal(L l, R r) { + internal::is_not_equal_t is_not_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::is_not_equal_t ne(L l, R r) { + internal::is_not_equal_t ne(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::greater_than_t greater_than(L l, R r) { + internal::greater_than_t greater_than(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::greater_than_t gt(L l, R r) { + internal::greater_than_t gt(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::greater_or_equal_t greater_or_equal(L l, R r) { + internal::greater_or_equal_t greater_or_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::greater_or_equal_t ge(L l, R r) { + internal::greater_or_equal_t ge(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::lesser_than_t lesser_than(L l, R r) { + internal::lesser_than_t lesser_than(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::lesser_than_t lt(L l, R r) { + internal::lesser_than_t lt(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::lesser_or_equal_t lesser_or_equal(L l, R r) { + internal::lesser_or_equal_t lesser_or_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::lesser_or_equal_t le(L l, R r) { + internal::lesser_or_equal_t le(L l, R r) { return {std::move(l), std::move(r)}; } template - conditions::where_t where(C c) { + internal::where_t where(C c) { return {std::move(c)}; } @@ -2914,7 +3046,7 @@ namespace sqlite_orm { * Example: storage.select(&User::name, order_by(&User::id)) */ template - conditions::order_by_t order_by(O o) { + internal::order_by_t order_by(O o) { return {std::move(o)}; } @@ -2923,7 +3055,7 @@ namespace sqlite_orm { * Example: storage.get_all(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) */ template - conditions::multi_order_by_t multi_order_by(Args &&... args) { + internal::multi_order_by_t multi_order_by(Args &&... args) { return {std::make_tuple(std::forward(args)...)}; } @@ -2940,8 +3072,10 @@ namespace sqlite_orm { * } */ template - conditions::dynamic_order_by_t dynamic_order_by(const S &storage) { - return {storage}; + internal::dynamic_order_by_t> + dynamic_order_by(const S &storage) { + internal::serializator_context_builder builder(storage); + return builder(); } /** @@ -2949,7 +3083,7 @@ namespace sqlite_orm { * Example: storage.get_all(group_by(&Employee::name)) */ template - conditions::group_by_t group_by(Args &&... args) { + internal::group_by_t group_by(Args &&... args) { return {std::make_tuple(std::forward(args)...)}; } @@ -2958,7 +3092,7 @@ namespace sqlite_orm { * Example: storage.select(between(&User::id, 10, 20)) */ template - conditions::between_t between(A expr, T b1, T b2) { + internal::between_t between(A expr, T b1, T b2) { return {std::move(expr), std::move(b1), std::move(b2)}; } @@ -2967,7 +3101,7 @@ namespace sqlite_orm { * Example: storage.select(like(&User::name, "T%")) */ template - conditions::like_t like(A a, T t) { + internal::like_t like(A a, T t) { return {std::move(a), std::move(t), {}}; } @@ -2976,7 +3110,7 @@ namespace sqlite_orm { * Example: storage.select(glob(&User::name, "*S")) */ template - conditions::glob_t glob(A a, T t) { + internal::glob_t glob(A a, T t) { return {std::move(a), std::move(t)}; } @@ -2985,7 +3119,7 @@ namespace sqlite_orm { * Example: storage.select(like(&User::name, "T%", "%")) */ template - conditions::like_t like(A a, T t, E e) { + internal::like_t like(A a, T t, E e) { return {std::move(a), std::move(t), {std::move(e)}}; } @@ -2998,7 +3132,7 @@ namespace sqlite_orm { order_by(&Agent::comission)); */ template - conditions::exists_t exists(T t) { + internal::exists_t exists(T t) { return {std::move(t)}; } @@ -3007,7 +3141,7 @@ namespace sqlite_orm { * Example: storage.get_all(group_by(&Employee::name), having(greater_than(count(&Employee::name), 2))); */ template - conditions::having_t having(T t) { + internal::having_t having(T t) { return {std::move(t)}; } @@ -3016,7 +3150,7 @@ namespace sqlite_orm { * Example: cast(&User::id) */ template - conditions::cast_t cast(E e) { + internal::cast_t cast(E e) { return {std::move(e)}; } } @@ -3208,9 +3342,9 @@ namespace sqlite_orm { }; template - struct join_iterator, Tail...> : public join_iterator { + struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; - using join_type = conditions::cross_join_t; + using join_type = cross_join_t; template void operator()(const L &l) { @@ -3220,9 +3354,9 @@ namespace sqlite_orm { }; template - struct join_iterator, Tail...> : public join_iterator { + struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; - using join_type = conditions::natural_join_t; + using join_type = natural_join_t; template void operator()(const L &l) { @@ -3232,9 +3366,9 @@ namespace sqlite_orm { }; template - struct join_iterator, Tail...> : public join_iterator { + struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; - using join_type = conditions::left_join_t; + using join_type = left_join_t; template void operator()(const L &l) { @@ -3244,9 +3378,9 @@ namespace sqlite_orm { }; template - struct join_iterator, Tail...> : public join_iterator { + struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; - using join_type = conditions::join_t; + using join_type = join_t; template void operator()(const L &l) { @@ -3256,9 +3390,9 @@ namespace sqlite_orm { }; template - struct join_iterator, Tail...> : public join_iterator { + struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; - using join_type = conditions::left_outer_join_t; + using join_type = left_outer_join_t; template void operator()(const L &l) { @@ -3268,9 +3402,9 @@ namespace sqlite_orm { }; template - struct join_iterator, Tail...> : public join_iterator { + struct join_iterator, Tail...> : public join_iterator { using super = join_iterator; - using join_type = conditions::inner_join_t; + using join_type = inner_join_t; template void operator()(const L &l) { @@ -3333,7 +3467,10 @@ namespace sqlite_orm { namespace sqlite_orm { - namespace core_functions { + namespace internal { + + template + struct unique_ptr_result_of {}; /** * Base class for operator overloading @@ -3402,9 +3539,45 @@ namespace sqlite_orm { } }; -#if SQLITE_VERSION_NUMBER >= 3007016 - - struct char_string { + struct hex_string { + operator std::string() const { + return "HEX"; + } + }; + + struct quote_string { + operator std::string() const { + return "QUOTE"; + } + }; + + struct randomblob_string { + operator std::string() const { + return "RANDOMBLOB"; + } + }; + + struct instr_string { + operator std::string() const { + return "INSTR"; + } + }; + + struct replace_string { + operator std::string() const { + return "REPLACE"; + } + }; + + struct round_string { + operator std::string() const { + return "ROUND"; + } + }; + +#if SQLITE_VERSION_NUMBER >= 3007016 + + struct char_string { operator std::string() const { return "CHAR"; } @@ -3430,6 +3603,12 @@ namespace sqlite_orm { } }; + struct time_string { + operator std::string() const { + return "TIME"; + } + }; + struct datetime_string { operator std::string() const { return "DATETIME"; @@ -3442,6 +3621,12 @@ namespace sqlite_orm { } }; + struct strftime_string { + operator std::string() const { + return "STRFTIME"; + } + }; + struct zeroblob_string { operator std::string() const { return "ZEROBLOB"; @@ -3453,57 +3638,138 @@ namespace sqlite_orm { return "SUBSTR"; } }; +#ifdef SQLITE_SOUNDEX + struct soundex_string { + operator std::string() const { + return "SOUNDEX"; + } + }; +#endif + struct total_string { + operator std::string() const { + return "TOTAL"; + } + }; + + struct sum_string { + operator std::string() const { + return "SUM"; + } + }; + + struct count_string { + operator std::string() const { + return "COUNT"; + } + }; + + /** + * T is use to specify type explicitly for queries like + * SELECT COUNT(*) FROM table_name; + * T can be omitted with void. + */ + template + struct count_asterisk_t : count_string { + using type = T; + }; + + /** + * The same thing as count() but without T arg. + * Is used in cases like this: + * SELECT cust_code, cust_name, cust_city, grade + * FROM customer + * WHERE grade=2 AND EXISTS + * (SELECT COUNT(*) + * FROM customer + * WHERE grade=2 + * GROUP BY grade + * HAVING COUNT(*)>2); + * `c++` + * auto rows = + * storage.select(columns(&Customer::code, &Customer::name, &Customer::city, &Customer::grade), + * where(is_equal(&Customer::grade, 2) + * and exists(select(count(), + * where(is_equal(&Customer::grade, 2)), + * group_by(&Customer::grade), + * having(greater_than(count(), 2)))))); + */ + struct count_asterisk_without_type : count_string {}; + + struct avg_string { + operator std::string() const { + return "AVG"; + } + }; + + struct max_string { + operator std::string() const { + return "MAX"; + } + }; + + struct min_string { + operator std::string() const { + return "MIN"; + } + }; + + struct group_concat_string { + operator std::string() const { + return "GROUP_CONCAT"; + } + }; + } /** * Cute operators for core functions */ - template::value>::type> - conditions::lesser_than_t operator<(F f, R r) { + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::lesser_than_t operator<(F f, R r) { return {std::move(f), std::move(r)}; } - template::value>::type> - conditions::lesser_or_equal_t operator<=(F f, R r) { + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::lesser_or_equal_t operator<=(F f, R r) { return {std::move(f), std::move(r)}; } - template::value>::type> - conditions::greater_than_t operator>(F f, R r) { + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::greater_than_t operator>(F f, R r) { return {std::move(f), std::move(r)}; } - template::value>::type> - conditions::greater_or_equal_t operator>=(F f, R r) { + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::greater_or_equal_t operator>=(F f, R r) { return {std::move(f), std::move(r)}; } - template::value>::type> - conditions::is_equal_t operator==(F f, R r) { + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::is_equal_t operator==(F f, R r) { return {std::move(f), std::move(r)}; } - template::value>::type> - conditions::is_not_equal_t operator!=(F f, R r) { + template< + class F, + class R, + typename = typename std::enable_if::value>::type> + internal::is_not_equal_t operator!=(F f, R r) { return {std::move(f), std::move(r)}; } @@ -3511,42 +3777,42 @@ namespace sqlite_orm { * LENGTH(x) function https://sqlite.org/lang_corefunc.html#length */ template - core_functions::core_function_t length(T t) { + internal::core_function_t length(T t) { std::tuple args{std::forward(t)}; - return {std::move(args)}; + return {move(args)}; } /** * ABS(x) function https://sqlite.org/lang_corefunc.html#abs */ template - core_functions::core_function_t, core_functions::abs_string, T> abs(T t) { + internal::core_function_t, internal::abs_string, T> abs(T t) { std::tuple args{std::forward(t)}; - return {std::move(args)}; + return {move(args)}; } /** * LOWER(x) function https://sqlite.org/lang_corefunc.html#lower */ template - core_functions::core_function_t lower(T t) { + internal::core_function_t lower(T t) { std::tuple args{std::forward(t)}; - return {std::move(args)}; + return {move(args)}; } /** * UPPER(x) function https://sqlite.org/lang_corefunc.html#upper */ template - core_functions::core_function_t upper(T t) { + internal::core_function_t upper(T t) { std::tuple args{std::forward(t)}; - return {std::move(args)}; + return {move(args)}; } /** * CHANGES() function https://sqlite.org/lang_corefunc.html#changes */ - inline core_functions::core_function_t changes() { + inline internal::core_function_t changes() { return {{}}; } @@ -3554,54 +3820,117 @@ namespace sqlite_orm { * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim */ template - core_functions::core_function_t trim(T t) { + internal::core_function_t trim(T t) { std::tuple args{std::forward(t)}; - return {std::move(args)}; + return {move(args)}; } /** * TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim */ template - core_functions::core_function_t trim(X x, Y y) { + internal::core_function_t trim(X x, Y y) { std::tuple args{std::forward(x), std::forward(y)}; - return {std::move(args)}; + return {move(args)}; } /** * LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim */ template - core_functions::core_function_t ltrim(X x) { + internal::core_function_t ltrim(X x) { std::tuple args{std::forward(x)}; - return {std::move(args)}; + return {move(args)}; } /** * LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim */ template - core_functions::core_function_t ltrim(X x, Y y) { + internal::core_function_t ltrim(X x, Y y) { std::tuple args{std::forward(x), std::forward(y)}; - return {std::move(args)}; + return {move(args)}; } /** * RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim */ template - core_functions::core_function_t rtrim(X x) { + internal::core_function_t rtrim(X x) { std::tuple args{std::forward(x)}; - return {std::move(args)}; + return {move(args)}; } /** * RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim */ template - core_functions::core_function_t rtrim(X x, Y y) { + internal::core_function_t rtrim(X x, Y y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {move(args)}; + } + + /** + * HEX(X) function https://sqlite.org/lang_corefunc.html#hex + */ + template + internal::core_function_t hex(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * QUOTE(X) function https://sqlite.org/lang_corefunc.html#quote + */ + template + internal::core_function_t quote(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * RANDOMBLOB(X) function https://sqlite.org/lang_corefunc.html#randomblob + */ + template + internal::core_function_t, internal::randomblob_string, X> randomblob(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * INSTR(X) function https://sqlite.org/lang_corefunc.html#instr + */ + template + internal::core_function_t instr(X x, Y y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {move(args)}; + } + + /** + * REPLACE(X) function https://sqlite.org/lang_corefunc.html#replace + */ + template + internal::core_function_t replace(X x, Y y, Z z) { + std::tuple args{std::forward(x), std::forward(y), std::forward(z)}; + return {move(args)}; + } + + /** + * ROUND(X) function https://sqlite.org/lang_corefunc.html#round + */ + template + internal::core_function_t round(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * ROUND(X, Y) function https://sqlite.org/lang_corefunc.html#round + */ + template + internal::core_function_t round(X x, Y y) { std::tuple args{std::forward(x), std::forward(y)}; - return {std::move(args)}; + return {move(args)}; } #if SQLITE_VERSION_NUMBER >= 3007016 @@ -3610,14 +3939,14 @@ namespace sqlite_orm { * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char */ template - core_functions::core_function_t char_(Args... args) { + internal::core_function_t char_(Args... args) { return {std::make_tuple(std::forward(args)...)}; } /** * RANDOM() function https://www.sqlite.org/lang_corefunc.html#random */ - inline core_functions::core_function_t random() { + inline internal::core_function_t random() { return {{}}; } @@ -3627,7 +3956,7 @@ namespace sqlite_orm { * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce */ template - core_functions::core_function_t coalesce(Args... args) { + internal::core_function_t coalesce(Args... args) { return {std::make_tuple(std::forward(args)...)}; } @@ -3635,54 +3964,171 @@ namespace sqlite_orm { * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - core_functions::core_function_t date(Args... args) { + internal::core_function_t date(Args... args) { std::tuple t{std::forward(args)...}; - return {std::move(t)}; + return {move(t)}; + } + + /** + * TIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::core_function_t time(Args... args) { + std::tuple t{std::forward(args)...}; + return {move(t)}; } /** * DATETIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - core_functions::core_function_t datetime(Args... args) { + internal::core_function_t datetime(Args... args) { std::tuple t{std::forward(args)...}; - return {std::move(t)}; + return {move(t)}; } /** * JULIANDAY(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - core_functions::core_function_t julianday(Args... args) { + internal::core_function_t julianday(Args... args) { std::tuple t{std::forward(args)...}; - return {std::move(t)}; + return {move(t)}; + } + + /** + * STRFTIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::core_function_t strftime(Args... args) { + std::tuple t{std::forward(args)...}; + return {move(t)}; } /** * ZEROBLOB(N) function https://www.sqlite.org/lang_corefunc.html#zeroblob */ template - core_functions::core_function_t, core_functions::zeroblob_string, N> zeroblob(N n) { + internal::core_function_t, internal::zeroblob_string, N> zeroblob(N n) { std::tuple args{std::forward(n)}; - return {std::move(args)}; + return {move(args)}; } /** * SUBSTR(X,Y) function https://www.sqlite.org/lang_corefunc.html#substr */ template - core_functions::core_function_t substr(X x, Y y) { + internal::core_function_t substr(X x, Y y) { std::tuple args{std::forward(x), std::forward(y)}; - return {std::move(args)}; + return {move(args)}; } /** * SUBSTR(X,Y,Z) function https://www.sqlite.org/lang_corefunc.html#substr */ template - core_functions::core_function_t substr(X x, Y y, Z z) { + internal::core_function_t substr(X x, Y y, Z z) { std::tuple args{std::forward(x), std::forward(y), std::forward(z)}; - return {std::move(args)}; + return {move(args)}; + } + +#ifdef SQLITE_SOUNDEX + /** + * SOUNDEX(X) function https://www.sqlite.org/lang_corefunc.html#soundex + */ + template + internal::core_function_t soundex(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } +#endif + + /** + * TOTAL(X) aggregate function. + */ + template + internal::core_function_t total(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * SUM(X) aggregate function. + */ + template + internal::core_function_t, internal::sum_string, X> sum(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * COUNT(X) aggregate function. + */ + template + internal::core_function_t count(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * 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::core_function_t avg(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * MAX(X) aggregate function. + */ + template + internal::core_function_t, internal::max_string, X> max(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * MIN(X) aggregate function. + */ + template + internal::core_function_t, internal::min_string, X> min(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * GROUP_CONCAT(X) aggregate function. + */ + template + internal::core_function_t group_concat(X x) { + std::tuple args{std::forward(x)}; + return {move(args)}; + } + + /** + * GROUP_CONCAT(X, Y) aggregate function. + */ + template + internal::core_function_t group_concat(X x, Y y) { + std::tuple args{std::forward(x), std::forward(y)}; + return {move(args)}; } template - struct avg_t : avg_string { - using arg_type = T; - - arg_type arg; - - avg_t(arg_type arg_) : arg(std::move(arg_)) {} - }; - - struct count_string { - operator std::string() const { - return "COUNT"; - } - }; - - /** - * Result of count(...) call. - * T is an argument type - */ - template - struct count_t : count_string { - using arg_type = T; - - arg_type arg; - - count_t(arg_type arg_) : arg(std::move(arg_)) {} - }; - - /** - * T is use to specify type explicitly for queries like - * SELECT COUNT(*) FROM table_name; - * T can be omitted with void. - */ - template - struct count_asterisk_t : count_string { - using type = T; - }; - - /** - * The same thing as count() but without T arg. - * Is used in cases like this: - * SELECT cust_code, cust_name, cust_city, grade - * FROM customer - * WHERE grade=2 AND EXISTS - * (SELECT COUNT(*) - * FROM customer - * WHERE grade=2 - * GROUP BY grade - * HAVING COUNT(*)>2); - * `c++` - * auto rows = - * storage.select(columns(&Customer::code, &Customer::name, &Customer::city, &Customer::grade), - * where(is_equal(&Customer::grade, 2) - * and exists(select(count(), - * where(is_equal(&Customer::grade, 2)), - * group_by(&Customer::grade), - * having(greater_than(count(), 2)))))); - */ - struct count_asterisk_without_type : count_string {}; - - struct sum_string { - operator std::string() const { - return "SUM"; - } - }; - - /** - * Result of sum(...) call. - * T is an argument type - */ - template - struct sum_t : sum_string { - using arg_type = T; - - arg_type arg; - - sum_t(arg_type arg_) : arg(std::move(arg_)) {} - }; - - struct total_string { - operator std::string() const { - return "TOTAL"; - } - }; - - /** - * Result of total(...) call. - * T is an argument type - */ - template - struct total_t : total_string { - using arg_type = T; - - arg_type arg; - - total_t(arg_type arg_) : arg(std::move(arg_)) {} - }; - - struct max_string { - operator std::string() const { - return "MAX"; - } - }; - - /** - * Result of max(...) call. - * T is an argument type - */ - template - struct max_t : max_string { - using arg_type = T; - - arg_type arg; - - max_t(arg_type arg_) : arg(std::move(arg_)) {} - }; - - struct min_string { - operator std::string() const { - return "MIN"; - } - }; - - /** - * Result of min(...) call. - * T is an argument type - */ - template - struct min_t : min_string { - using arg_type = T; - - arg_type arg; - - min_t(arg_type arg_) : arg(std::move(arg_)) {} - }; - - struct group_concat_string { - operator std::string() const { - return "GROUP_CONCAT"; - } - }; - - /** - * Result of group_concat(X) call. - * T is an argument type - */ - template - struct group_concat_single_t : group_concat_string { - using arg_type = T; - - arg_type arg; - - group_concat_single_t(arg_type arg_) : arg(std::move(arg_)) {} - }; - - struct group_concat_double_base : group_concat_string { - std::string y; - - group_concat_double_base(std::string y_) : y(move(y_)) {} - }; - - /** - * Result of group_concat(X, Y) call. - * T is an argument type - */ - template - struct group_concat_double_t : group_concat_double_base { - using arg_type = T; - - arg_type arg; - - group_concat_double_t(arg_type arg_, std::string y) : - group_concat_double_base(move(y)), arg(std::move(arg_)) {} - }; - } - - /** - * AVG(X) aggregate function. - */ - template - aggregate_functions::avg_t avg(T t) { - return {std::move(t)}; - } - - /** - * COUNT(X) aggregate function. - */ - template - aggregate_functions::count_t count(T t) { - return {std::move(t)}; - } - - /** - * COUNT(*) without FROM function. - */ - inline aggregate_functions::count_asterisk_without_type count() { - return {}; - } - - /** - * COUNT(*) with FROM function. Specified type T will be serializeed as - * a from argument. - */ - template - aggregate_functions::count_asterisk_t count() { - return {}; - } - - /** - * SUM(X) aggregate function. - */ - template - aggregate_functions::sum_t sum(T t) { - return {std::move(t)}; - } - - /** - * MAX(X) aggregate function. - */ - template - aggregate_functions::max_t max(T t) { - return {std::move(t)}; - } - - /** - * MIN(X) aggregate function. - */ - template - aggregate_functions::min_t min(T t) { - return {std::move(t)}; - } - - /** - * TOTAL(X) aggregate function. - */ - template - aggregate_functions::total_t total(T t) { - return {std::move(t)}; - } - - /** - * GROUP_CONCAT(X) aggregate function. - */ - template - aggregate_functions::group_concat_single_t group_concat(T t) { - return {std::move(t)}; - } - - /** - * GROUP_CONCAT(X, Y) aggregate function. - */ - template - aggregate_functions::group_concat_double_t group_concat(T t, Y y) { - return {std::move(t), std::move(y)}; - } -} -#pragma once - -namespace sqlite_orm { - - namespace internal { + namespace internal { /** * Cute class used to compare setters/getters and member pointers with each other. @@ -4158,8 +4332,8 @@ namespace sqlite_orm { using left_type = typename compound_operator::left_type; using right_type = typename compound_operator::right_type; - union_t(left_type l, right_type r, decltype(all) all) : - compound_operator(std::move(l), std::move(r)), union_base{all} {} + union_t(left_type l, right_type r, decltype(all) 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) {} }; @@ -4214,6 +4388,11 @@ namespace sqlite_orm { using type = T; }; + template + struct object_t { + using type = T; + }; + template struct then_t { using expression_type = T; @@ -4252,9 +4431,9 @@ namespace sqlite_orm { template simple_case_builder> when(W w, then_t t) { using result_args_type = std::tuple>; - result_args_type result_args; - move_tuple::value>(result_args, this->args); std::pair newPair{std::move(w), std::move(t.expression)}; + result_args_type result_args = + std::tuple_cat(std::move(this->args), std::move(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)}; } @@ -4271,14 +4450,10 @@ namespace sqlite_orm { 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 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"); } } @@ -4386,10 +4561,29 @@ namespace sqlite_orm { return {std::move(lhs), std::move(rhs), true}; } + /** + * SELECT * FROM T function. + * T is typed mapped to a storage. + * Example: auto rows = storage.select(asterisk()); + * // decltype(rows) is std::vector> + * If you need to fetch result as objects not tuple please use `object` instead. + */ template internal::asterisk_t asterisk() { return {}; } + + /** + * SELECT * FROM T function. + * T is typed mapped to a storage. + * Example: auto rows = storage.select(object()); + * // decltype(rows) is std::vector + * If you need to fetch result as tuples not objects please use `asterisk` instead. + */ + template + internal::object_t object() { + return {}; + } } #pragma once @@ -4440,12 +4634,12 @@ namespace sqlite_orm { namespace sqlite_orm { struct table_info { - int cid; + int cid = 0; std::string name; std::string type; - bool notnull; + bool notnull = false; std::string dflt_value; - int pk; + int pk = 0; }; } @@ -4497,11 +4691,12 @@ namespace sqlite_orm { #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type #include // std::string, std::wstring #ifndef SQLITE_ORM_OMITS_CODECVT -#include // std::wstring_convert, std::codecvt_utf8_utf16 +#include // std::codecvt_utf8_utf16 #endif // SQLITE_ORM_OMITS_CODECVT #include // std::vector #include // std::nullptr_t #include // std::declval +#include // std::wstring_convert // #include "is_std_ptr.h" @@ -4746,7 +4941,7 @@ namespace sqlite_orm { #include // std::unique_ptr #include // std::array #include // std::transform -#include // std::toupper +#include // std::toupper namespace sqlite_orm { @@ -4781,15 +4976,17 @@ namespace sqlite_orm { 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), ::toupper); - static std::array all = { + 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); @@ -4862,8 +5059,8 @@ namespace sqlite_orm { /** * Specialization for std::string. */ - template - struct row_extractor::value>> { + template<> + struct row_extractor { std::string extract(const char *row_value) { if(row_value) { return row_value; @@ -4885,8 +5082,8 @@ namespace sqlite_orm { /** * Specialization for std::wstring. */ - template - struct row_extractor::value>> { + template<> + struct row_extractor { std::wstring extract(const char *row_value) { if(row_value) { std::wstring_convert> converter; @@ -4907,46 +5104,14 @@ namespace sqlite_orm { } }; #endif // SQLITE_ORM_OMITS_CODECVT - /** - * Specialization for std::vector. - */ + template - struct row_extractor>::value>> { - std::vector extract(const char *row_value) { + struct row_extractor::value>> { + using value_type = typename is_std_ptr::element_type; + + V extract(const char *row_value) { if(row_value) { - auto len = ::strlen(row_value); - return this->go(row_value, len); - } else { - return {}; - } - } - - std::vector extract(sqlite3_stmt *stmt, int columnIndex) { - auto bytes = static_cast(sqlite3_column_blob(stmt, columnIndex)); - auto len = sqlite3_column_bytes(stmt, columnIndex); - return this->go(bytes, len); - } - - protected: - std::vector go(const char *bytes, size_t len) { - if(len) { - std::vector res; - res.reserve(len); - std::copy(bytes, bytes + len, std::back_inserter(res)); - return res; - } else { - return {}; - } - } - }; - - template - struct row_extractor::value>> { - using value_type = typename is_std_ptr::element_type; - - V extract(const char *row_value) { - if(row_value) { - return is_std_ptr::make(row_extractor().extract(row_value)); + return is_std_ptr::make(row_extractor().extract(row_value)); } else { return {}; } @@ -5062,8 +5227,8 @@ namespace sqlite_orm { /** * Specialization for journal_mode. */ - template - struct row_extractor::value>> { + template<> + struct row_extractor { journal_mode extract(const char *row_value) { if(row_value) { if(auto res = internal::journal_mode_from_string(row_value)) { @@ -5149,32 +5314,109 @@ namespace sqlite_orm { #include // std::string #include // std::forward +// #include "indexed_column.h" + +#include // std::string + +namespace sqlite_orm { + + namespace internal { + + template + struct indexed_column_t { + using column_type = C; + + 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 = 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 + struct indexed_column_maker { + using type = indexed_column_t; + + indexed_column_t operator()(C col) const { + return {std::move(col)}; + } + }; + + template + struct indexed_column_maker> { + using type = indexed_column_t; + + indexed_column_t operator()(indexed_column_t col) const { + return std::move(col); + } + }; + + template + auto make_indexed_column(C col) { + indexed_column_maker maker; + return maker(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)}; + } + +} + namespace sqlite_orm { namespace internal { + struct index_base { + std::string name; + bool unique = false; + }; + template - struct index_t { + struct index_t : index_base { using columns_type = std::tuple; using object_type = void; - std::string name; - bool unique; - columns_type columns; + index_t(std::string name_, bool unique_, columns_type columns_) : + index_base{move(name_), unique_}, columns(move(columns_)) {} - template - void for_each_column_with_constraints(const L &) {} + columns_type columns; }; } template - internal::index_t make_index(const std::string &name, Cols... cols) { - return {name, false, std::make_tuple(std::forward(cols)...)}; + internal::index_t::type...> make_index(const std::string &name, + Cols... cols) { + return {name, false, std::make_tuple(internal::make_indexed_column(cols)...)}; } template - internal::index_t make_unique_index(const std::string &name, Cols... cols) { - return {name, true, std::make_tuple(std::forward(cols)...)}; + internal::index_t::type...> make_unique_index(const std::string &name, + Cols... cols) { + return {name, true, std::make_tuple(internal::make_indexed_column(cols)...)}; } } #pragma once @@ -5271,14 +5513,12 @@ namespace sqlite_orm { } #pragma once -#include // std::enable_if, std::is_same, std::decay +#include // std::enable_if, std::is_same, std::decay, std::is_arithmetic #include // std::tuple #include // std::reference_wrapper // #include "core_functions.h" -// #include "aggregate_functions.h" - // #include "select_constraints.h" // #include "operators.h" @@ -5431,12 +5671,15 @@ namespace sqlite_orm { namespace sqlite_orm { + using int64 = sqlite_int64; + using uint64 = sqlite_uint64; + namespace internal { /** * 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 core_functions::length_t has `type` int cause + * 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 * T - C++ type @@ -5469,61 +5712,23 @@ namespace sqlite_orm { using type = typename setter_traits::field_type; }; - template - struct column_result_t< - St, - T, - typename std::enable_if::value>::type> { - using type = typename T::return_type; - }; - - template - struct column_result_t, void> { - using type = double; + template + struct column_result_t, void> { + using type = R; }; - template - struct column_result_t, void> { - using type = int; + template + struct column_result_t, S, X>, void> { + using type = std::unique_ptr::type>; }; template - struct column_result_t, void> { + struct column_result_t, void> { using type = int; }; - template - struct column_result_t, void> { - using type = std::unique_ptr; - }; - - template - struct column_result_t, void> { - using type = double; - }; - - template - struct column_result_t, void> { - using type = std::string; - }; - - template - struct column_result_t, void> { - using type = std::string; - }; - - template - struct column_result_t, void> { - using type = std::unique_ptr::type>; - }; - - template - struct column_result_t, void> { - using type = std::unique_ptr::type>; - }; - template - struct column_result_t { + struct column_result_t { using type = int; }; @@ -5567,6 +5772,31 @@ namespace sqlite_orm { 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; @@ -5653,8 +5883,13 @@ namespace sqlite_orm { using type = typename storage_traits::storage_mapped_columns::type; }; + template + struct column_result_t, void> { + using type = T; + }; + template - struct column_result_t, void> { + struct column_result_t, void> { using type = T; }; @@ -5664,17 +5899,17 @@ namespace sqlite_orm { }; template - struct column_result_t, void> { + struct column_result_t, void> { using type = bool; }; template - struct column_result_t, void> { + struct column_result_t, void> { using type = bool; }; template - struct column_result_t, void> { + struct column_result_t, void> { using type = bool; }; @@ -5715,7 +5950,7 @@ namespace sqlite_orm { /** * Table name. */ - const std::string name; + std::string name; bool _without_rowid = false; }; @@ -5755,22 +5990,22 @@ namespace sqlite_orm { // Make static_if have at least one input as a workaround for GCC bug: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095 if(!res) { - static_if{}>([&res, &obj, &col](const C &c) { - if(compare_any(col.member_pointer, c)) { + static_if{}>([&res, &obj, &col](const C &c_) { + if(compare_any(col.member_pointer, c_)) { res = &(obj.*col.member_pointer); } })(c); } if(!res) { - static_if{}>([&res, &obj, &col](const C &c) { - if(compare_any(col.getter, c)) { + static_if{}>([&res, &obj, &col](const C &c_) { + if(compare_any(col.getter, c_)) { res = &((obj).*(col.getter))(); } })(c); } if(!res) { - static_if{}>([&res, &obj, &col](const C &c) { - if(compare_any(col.setter, c)) { + static_if{}>([&res, &obj, &col](const C &c_) { + if(compare_any(col.setter, c_)) { res = &((obj).*(col.getter))(); } })(c); @@ -5832,7 +6067,7 @@ namespace sqlite_orm { } /** - * Searches column name by class member pointer passed as first argument. + * Searches column name by class member pointer passed as the first argument. * @return column name or empty string if nothing found. */ template void {} */ @@ -5898,16 +6133,12 @@ namespace sqlite_orm { }); } - template - void for_each_column_with_constraints(const L &l) const { - iterate_tuple(this->columns, l); - } - template void for_each_column_with_field_type(const L &l) const { iterate_tuple(this->columns, [&l](auto &column) { using column_type = typename std::decay::type; - static_if{}>(l)(column); + using field_type = typename column_field_type::type; + static_if{}>(l)(column); }); } @@ -5922,11 +6153,12 @@ namespace sqlite_orm { using tuple_helper::tuple_contains_type; iterate_tuple(this->columns, [&l](auto &column) { using column_type = typename std::decay::type; - static_if{}>(l)(column); + using constraints_type = typename column_constraints_type::type; + static_if{}>(l)(column); }); } - std::vector get_table_info() { + std::vector get_table_info() const { std::vector res; res.reserve(size_t(this->columns_count)); this->for_each_column([&res](auto &col) { @@ -5986,6 +6218,7 @@ namespace sqlite_orm { #include // std::pair, std::make_pair #include // std::vector #include // std::find_if +#include // std::type_index // #include "error_code.h" @@ -6003,8 +6236,6 @@ namespace sqlite_orm { // #include "sync_schema_result.h" -// #include "sqlite_type.h" - // #include "field_value_holder.h" #include // std::enable_if @@ -6039,8 +6270,8 @@ namespace sqlite_orm { struct storage_impl_base { - bool table_exists(const std::string &tableName, sqlite3 *db) { - auto res = false; + bool table_exists(const std::string &tableName, sqlite3 *db) const { + auto result = false; std::stringstream ss; ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = '" << "table" @@ -6056,16 +6287,16 @@ namespace sqlite_orm { } return 0; }, - &res, + &result, nullptr); if(rc != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } - return res; + return result; } - void rename_table(sqlite3 *db, const std::string &oldName, const std::string &newName) { + void rename_table(sqlite3 *db, const std::string &oldName, const std::string &newName) const { std::stringstream ss; ss << "ALTER TABLE " << oldName << " RENAME TO " << newName; auto query = ss.str(); @@ -6103,29 +6334,19 @@ namespace sqlite_orm { }); if(dbColumnInfoIt != dbTableInfo.end()) { auto &dbColumnInfo = *dbColumnInfoIt; - auto dbColumnInfoType = to_sqlite_type(dbColumnInfo.type); - auto storageColumnInfoType = to_sqlite_type(storageColumnInfo.type); - if(dbColumnInfoType && storageColumnInfoType) { - auto columnsAreEqual = - dbColumnInfo.name == storageColumnInfo.name && - *dbColumnInfoType == *storageColumnInfoType && - dbColumnInfo.notnull == storageColumnInfo.notnull && - (dbColumnInfo.dflt_value.length() > 0) == (storageColumnInfo.dflt_value.length() > 0) && - dbColumnInfo.pk == storageColumnInfo.pk; - if(!columnsAreEqual) { - notEqual = true; - break; - } - dbTableInfo.erase(dbColumnInfoIt); - storageTableInfo.erase(storageTableInfo.begin() + - static_cast(storageColumnInfoIndex)); - --storageColumnInfoIndex; - } else { - - // undefined type/types + auto columnsAreEqual = + dbColumnInfo.name == storageColumnInfo.name && + dbColumnInfo.notnull == storageColumnInfo.notnull && + (dbColumnInfo.dflt_value.length() > 0) == (storageColumnInfo.dflt_value.length() > 0) && + dbColumnInfo.pk == storageColumnInfo.pk; + if(!columnsAreEqual) { notEqual = true; break; } + dbTableInfo.erase(dbColumnInfoIt); + storageTableInfo.erase(storageTableInfo.begin() + + static_cast(storageColumnInfoIndex)); + --storageColumnInfoIndex; } else { columnsToAdd.push_back(&storageColumnInfo); } @@ -6133,8 +6354,8 @@ namespace sqlite_orm { return notEqual; } - std::vector get_table_info(const std::string &tableName, sqlite3 *db) { - std::vector res; + std::vector get_table_info(const std::string &tableName, sqlite3 *db) const { + std::vector result; auto query = "PRAGMA table_info('" + tableName + "')"; auto rc = sqlite3_exec( db, @@ -6154,13 +6375,13 @@ namespace sqlite_orm { } return 0; }, - &res, + &result, nullptr); if(rc != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } - return res; + return result; } }; @@ -6182,7 +6403,7 @@ namespace sqlite_orm { template void for_each(const L &l) { this->super::for_each(l); - l(this); + l(*this); } #if SQLITE_VERSION_NUMBER >= 3006019 @@ -6192,7 +6413,7 @@ namespace sqlite_orm { */ int foreign_keys_count() { auto res = 0; - this->table.for_each_column_with_constraints([&res](auto &c) { + iterate_tuple(this->table.columns, [&res](auto &c) { if(internal::is_foreign_key::type>::value) { ++res; } @@ -6212,22 +6433,6 @@ namespace sqlite_orm { return this->table.find_column_name(m); } - /** - * Same thing as above for getter. - */ - template::value>::type> - std::string column_name_simple(T g) const { - return this->table.find_column_name(g); - } - - /** - * Same thing as above for setter. - */ - template::value>::type> - std::string column_name_simple(T s) const { - return this->table.find_column_name(s); - } - /** * Cute function used to find column name by its type and member pointer. Uses SFINAE to * skip inequal type O. @@ -6247,44 +6452,6 @@ namespace sqlite_orm { return this->super::column_name(m); } - /** - * Cute function used to find column name by its type and getter pointer. Uses SFINAE to - * skip inequal type O. - */ - template - std::string column_name(const F &(O::*g)() const, - typename std::enable_if::value>::type * = nullptr) const { - return this->table.find_column_name(g); - } - - /** - * Opposite version of function defined above. Just calls same function in superclass. - */ - template - std::string column_name(const F &(O::*g)() const, - typename std::enable_if::value>::type * = nullptr) const { - return this->super::column_name(g); - } - - /** - * Cute function used to find column name by its type and setter pointer. Uses SFINAE to - * skip inequal type O. - */ - template - std::string column_name(void (O::*s)(F), - typename std::enable_if::value>::type * = nullptr) const { - return this->table.find_column_name(s); - } - - /** - * Opposite version of function defined above. Just calls same function in superclass. - */ - template - std::string column_name(void (O::*s)(F), - typename std::enable_if::value>::type * = nullptr) const { - return this->super::column_name(s); - } - template std::string column_name(const column_pointer &c, typename std::enable_if::value>::type * = nullptr) const { @@ -6298,62 +6465,45 @@ namespace sqlite_orm { } template - auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { + const auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { return *this; } template - auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { + const auto &get_impl(typename std::enable_if::value>::type * = nullptr) const { return this->super::template get_impl(); } template - std::string find_table_name(typename std::enable_if::value>::type * = nullptr) const { - return this->table.name; + auto &get_impl(typename std::enable_if::value>::type * = nullptr) { + return *this; } template - std::string find_table_name(typename std::enable_if::value>::type * = nullptr) const { - return this->super::template find_table_name(); + auto &get_impl(typename std::enable_if::value>::type * = nullptr) { + return this->super::template get_impl(); } template - std::string dump(const O &o, typename std::enable_if::value>::type * = nullptr) { - return this->super::dump(o, nullptr); + const auto *find_table(typename std::enable_if::value>::type * = nullptr) const { + return &this->table; } template - std::string dump(const O &o, typename std::enable_if::value>::type * = nullptr) { - std::stringstream ss; - ss << "{ "; - using pair = std::pair; - std::vector pairs; - this->table.for_each_column([&pairs, &o](auto &c) { - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - pair p{c.name, ""}; - if(c.member_pointer) { - p.second = field_printer()(o.*c.member_pointer); - } else { - using getter_type = typename column_type::getter_type; - field_value_holder valueHolder{((o).*(c.getter))()}; - p.second = field_printer()(valueHolder.value); - } - pairs.push_back(std::move(p)); - }); - for(size_t i = 0; i < pairs.size(); ++i) { - auto &p = pairs[i]; - ss << p.first << " : '" << p.second << "'"; - if(i < pairs.size() - 1) { - ss << ", "; - } else { - ss << " }"; - } + const auto *find_table(typename std::enable_if::value>::type * = nullptr) const { + return this->super::template find_table(); + } + + std::string find_table_name(std::type_index ti) const { + std::type_index thisTypeIndex{typeid(typename H::object_type)}; + if(thisTypeIndex == ti) { + return this->table.name; + } else { + return this->super::find_table_name(ti); } - return ss.str(); } - void add_column(const table_info &ti, sqlite3 *db) { + void add_column(const table_info &ti, sqlite3 *db) const { std::stringstream ss; ss << "ALTER TABLE " << this->table.name << " ADD COLUMN " << ti.name << " "; ss << ti.type << " "; @@ -6387,11 +6537,22 @@ namespace sqlite_orm { * Copies current table to another table with a given **name**. * Performs CREATE TABLE %name% AS SELECT %this->table.columns_names()% FROM &this->table.name%; */ - void copy_table(sqlite3 *db, const std::string &name) { + void + copy_table(sqlite3 *db, const std::string &name, const std::vector &columnsToIgnore) const { + std::ignore = columnsToIgnore; + std::stringstream ss; std::vector columnNames; - this->table.for_each_column([&columnNames](auto &c) { - columnNames.emplace_back(c.name); + this->table.for_each_column([&columnNames, &columnsToIgnore](auto &c) { + auto &columnName = c.name; + auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), + columnsToIgnore.end(), + [&columnName](auto tableInfoPointer) { + return columnName == tableInfoPointer->name; + }); + if(columnToIgnoreIt == columnsToIgnore.end()) { + columnNames.emplace_back(columnName); + } }); auto columnNamesCount = columnNames.size(); ss << "INSERT INTO " << name << " ("; @@ -6428,7 +6589,7 @@ namespace sqlite_orm { } } - sync_schema_result schema_status(sqlite3 *db, bool preserve) { + sync_schema_result schema_status(sqlite3 *db, bool preserve) const { auto res = sync_schema_result::already_in_sync; @@ -6499,8 +6660,7 @@ namespace sqlite_orm { template<> struct storage_impl<> : storage_impl_base { - template - std::string find_table_name() const { + std::string find_table_name(std::type_index) const { return {}; } @@ -6512,8 +6672,8 @@ namespace sqlite_orm { } template - std::string dump(const O &, sqlite3 *, std::nullptr_t) { - throw std::system_error(std::make_error_code(orm_error_code::type_is_not_mapped_to_storage)); + const void *find_table() const { + return nullptr; } }; @@ -6538,7 +6698,6 @@ namespace sqlite_orm { #include // std::vector #include // std::tuple_size, std::tuple, std::make_tuple #include // std::forward, std::pair -#include // std::set #include // std::find #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -6547,39 +6706,154 @@ namespace sqlite_orm { // #include "alias.h" +// #include "row_extractor_builder.h" + // #include "row_extractor.h" -// #include "error_code.h" +// #include "mapped_row_extractor.h" -// #include "type_printer.h" +#include -// #include "tuple_helper.h" +// #include "object_from_column_builder.h" -// #include "constraints.h" +#include -// #include "table_type.h" +// #include "row_extractor.h" -// #include "type_is_nullable.h" +namespace sqlite_orm { -// #include "field_printer.h" + namespace internal { -// #include "rowid.h" + struct object_from_column_builder_base { + sqlite3_stmt *stmt = nullptr; + mutable int index = 0; + }; -// #include "aggregate_functions.h" + /** + * 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; -// #include "operators.h" + object_type &object; -// #include "select_constraints.h" + object_from_column_builder(object_type &object_, sqlite3_stmt *stmt_) : + object_from_column_builder_base{stmt_}, object(object_) {} -// #include "core_functions.h" + template + void operator()(const C &c) const { + using field_type = typename C::field_type; + auto value = row_extractor().extract(this->stmt, this->index++); + if(c.member_pointer) { + this->object.*c.member_pointer = std::move(value); + } else { + ((this->object).*(c.setter))(std::move(value)); + } + } + }; -// #include "conditions.h" + } +} -// #include "statement_binder.h" +namespace sqlite_orm { -// #include "column_result.h" + namespace internal { -// #include "mapped_type_proxy.h" + /** + * This is a private row extractor class. It is used for extracting rows as objects instead of tuple. + * Main difference from regular `row_extractor` is that this class takes table info which is required + * for constructing objects by member pointers. To construct please use `row_extractor_builder` class + * Type arguments: + * V is value type just like regular `row_extractor` has + * T is table info class `table_t` + */ + template + struct mapped_row_extractor { + using table_info_t = T; + + mapped_row_extractor(const table_info_t &tableInfo_) : tableInfo(tableInfo_) {} + + V extract(sqlite3_stmt *stmt, int /*columnIndex*/) { + V res; + object_from_column_builder builder{res, stmt}; + this->tableInfo.for_each_column(builder); + return res; + } + + const table_info_t &tableInfo; + }; + + } + +} + +namespace sqlite_orm { + + namespace internal { + + /** + * This builder is used to construct different row extractors depending on type. + * It has two specializations: for mapped to storage types (e.g. User, Visit etc) and + * for non-mapped (e.g. std::string, QString, int etc). For non mapped its operator() returns + * generic `row_extractor`, for mapped it returns `mapped_row_extractor` instance. + */ + template + struct row_extractor_builder; + + template + struct row_extractor_builder { + + row_extractor operator()(const I * /*tableInfo*/) const { + return {}; + } + }; + + template + struct row_extractor_builder { + + mapped_row_extractor operator()(const I *tableInfo) const { + return {*tableInfo}; + } + }; + + template + auto make_row_extractor(const I *tableInfo) { + using builder_t = row_extractor_builder; + return builder_t{}(tableInfo); + } + + } + +} + +// #include "error_code.h" + +// #include "type_printer.h" + +// #include "tuple_helper.h" + +// #include "constraints.h" + +// #include "type_is_nullable.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" @@ -6623,6 +6897,8 @@ namespace sqlite_orm { // #include "error_code.h" +// #include "object_from_column_builder.h" + namespace sqlite_orm { namespace internal { @@ -6652,17 +6928,8 @@ namespace sqlite_orm { temp = std::make_unique(); auto &storage = this->view.storage; auto &impl = storage.template get_impl(); - auto index = 0; - impl.table.for_each_column([&index, &temp, this](auto &c) { - using field_type = typename std::decay::type::field_type; - auto value = row_extractor().extract(*this->stmt, index++); - if(c.member_pointer) { - auto member_pointer = c.member_pointer; - (*temp).*member_pointer = std::move(value); - } else { - ((*temp).*(c.setter))(std::move(value)); - } - }); + object_from_column_builder builder{*temp, *this->stmt}; + impl.table.for_each_column(builder); } public: @@ -6917,22 +7184,33 @@ namespace sqlite_orm { expression_type t; - prepared_statement_t(T t_, sqlite3_stmt *stmt, connection_ref con_) : - prepared_statement_base{stmt, std::move(con_)}, t(std::move(t_)) {} + prepared_statement_t(T t_, sqlite3_stmt *stmt_, connection_ref con_) : + prepared_statement_base{stmt_, std::move(con_)}, t(std::move(t_)) {} }; - template + template + struct is_prepared_statement : std::false_type {}; + + template + struct is_prepared_statement> : std::true_type {}; + + /** + * 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 + template struct get_all_pointer_t { using type = T; + using return_type = R; using conditions_type = std::tuple; @@ -6940,9 +7218,10 @@ namespace sqlite_orm { }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template + template struct get_all_optional_t { using type = T; + using return_type = R; using conditions_type = std::tuple; @@ -7071,8 +7350,8 @@ namespace sqlite_orm { * Create a replace statement. * T is an object type mapped to a storage. * Usage: storage.replace(myUserInstance); - * Parameter obj is accepted by value. Is you want to accept it by ref - * the use std::ref function: storage.replace(std::ref(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) { @@ -7083,8 +7362,8 @@ namespace sqlite_orm { * Create an insert statement. * T is an object type mapped to a storage. * Usage: storage.insert(myUserInstance); - * Parameter obj is accepted by value. Is you want to accept it by ref - * the use std::ref function: storage.insert(std::ref(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) { @@ -7096,8 +7375,8 @@ namespace sqlite_orm { * 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. Is you want to accept it by ref - * the use std::ref function: storage.insert(std::ref(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) { @@ -7119,8 +7398,8 @@ namespace sqlite_orm { * Create an update statement. * T is an object type mapped to a storage. * Usage: storage.update(myUserInstance); - * Parameter obj is accepted by value. Is you want to accept it by ref - * the use std::ref function: storage.update(std::ref(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) { @@ -7181,7 +7460,21 @@ namespace sqlite_orm { * Usage: storage.get_all(...); */ template - internal::get_all_t get_all(Args... args) { + internal::get_all_t, Args...> get_all(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + args_tuple conditions{std::forward(args)...}; + return {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)...}; @@ -7206,8 +7499,23 @@ namespace sqlite_orm { * Usage: storage.get_all_pointer(...); */ template - internal::get_all_pointer_t get_all_pointer(Args... args) { - std::tuple conditions{std::forward(args)...}; + 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 {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 {move(conditions)}; } @@ -7218,13 +7526,63 @@ namespace sqlite_orm { * Usage: storage.get_all_optional(...); */ template - internal::get_all_optional_t get_all_optional(Args... args) { - std::tuple conditions{std::forward(args)...}; + 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 {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 {move(conditions)}; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED } +// #include "values.h" + +#include // std::vector +#include +#include // std::tuple + +namespace sqlite_orm { + + namespace internal { + + template + struct values_t { + std::tuple tuple; + }; + + template + struct dynamic_values_t { + std::vector vector; + }; + + } + + template + internal::values_t values(Args... args) { + return {{std::forward(args)...}}; + } + + template + internal::dynamic_values_t values(std::vector vector) { + return {{move(vector)}}; + } + +} + namespace sqlite_orm { namespace internal { @@ -7271,8 +7629,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::where_t; + struct ast_iterator, void> { + using node_type = where_t; template void operator()(const node_type &where, const L &l) const { @@ -7281,9 +7639,7 @@ namespace sqlite_orm { }; template - struct ast_iterator< - T, - typename std::enable_if::value>::type> { + struct ast_iterator::value>::type> { using node_type = T; template @@ -7315,8 +7671,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::in_t; + struct ast_iterator, void> { + using node_type = in_t; template void operator()(const node_type &in, const C &l) const { @@ -7369,9 +7725,9 @@ namespace sqlite_orm { } }; - template - struct ast_iterator, void> { - using node_type = get_all_t; + template + struct ast_iterator, void> { + using node_type = get_all_t; template void operator()(const node_type &get, const L &l) const { @@ -7445,8 +7801,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::having_t; + struct ast_iterator, void> { + using node_type = having_t; template void operator()(const node_type &hav, const L &l) const { @@ -7455,8 +7811,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::cast_t; + struct ast_iterator, void> { + using node_type = cast_t; template void operator()(const node_type &c, const L &l) const { @@ -7465,8 +7821,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::exists_t; + struct ast_iterator, void> { + using node_type = exists_t; template void operator()(const node_type &e, const L &l) const { @@ -7475,8 +7831,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::like_t; + struct ast_iterator, void> { + using node_type = like_t; template void operator()(const node_type &lk, const L &l) const { @@ -7489,8 +7845,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::glob_t; + struct ast_iterator, void> { + using node_type = glob_t; template void operator()(const node_type &lk, const L &l) const { @@ -7500,8 +7856,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::between_t; + struct ast_iterator, void> { + using node_type = between_t; template void operator()(const node_type &b, const L &l) const { @@ -7512,8 +7868,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::named_collate; + struct ast_iterator, void> { + using node_type = named_collate; template void operator()(const node_type &col, const L &l) const { @@ -7522,8 +7878,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::negated_condition_t; + struct ast_iterator, void> { + using node_type = negated_condition_t; template void operator()(const node_type &neg, const L &l) const { @@ -7532,8 +7888,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::is_null_t; + struct ast_iterator, void> { + using node_type = is_null_t; template void operator()(const node_type &i, const L &l) const { @@ -7542,8 +7898,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::is_not_null_t; + struct ast_iterator, void> { + using node_type = is_not_null_t; template void operator()(const node_type &i, const L &l) const { @@ -7552,8 +7908,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = core_functions::core_function_t; + struct ast_iterator, void> { + using node_type = core_function_t; template void operator()(const node_type &f, const L &l) const { @@ -7562,8 +7918,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::left_join_t; + struct ast_iterator, void> { + using node_type = left_join_t; template void operator()(const node_type &j, const L &l) const { @@ -7572,8 +7928,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::on_t; + struct ast_iterator, void> { + using node_type = on_t; template void operator()(const node_type &o, const L &l) const { @@ -7582,8 +7938,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::join_t; + struct ast_iterator, void> { + using node_type = join_t; template void operator()(const node_type &j, const L &l) const { @@ -7592,8 +7948,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::left_outer_join_t; + struct ast_iterator, void> { + using node_type = left_outer_join_t; template void operator()(const node_type &j, const L &l) const { @@ -7602,8 +7958,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::inner_join_t; + struct ast_iterator, void> { + using node_type = inner_join_t; template void operator()(const node_type &j, const L &l) const { @@ -7617,8 +7973,8 @@ namespace sqlite_orm { template void operator()(const node_type &c, const L &l) const { - c.case_expression.apply([&l](auto &c) { - iterate_ast(c, l); + c.case_expression.apply([&l](auto &c_) { + iterate_ast(c_, l); }); iterate_tuple(c.args, [&l](auto &pair) { iterate_ast(pair.first, l); @@ -7641,8 +7997,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::limit_t; + struct ast_iterator, void> { + using node_type = limit_t; template void operator()(const node_type &a, const L &l) const { @@ -7651,8 +8007,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::limit_t; + struct ast_iterator, void> { + using node_type = limit_t; template void operator()(const node_type &a, const L &l) const { @@ -7664,8 +8020,8 @@ namespace sqlite_orm { }; template - struct ast_iterator, void> { - using node_type = conditions::limit_t; + struct ast_iterator, void> { + using node_type = limit_t; template void operator()(const node_type &a, const L &l) const { @@ -7675,6 +8031,67 @@ namespace sqlite_orm { iterate_ast(a.lim, l); } }; + + template + struct ast_iterator, void> { + using node_type = distinct_t; + + template + void operator()(const node_type &a, const L &l) const { + iterate_ast(a.t, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = all_t; + + template + void operator()(const node_type &a, const L &l) const { + iterate_ast(a.t, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = bitwise_not_t; + + template + void operator()(const node_type &a, const L &l) const { + iterate_ast(a.argument, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = values_t; + + template + void operator()(const node_type &node, const L &l) const { + iterate_ast(node.tuple, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = dynamic_values_t; + + template + void operator()(const node_type &node, const L &l) const { + iterate_ast(node.vector, l); + } + }; + + template + struct ast_iterator, void> { + using node_type = collate_t; + + template + void operator()(const node_type &node, const L &l) const { + iterate_ast(node.expr, l); + } + }; + } } @@ -7694,7 +8111,7 @@ namespace sqlite_orm { storage_type &storage; connection_ref connection; - get_all_t args; + 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_)...)} {} @@ -7714,7 +8131,11 @@ namespace sqlite_orm { iterator_t begin() { sqlite3_stmt *stmt = nullptr; auto db = this->connection.get(); - auto query = this->storage.string_from_expression(this->args, false); + using context_t = serializator_context; + context_t context{this->storage.impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(this->args, context); auto ret = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); if(ret == SQLITE_OK) { auto index = 1; @@ -7778,6 +8199,14 @@ namespace sqlite_orm { 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"); } @@ -7826,7 +8255,7 @@ namespace sqlite_orm { T get_pragma(const std::string &name) { auto connection = this->get_connection(); auto query = "PRAGMA " + name; - T res; + T result; auto db = connection.get(); auto rc = sqlite3_exec( db, @@ -7838,10 +8267,10 @@ namespace sqlite_orm { } return 0; }, - &res, + &result, nullptr); if(rc == SQLITE_OK) { - return res; + return result; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -8130,14 +8559,14 @@ namespace sqlite_orm { const std::string &zSourceName, std::unique_ptr holder_) : handle(sqlite3_backup_init(to_.get(), zDestName.c_str(), from_.get(), zSourceName.c_str())), - holder(move(holder_)), to(to_), from(from_) { + to(to_), from(from_), holder(move(holder_)) { if(!this->handle) { throw std::system_error(std::make_error_code(orm_error_code::failed_to_init_a_backup)); } } backup_t(backup_t &&other) : - handle(other.handle), holder(move(other.holder)), to(other.to), from(other.from) { + handle(other.handle), to(other.to), from(other.from), holder(move(other.holder)) { other.handle = nullptr; } @@ -8171,9 +8600,9 @@ namespace sqlite_orm { protected: sqlite3_backup *handle = nullptr; - std::unique_ptr holder; connection_ref to; connection_ref from; + std::unique_ptr holder; }; } } @@ -8244,6 +8673,16 @@ namespace sqlite_orm { this->drop_table_internal(tableName, con.get()); } + /** + * Rename table named `from` to `to`. + */ + void rename_table(const std::string &from, const std::string &to) { + auto con = this->get_connection(); + std::stringstream ss; + ss << "ALTER TABLE '" << from << "' RENAME TO '" << to << "'"; + this->perform_query_without_result(ss.str(), con.get()); + } + /** * sqlite3_changes function. */ @@ -8277,15 +8716,13 @@ namespace sqlite_orm { return sqlite3_libversion(); } - bool transaction(std::function f) { + bool transaction(const std::function &f) { this->begin_transaction(); - auto con = this->get_connection(); - auto db = con.get(); auto shouldCommit = f(); if(shouldCommit) { - this->commit(db); + this->commit(); } else { - this->rollback(db); + this->rollback(); } return shouldCommit; } @@ -8323,10 +8760,10 @@ namespace sqlite_orm { db, sql.c_str(), [](void *data, int argc, char **argv, char * * /*columnName*/) -> int { - auto &tableNames = *(data_t *)data; + auto &tableNames_ = *(data_t *)data; for(int i = 0; i < argc; i++) { if(argv[i]) { - tableNames.push_back(argv[i]); + tableNames_.push_back(argv[i]); } } return 0; @@ -8420,7 +8857,8 @@ namespace sqlite_orm { backup_t make_backup_to(const std::string &filename) { auto holder = std::make_unique(filename); - return {connection_ref{*holder}, "main", this->get_connection(), "main", move(holder)}; + connection_ref conRef{*holder}; + return {conRef, "main", this->get_connection(), "main", move(holder)}; } backup_t make_backup_to(storage_base &other) { @@ -8429,7 +8867,8 @@ namespace sqlite_orm { backup_t make_backup_from(const std::string &filename) { auto holder = std::make_unique(filename); - return {this->get_connection(), "main", connection_ref{*holder}, "main", move(holder)}; + connection_ref conRef{*holder}; + return {this->get_connection(), "main", conRef, "main", move(holder)}; } backup_t make_backup_from(storage_base &other) { @@ -8440,6 +8879,27 @@ namespace sqlite_orm { 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; + } + + int busy_handler(std::function handler) { + _busy_handler = 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(const std::string &filename_, int foreignKeysCount) : pragma(std::bind(&storage_base::get_connection, this)), @@ -8477,6 +8937,7 @@ namespace sqlite_orm { std::unique_ptr connection; std::map collatingFunctions; const int cachedForeignKeysCount; + std::function _busy_handler; connection_ref get_connection() { connection_ref res{*this->connection}; @@ -8501,7 +8962,7 @@ namespace sqlite_orm { bool foreign_keys(sqlite3 *db) { std::string query = "PRAGMA foreign_keys"; - auto res = false; + auto result = false; auto rc = sqlite3_exec( db, query.c_str(), @@ -8512,13 +8973,13 @@ namespace sqlite_orm { } return 0; }, - &res, + &result, nullptr); if(rc != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } - return res; + return result; } #endif @@ -8549,6 +9010,10 @@ namespace sqlite_orm { sqlite3_limit(db, p.first, p.second); } + if(_busy_handler) { + sqlite3_busy_handler(this->connection->get(), busy_handler_callback, this); + } + if(this->on_open) { this->on_open(db); } @@ -8612,7 +9077,7 @@ namespace sqlite_orm { } std::string current_timestamp(sqlite3 *db) { - std::string res; + std::string result; std::stringstream ss; ss << "SELECT CURRENT_TIMESTAMP"; auto query = ss.str(); @@ -8628,19 +9093,22 @@ namespace sqlite_orm { } return 0; }, - &res, + &result, nullptr); if(rc != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); } - return res; + return result; } void drop_table_internal(const std::string &tableName, sqlite3 *db) { std::stringstream ss; ss << "DROP TABLE '" << tableName + "'"; - auto query = ss.str(); + this->perform_query_without_result(ss.str(), db); + } + + void perform_query_without_result(const std::string &query, sqlite3 *db) { sqlite3_stmt *stmt; if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { statement_finalizer finalizer{stmt}; @@ -8656,52 +9124,26 @@ namespace sqlite_orm { } } - template - std::string process_order_by(const conditions::dynamic_order_by_t &orderBy) const { - std::vector expressions; - for(auto &entry: orderBy) { - std::string entryString; - { - std::stringstream 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; - } - entryString = ss.str(); - } - expressions.push_back(move(entryString)); - }; - std::stringstream ss; - ss << static_cast(orderBy) << " "; - for(size_t i = 0; i < expressions.size(); ++i) { - ss << expressions[i]; - if(i < expressions.size() - 1) { - ss << ", "; - } - } - ss << " "; - return 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; + } + } + // returns foreign keys count in storage definition template static int foreign_keys_count(T &storageImpl) { auto res = 0; - storageImpl.for_each([&res](auto impl) { - res += impl->foreign_keys_count(); + storageImpl.for_each([&res](auto &impl) { + res += impl.foreign_keys_count(); }); return res; } @@ -8835,262 +9277,368 @@ namespace sqlite_orm { } } -namespace sqlite_orm { +// #include "statement_serializator.h" - namespace conditions { +#include // std::stringstream +#include // std::string +#include // std::enable_if +#include // std::vector +#include // std::iter_swap - template - struct dynamic_order_by_t; - } +// #include "core_functions.h" - namespace internal { +// #include "constraints.h" - /** - * 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 impl_type = storage_impl; +// #include "conditions.h" - /** - * @param filename database filename. - * @param impl_ storage_impl head - */ - storage_t(const std::string &filename, impl_type impl_) : - storage_base{filename, foreign_keys_count(impl_)}, impl(std::move(impl_)) {} +// #include "column.h" - storage_t(const storage_t &other) : storage_base(other), impl(other.impl) {} +// #include "rowid.h" - protected: - impl_type impl; +// #include "type_printer.h" - template - friend struct view_t; +// #include "table_name_collector.h" - template - friend struct conditions::dynamic_order_by_t; +#include // std::set +#include // std::string +#include // std::function +#include // std::type_index - template - friend struct iterator_t; +// #include "select_constraints.h" - template - std::string serialize_column_schema(const internal::column_t &c) { - std::stringstream ss; - ss << "'" << c.name << "' "; - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - using constraints_type = typename column_type::constraints_type; - ss << type_printer().print() << " "; - { - std::vector constraintsStrings; - constexpr const size_t constraintsCount = std::tuple_size::value; - constraintsStrings.reserve(constraintsCount); - int primaryKeyIndex = -1; - int autoincrementIndex = -1; - int tupleIndex = 0; - iterate_tuple(c.constraints, - [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex](auto &v) { - using constraint_type = typename std::decay::type; - constraintsStrings.push_back(serialize(v)); - if(is_primary_key::value) { - primaryKeyIndex = tupleIndex; - } else if(std::is_same::value) { - autoincrementIndex = tupleIndex; - } - ++tupleIndex; - }); - if(primaryKeyIndex != -1 && autoincrementIndex != -1 && autoincrementIndex < primaryKeyIndex) { - iter_swap(constraintsStrings.begin() + primaryKeyIndex, - constraintsStrings.begin() + autoincrementIndex); - } - for(auto &str: constraintsStrings) { - ss << str << ' '; - } +// #include "alias.h" + +// #include "core_functions.h" + +namespace sqlite_orm { + + namespace internal { + + struct table_name_collector { + using table_name_set = std::set>; + using find_table_name_t = std::function; + + find_table_name_t find_table_name; + mutable table_name_set table_names; + + template + table_name_set operator()(const T &) const { + return {}; + } + + template + void operator()(F O::*, std::string alias = {}) const { + if(this->find_table_name) { + table_names.insert(std::make_pair(this->find_table_name(typeid(O)), move(alias))); } - if(c.not_null()) { - ss << "NOT NULL "; + } + + template + void operator()(const column_pointer &) const { + if(this->find_table_name) { + table_names.insert({this->find_table_name(typeid(T)), ""}); } - return ss.str(); } - template - std::string serialize_column_schema(const constraints::primary_key_t &fk) { - std::stringstream ss; - ss << static_cast(fk) << " ("; - std::vector columnNames; - columnNames.reserve(std::tuple_size::value); - iterate_tuple(fk.columns, [&columnNames, this](auto &c) { - columnNames.push_back(this->impl.column_name(c)); - }); - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << columnNames[i]; - if(i < columnNames.size() - 1) { - ss << ", "; + template + void operator()(const alias_column_t &a) const { + (*this)(a.column, alias_extractor::get()); + } + + template + void operator()(const count_asterisk_t &) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + if(!tableName.empty()) { + table_names.insert(std::make_pair(move(tableName), "")); } } - ss << ") "; - return ss.str(); } -#if SQLITE_VERSION_NUMBER >= 3006019 + template + void operator()(const asterisk_t &) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + table_names.insert(std::make_pair(move(tableName), "")); + } + } - template - std::string - serialize_column_schema(const constraints::foreign_key_t, std::tuple> &fk) { - std::stringstream ss; - std::vector columnNames; - using columns_type_t = typename std::decay::type::columns_type; - constexpr const size_t columnsCount = std::tuple_size::value; - columnNames.reserve(columnsCount); - iterate_tuple(fk.columns, [&columnNames, this](auto &v) { - columnNames.push_back(this->impl.column_name(v)); - }); - ss << "FOREIGN KEY( "; - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << columnNames[i]; - if(i < columnNames.size() - 1) { - ss << ","; - } - ss << " "; + template + void operator()(const object_t &) const { + if(this->find_table_name) { + auto tableName = this->find_table_name(typeid(T)); + table_names.insert(std::make_pair(move(tableName), "")); } - ss << ") REFERENCES "; - std::vector referencesNames; - using references_type_t = typename std::decay::type::references_type; - constexpr const size_t referencesCount = std::tuple_size::value; - referencesNames.reserve(referencesCount); - { - using first_reference_t = typename std::tuple_element<0, references_type_t>::type; - using first_reference_mapped_type = typename internal::table_type::type; - auto refTableName = this->impl.template find_table_name(); - ss << refTableName << " "; + } + }; + + } + +} + +// #include "column_names_getter.h" + +#include // std::string +#include // std::vector +#include // std::reference_wrapper + +// #include "error_code.h" + +// #include "select_constraints.h" + +namespace sqlite_orm { + + namespace internal { + + template + std::string serialize(const T &t, const C &context); + + template + struct column_names_getter { + using expression_type = T; + + template + std::vector operator()(const expression_type &t, const C &context) { + auto newContext = context; + newContext.skip_table_name = false; + auto columnName = serialize(t, newContext); + if(columnName.length()) { + return {move(columnName)}; + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); } - iterate_tuple(fk.references, [&referencesNames, this](auto &v) { - referencesNames.push_back(this->impl.column_name(v)); - }); - ss << "( "; - for(size_t i = 0; i < referencesNames.size(); ++i) { - ss << referencesNames[i]; - if(i < referencesNames.size() - 1) { - ss << ","; + } + }; + + template + std::vector get_column_names(const T &t, const C &context) { + column_names_getter serializator; + return serializator(t, context); + } + + template + struct column_names_getter, void> { + using expression_type = std::reference_wrapper; + + template + std::vector operator()(const expression_type &expression, const C &context) { + return get_column_names(expression.get(), context); + } + }; + + template + struct column_names_getter, void> { + using expression_type = asterisk_t; + + template + std::vector operator()(const expression_type &, const C &) { + std::vector res; + res.push_back("*"); + return res; + } + }; + + template + struct column_names_getter, void> { + using expression_type = object_t; + + template + std::vector operator()(const expression_type &, const C &) { + std::vector res; + res.push_back("*"); + return res; + } + }; + + template + struct column_names_getter, void> { + using expression_type = columns_t; + + template + std::vector operator()(const expression_type &cols, const C &context) { + std::vector columnNames; + columnNames.reserve(static_cast(cols.count)); + auto newContext = context; + newContext.skip_table_name = false; + iterate_tuple(cols.columns, [&columnNames, &newContext](auto &m) { + auto columnName = serialize(m, newContext); + if(columnName.length()) { + columnNames.push_back(columnName); + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); } - ss << " "; - } - ss << ") "; - if(fk.on_update) { - ss << static_cast(fk.on_update) << " " << fk.on_update._action << " "; + }); + return columnNames; + } + }; + + } +} + +// #include "order_by_serializator.h" + +#include // std::string +#include // std::vector +#include // std::stringstream + +namespace sqlite_orm { + + namespace internal { + + template + struct order_by_serializator; + + template + std::string serialize_order_by(const T &t, const C &context) { + order_by_serializator serializator; + return serializator(t, context); + } + + template + struct order_by_serializator, void> { + using statement_type = order_by_t; + + template + std::string operator()(const statement_type &orderBy, const C &context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + auto columnName = serialize(orderBy.o, newContext); + ss << columnName << " "; + if(orderBy._collate_argument.length()) { + ss << "COLLATE " << orderBy._collate_argument << " "; } - if(fk.on_delete) { - ss << static_cast(fk.on_delete) << " " << fk.on_delete._action << " "; + switch(orderBy.asc_desc) { + case 1: + ss << "ASC"; + break; + case -1: + ss << "DESC"; + break; } return ss.str(); } -#endif + }; - template - void create_table(sqlite3 *db, const std::string &tableName, I *impl) { + template + struct order_by_serializator, void> { + using statement_type = dynamic_order_by_t; + + template + std::string operator()(const statement_type &orderBy, const C &) const { + std::vector expressions; + for(auto &entry: orderBy) { + std::string entryString; + { + std::stringstream 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; + } + entryString = ss.str(); + } + expressions.push_back(move(entryString)); + }; std::stringstream ss; - ss << "CREATE TABLE '" << tableName << "' ( "; - auto columnsCount = impl->table.columns_count; - auto index = 0; - impl->table.for_each_column_with_constraints([columnsCount, &index, &ss, this](auto &c) { - ss << this->serialize_column_schema(c); - if(index < columnsCount - 1) { + ss << static_cast(orderBy) << " "; + for(size_t i = 0; i < expressions.size(); ++i) { + ss << expressions[i]; + if(i < expressions.size() - 1) { ss << ", "; } - index++; - }); - ss << ") "; - if(impl->table._without_rowid) { - ss << "WITHOUT ROWID "; - } - auto query = ss.str(); - sqlite3_stmt *stmt; - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer{stmt}; - if(sqlite3_step(stmt) == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); } + ss << " "; + return ss.str(); } + }; - template - void backup_table(sqlite3 *db, I *impl) { + } +} - // 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 = impl->table.name + "_backup"; - if(impl->table_exists(backupTableName, db)) { - int suffix = 1; - do { - std::stringstream stream; - stream << suffix; - auto anotherBackupTableName = backupTableName + stream.str(); - if(!impl->table_exists(anotherBackupTableName, db)) { - backupTableName = anotherBackupTableName; - break; - } - ++suffix; - } while(true); - } +// #include "values.h" - this->create_table(db, backupTableName, impl); +// #include "table_type.h" - impl->copy_table(db, backupTableName); +// #include "indexed_column.h" - this->drop_table_internal(impl->table.name, db); +namespace sqlite_orm { - impl->rename_table(db, backupTableName, impl->table.name); - } + namespace internal { - template - void assert_mapped_type() const { - using mapped_types_tuples = std::tuple; - static_assert(tuple_helper::has_type::value, "type is not mapped to a storage"); - } + template + struct statement_serializator; - template - auto &get_impl() const { - return this->impl.template get_impl(); - } + template + std::string serialize(const T &t, const C &context) { + statement_serializator serializator; + return serializator(t, context); + } - template - typename std::enable_if::value, std::string>::type - string_from_expression(const T &, bool /*noTableName*/) const { - return "?"; + template + struct statement_serializator::value>::type> { + using statement_type = T; + + template + std::string operator()(const statement_type &statement, const C &context) { + if(context.replace_bindable_with_question) { + return "?"; + } else { + return field_printer{}(statement); + } } + }; - template - std::string string_from_expression(std::reference_wrapper ref, bool noTableName) const { - return this->string_from_expression(ref.get(), noTableName); + template + struct statement_serializator, void> { + using statement_type = std::reference_wrapper; + + template + std::string operator()(const statement_type &s, const C &context) { + return serialize(s.get(), context); } + }; + + template<> + struct statement_serializator { + using statement_type = std::nullptr_t; - std::string string_from_expression(std::nullptr_t, bool /*noTableName*/) const { + template + std::string operator()(const statement_type &, const C &) { return "?"; } + }; - template - std::string string_from_expression(const alias_holder &, bool /*noTableName*/) const { + template + struct statement_serializator, void> { + using statement_type = alias_holder; + + template + std::string operator()(const statement_type &, const C &) { return T::get(); } + }; - template - std::string string_from_expression(const core_functions::core_function_t &c, - bool noTableName) const { + template + struct statement_serializator, void> { + using statement_type = core_function_t; + + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; ss << static_cast(c) << "("; std::vector args; using args_type = typename std::decay::type::args_type; args.reserve(std::tuple_size::value); - iterate_tuple(c.args, [&args, this, noTableName](auto &v) { - args.push_back(this->string_from_expression(v, noTableName)); + iterate_tuple(c.args, [&args, &context](auto &v) { + args.push_back(serialize(v, context)); }); for(size_t i = 0; i < args.size(); ++i) { ss << args[i]; @@ -9101,446 +9649,783 @@ namespace sqlite_orm { ss << ")"; return ss.str(); } + }; + + template + struct statement_serializator, void> { + using statement_type = as_t; - template - std::string string_from_expression(const as_t &als, bool noTableName) const { + template + std::string operator()(const statement_type &c, const C &context) const { auto tableAliasString = alias_extractor::get(); - return this->string_from_expression(als.expression, noTableName) + " AS " + tableAliasString; + return serialize(c.expression, context) + " AS " + tableAliasString; } + }; - template - std::string string_from_expression(const alias_column_t &als, bool noTableName) const { + template + struct statement_serializator, void> { + using statement_type = alias_column_t; + + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - if(!noTableName) { + if(!context.skip_table_name) { ss << "'" << T::get() << "'."; } - ss << this->string_from_expression(als.column, true); + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(c.column, newContext); return ss.str(); } + }; - std::string string_from_expression(const std::string &, bool /*noTableName*/) const { - return "?"; + template<> + struct statement_serializator { + using statement_type = std::string; + + template + std::string operator()(const statement_type &c, const C &context) const { + if(context.replace_bindable_with_question) { + return "?"; + } else { + return "\"" + c + "\""; + } } + }; - std::string string_from_expression(const char *, bool /*noTableName*/) const { - return "?"; + template<> + struct statement_serializator { + using statement_type = const char *; + + template + std::string operator()(const char *c, const C &context) const { + if(context.replace_bindable_with_question) { + return "?"; + } else { + return std::string("'") + c + "'"; + } } + }; - template - std::string string_from_expression(F O::*m, bool noTableName) const { + template + struct statement_serializator { + using statement_type = F O::*; + + template + std::string operator()(const statement_type &m, const C &context) const { std::stringstream ss; - if(!noTableName) { - ss << "'" << this->impl.template find_table_name() << "'."; + if(!context.skip_table_name) { + ss << "\"" << context.impl.find_table_name(typeid(O)) << "\"."; } - ss << "\"" << this->impl.column_name(m) << "\""; + ss << "\"" << context.column_name(m) << "\""; return ss.str(); } + }; - std::string string_from_expression(const rowid_t &rid, bool /*noTableName*/) const { - return static_cast(rid); + template<> + struct statement_serializator { + using statement_type = rowid_t; + + template + std::string operator()(const statement_type &s, const C &) { + return static_cast(s); } + }; + + template<> + struct statement_serializator { + using statement_type = oid_t; - std::string string_from_expression(const oid_t &rid, bool /*noTableName*/) const { - return static_cast(rid); + template + std::string operator()(const statement_type &s, const C &) { + return static_cast(s); } + }; + + template<> + struct statement_serializator<_rowid_t, void> { + using statement_type = _rowid_t; - std::string string_from_expression(const _rowid_t &rid, bool /*noTableName*/) const { - return static_cast(rid); + template + std::string operator()(const statement_type &s, const C &) { + return static_cast(s); } + }; - template - std::string string_from_expression(const table_rowid_t &rid, bool noTableName) const { + template + struct statement_serializator, void> { + using statement_type = table_rowid_t; + + template + std::string operator()(const statement_type &s, const C &context) { + std::stringstream ss; + if(!context.skip_table_name) { + ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; + } + ss << static_cast(s); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = table_oid_t; + + template + std::string operator()(const statement_type &s, const C &context) { + std::stringstream ss; + if(!context.skip_table_name) { + ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; + } + ss << static_cast(s); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = table__rowid_t; + + template + std::string operator()(const statement_type &s, const C &context) { + std::stringstream ss; + if(!context.skip_table_name) { + ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; + } + ss << static_cast(s); + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = binary_operator; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto lhs = serialize(c.lhs, context); + auto rhs = serialize(c.rhs, context); + std::stringstream ss; + ss << "(" << lhs << " " << static_cast(c) << " " << rhs << ")"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = count_asterisk_t; + + template + std::string operator()(const statement_type &, const C &context) const { + return serialize(count_asterisk_without_type{}, context); + } + }; + + template<> + struct statement_serializator { + using statement_type = count_asterisk_without_type; + + template + std::string operator()(const statement_type &c, const C &) const { + std::stringstream ss; + ss << static_cast(c) << "(*)"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = distinct_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + auto expr = serialize(c.t, context); + ss << static_cast(c) << "(" << expr << ")"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = all_t; + + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - if(!noTableName) { - ss << "'" << this->impl.template find_table_name() << "'."; - } - ss << static_cast(rid); + auto expr = serialize(c.t, context); + ss << static_cast(c) << "(" << expr << ")"; return ss.str(); } + }; - template - std::string string_from_expression(const table_oid_t &rid, bool noTableName) const { + template + struct statement_serializator, void> { + using statement_type = column_pointer; + + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - if(!noTableName) { - ss << "'" << this->impl.template find_table_name() << "'."; + if(!context.skip_table_name) { + ss << "'" << context.impl.find_table_name(typeid(T)) << "'."; } - ss << static_cast(rid); + ss << "\"" << context.impl.column_name_simple(c.field) << "\""; return ss.str(); } + }; - template - std::string string_from_expression(const table__rowid_t &rid, bool noTableName) const { + template + struct statement_serializator, void> { + using statement_type = cast_t; + + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - if(!noTableName) { - ss << "'" << this->impl.template find_table_name() << "'."; - } - ss << static_cast(rid); + ss << static_cast(c) << " ("; + ss << serialize(c.expression, context) << " AS " << type_printer().print() << ")"; return ss.str(); } + }; - template - std::string string_from_expression(const aggregate_functions::group_concat_double_t &f, - bool noTableName) const { + template + struct statement_serializator::value>::type> { + using statement_type = T; + + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - auto expr = this->string_from_expression(f.arg, noTableName); - auto expr2 = this->string_from_expression(f.y, noTableName); - ss << static_cast(f) << "(" << expr << ", " << expr2 << ")"; + ss << serialize(c.left, context) << " "; + ss << static_cast(c) << " "; + ss << serialize(c.right, context); return ss.str(); } + }; - template - std::string string_from_expression(const aggregate_functions::group_concat_single_t &f, - bool noTableName) const { + template + struct statement_serializator, void> { + using statement_type = simple_case_t; + + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - auto expr = this->string_from_expression(f.arg, noTableName); - ss << static_cast(f) << "(" << expr << ")"; + 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_serializator, void> { + using statement_type = is_null_t; - template - std::string string_from_expression(const binary_operator &f, bool noTableName) const { + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - auto lhs = this->string_from_expression(f.lhs, noTableName); - auto rhs = this->string_from_expression(f.rhs, noTableName); - ss << "(" << lhs << " " << static_cast(f) << " " << rhs << ")"; + ss << serialize(c.t, context) << " " << static_cast(c); return ss.str(); } + }; - template - std::string string_from_expression(const aggregate_functions::min_t &f, bool noTableName) const { + template + struct statement_serializator, void> { + using statement_type = is_not_null_t; + + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - auto expr = this->string_from_expression(f.arg, noTableName); - ss << static_cast(f) << "(" << expr << ")"; + ss << serialize(c.t, context) << " " << static_cast(c); return ss.str(); } + }; - template - std::string string_from_expression(const aggregate_functions::max_t &f, bool noTableName) const { + template + struct statement_serializator, void> { + using statement_type = bitwise_not_t; + + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - auto expr = this->string_from_expression(f.arg, noTableName); - ss << static_cast(f) << "(" << expr << ")"; + ss << static_cast(c) << " "; + auto cString = serialize(c.argument, context); + ss << " (" << cString << " )"; return ss.str(); } + }; - template - std::string string_from_expression(const aggregate_functions::total_t &f, bool noTableName) const { + template + struct statement_serializator, void> { + using statement_type = negated_condition_t; + + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - auto expr = this->string_from_expression(f.arg, noTableName); - ss << static_cast(f) << "(" << expr << ")"; + ss << static_cast(c) << " "; + auto cString = serialize(c.c, context); + ss << " (" << cString << " )"; return ss.str(); } + }; - template - std::string string_from_expression(const aggregate_functions::sum_t &f, bool noTableName) const { + template + struct statement_serializator::value>::type> { + using statement_type = T; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto leftString = serialize(c.l, context); + auto rightString = serialize(c.r, context); std::stringstream ss; - auto expr = this->string_from_expression(f.arg, noTableName); - ss << static_cast(f) << "(" << expr << ")"; + if(context.use_parentheses) { + ss << "("; + } + ss << leftString << " " << static_cast(c) << " " << rightString; + if(context.use_parentheses) { + ss << ")"; + } return ss.str(); } + }; - template - std::string string_from_expression(const aggregate_functions::count_asterisk_t &, - bool noTableName) const { - return this->string_from_expression(aggregate_functions::count_asterisk_without_type{}, noTableName); + template + struct statement_serializator, void> { + using statement_type = named_collate; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto newContext = context; + newContext.use_parentheses = false; + auto res = serialize(c.expr, newContext); + return res + " " + static_cast(c); + } + }; + + template + struct statement_serializator, void> { + using statement_type = collate_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto newContext = context; + newContext.use_parentheses = false; + auto res = serialize(c.expr, newContext); + return res + " " + static_cast(c); } + }; + + template + struct statement_serializator, void> { + using statement_type = in_t; - std::string string_from_expression(const aggregate_functions::count_asterisk_without_type &f, - bool /*noTableName*/) const { + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - ss << static_cast(f) << "(*)"; + auto leftString = serialize(c.l, context); + ss << leftString << " " << static_cast(c) << " "; + auto newContext = context; + newContext.use_parentheses = true; + ss << serialize(c.arg, newContext); return ss.str(); } + }; - template - std::string string_from_expression(const aggregate_functions::count_t &f, bool noTableName) const { + template + struct statement_serializator>, void> { + using statement_type = in_t>; + + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - auto expr = this->string_from_expression(f.arg, noTableName); - ss << static_cast(f) << "(" << expr << ")"; + auto leftString = serialize(c.l, context); + ss << leftString << " " << static_cast(c) << " ( "; + for(size_t index = 0; index < c.arg.size(); ++index) { + auto &value = c.arg[index]; + ss << " " << serialize(value, context); + if(index < c.arg.size() - 1) { + ss << ", "; + } + } + ss << " )"; return ss.str(); } + }; - template - std::string string_from_expression(const aggregate_functions::avg_t &a, bool noTableName) const { + template + struct statement_serializator, void> { + using statement_type = like_t; + + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - auto expr = this->string_from_expression(a.arg, noTableName); - ss << static_cast(a) << "(" << expr << ") "; + 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 - std::string string_from_expression(const distinct_t &f, bool noTableName) const { + template + struct statement_serializator, void> { + using statement_type = glob_t; + + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - auto expr = this->string_from_expression(f.t, noTableName); - ss << static_cast(f) << "(" << expr << ") "; + ss << serialize(c.arg, context) << " "; + ss << static_cast(c) << " "; + ss << serialize(c.pattern, context); return ss.str(); } + }; - template - std::string string_from_expression(const all_t &f, bool noTableName) const { + template + struct statement_serializator, void> { + using statement_type = between_t; + + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - auto expr = this->string_from_expression(f.t, noTableName); - ss << static_cast(f) << "(" << expr << ") "; + 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 - std::string string_from_expression(const column_pointer &c, bool noTableName) const { + template + struct statement_serializator, void> { + using statement_type = exists_t; + + template + std::string operator()(const statement_type &c, const C &context) const { std::stringstream ss; - if(!noTableName) { - ss << "'" << this->impl.template find_table_name() << "'."; - } - auto &impl = this->get_impl(); - ss << "\"" << impl.column_name_simple(c.field) << "\""; + ss << static_cast(c) << " "; + ss << serialize(c.t, context); return ss.str(); } + }; - template - std::vector get_column_names(const T &t) const { - auto columnName = this->string_from_expression(t, false); - if(columnName.length()) { - return {columnName}; - } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); - } + template<> + struct statement_serializator { + using statement_type = constraints::autoincrement_t; + + template + std::string operator()(const statement_type &c, const C &) const { + return static_cast(c); } + }; - template - std::vector get_column_names(std::reference_wrapper r) const { - return this->get_column_names(r.get()); + template + struct statement_serializator, void> { + using statement_type = constraints::primary_key_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto res = static_cast(c); + using columns_tuple = typename statement_type::columns_tuple; + auto columnsCount = std::tuple_size::value; + if(columnsCount) { + res += "("; + decltype(columnsCount) columnIndex = 0; + iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto &column) { + res += context.column_name(column); + if(columnIndex < columnsCount - 1) { + res += ", "; + } + ++columnIndex; + }); + res += ")"; + } + return res; } + }; - template - std::vector get_column_names(const internal::asterisk_t &) const { - std::vector res; - res.push_back("*"); + template + struct statement_serializator, void> { + using statement_type = constraints::unique_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + auto res = static_cast(c); + using columns_tuple = typename statement_type::columns_tuple; + auto columnsCount = std::tuple_size::value; + if(columnsCount) { + res += "("; + decltype(columnsCount) columnIndex = 0; + iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto &column) { + res += context.column_name(column); + if(columnIndex < columnsCount - 1) { + res += ", "; + } + ++columnIndex; + }); + res += ")"; + } return res; } + }; - template - std::vector get_column_names(const internal::columns_t &cols) const { - std::vector columnNames; - columnNames.reserve(static_cast(cols.count)); - iterate_tuple(cols.columns, [&columnNames, this](auto &m) { - auto columnName = this->string_from_expression(m, false); - if(columnName.length()) { - columnNames.push_back(columnName); - } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); - } - }); - return columnNames; + template<> + struct statement_serializator { + using statement_type = constraints::collate_t; + + template + std::string operator()(const statement_type &c, const C &) const { + return static_cast(c); } + }; - /** - * Takes select_t object and returns SELECT query string - */ - template - std::string string_from_expression(const internal::select_t &sel, bool /*noTableName*/) const { + template + struct statement_serializator, void> { + using statement_type = constraints::default_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + return static_cast(c) + " (" + serialize(c.value, context) + ")"; + } + }; + + template + struct statement_serializator, std::tuple>, void> { + using statement_type = constraints::foreign_key_t, std::tuple>; + + template + std::string operator()(const statement_type &fk, const C &context) const { std::stringstream ss; - if(!is_base_of_template::value) { - if(!sel.highest_level) { - ss << "( "; - } - ss << "SELECT "; - } - if(get_distinct(sel.col)) { - ss << static_cast(distinct(0)) << " "; - } - auto columnNames = this->get_column_names(sel.col); + std::vector columnNames; + using columns_type_t = typename std::decay::type::columns_type; + constexpr const size_t columnsCount = std::tuple_size::value; + columnNames.reserve(columnsCount); + iterate_tuple(fk.columns, [&columnNames, &context](auto &v) { + columnNames.push_back(context.impl.column_name(v)); + }); + ss << "FOREIGN KEY("; for(size_t i = 0; i < columnNames.size(); ++i) { - ss << columnNames[i]; + ss << "'" << columnNames[i] << "'"; if(i < columnNames.size() - 1) { - ss << ","; + ss << ", "; } - ss << " "; } - auto tableNamesSet = this->parse_table_name(sel.col); - internal::join_iterator()([&tableNamesSet, this](const auto &c) { - using original_join_type = typename std::decay::type::join_type::type; - using cross_join_type = typename internal::mapped_type_proxy::type; - auto crossJoinedTableName = this->impl.template find_table_name(); - auto tableAliasString = alias_extractor::get(); - std::pair tableNameWithAlias(std::move(crossJoinedTableName), - std::move(tableAliasString)); - tableNamesSet.erase(tableNameWithAlias); + ss << ") REFERENCES "; + std::vector referencesNames; + using references_type_t = typename std::decay::type::references_type; + constexpr const size_t referencesCount = std::tuple_size::value; + referencesNames.reserve(referencesCount); + { + using first_reference_t = typename std::tuple_element<0, references_type_t>::type; + using first_reference_mapped_type = typename internal::table_type::type; + auto refTableName = context.impl.find_table_name(typeid(first_reference_mapped_type)); + ss << '\'' << refTableName << '\''; + } + iterate_tuple(fk.references, [&referencesNames, &context](auto &v) { + referencesNames.push_back(context.impl.column_name(v)); }); - if(!tableNamesSet.empty()) { - ss << "FROM "; - std::vector> tableNames(tableNamesSet.begin(), - tableNamesSet.end()); - for(size_t i = 0; i < tableNames.size(); ++i) { - auto &tableNamePair = tableNames[i]; - ss << "'" << tableNamePair.first << "' "; - if(!tableNamePair.second.empty()) { - ss << tableNamePair.second << " "; - } - if(int(i) < int(tableNames.size()) - 1) { - ss << ","; - } - ss << " "; + ss << "("; + for(size_t i = 0; i < referencesNames.size(); ++i) { + ss << "'" << referencesNames[i] << "'"; + if(i < referencesNames.size() - 1) { + ss << ", "; + } + } + ss << ")"; + 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_serializator, void> { + using statement_type = constraints::check_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + return static_cast(c) + " " + serialize(c.expression, context); + } + }; + + template + struct statement_serializator, void> { + using statement_type = column_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << "'" << c.name << "' "; + using column_type = typename std::decay::type; + using field_type = typename column_type::field_type; + using constraints_type = typename column_type::constraints_type; + ss << type_printer().print() << " "; + { + std::vector constraintsStrings; + constexpr const size_t constraintsCount = std::tuple_size::value; + constraintsStrings.reserve(constraintsCount); + int primaryKeyIndex = -1; + int autoincrementIndex = -1; + int tupleIndex = 0; + iterate_tuple( + c.constraints, + [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context](auto &v) { + using constraint_type = typename std::decay::type; + constraintsStrings.push_back(serialize(v, context)); + if(is_primary_key::value) { + primaryKeyIndex = tupleIndex; + } else if(std::is_same::value) { + autoincrementIndex = tupleIndex; + } + ++tupleIndex; + }); + if(primaryKeyIndex != -1 && autoincrementIndex != -1 && autoincrementIndex < primaryKeyIndex) { + iter_swap(constraintsStrings.begin() + primaryKeyIndex, + constraintsStrings.begin() + autoincrementIndex); } - } - iterate_tuple(sel.conditions, [&ss, this](auto &v) { - this->process_single_condition(ss, v); - }); - if(!is_base_of_template::value) { - if(!sel.highest_level) { - ss << ") "; + for(auto &str: constraintsStrings) { + ss << str << ' '; } } - return ss.str(); - } - - // Common code for statements returning the whole content of a table: get_all_t, get_all_pointer_t, - // get_all_optional_t. - template - std::stringstream string_from_expression_impl_get_all(bool /*noTableName*/) const { - std::stringstream ss; - ss << "SELECT "; - auto &impl = this->get_impl(); - auto columnNames = impl.table.column_names(); - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "\"" << impl.table.name << "\"." - << "\"" << columnNames[i] << "\""; - if(i < columnNames.size() - 1) { - ss << ", "; - } else { - ss << " "; - } + if(c.not_null()) { + ss << "NOT NULL "; } - ss << "FROM '" << impl.table.name << "' "; - return ss; - } - - template - std::string string_from_expression(const get_all_t &get, bool noTableName) const { - std::stringstream ss = this->string_from_expression_impl_get_all(noTableName); - this->process_conditions(ss, get.conditions); return ss.str(); } + }; - template - std::string string_from_expression(const get_all_pointer_t &get, bool noTableName) const { - std::stringstream ss = this->string_from_expression_impl_get_all(noTableName); - this->process_conditions(ss, get.conditions); - return ss.str(); - } + template + struct statement_serializator, void> { + using statement_type = remove_all_t; -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - std::string string_from_expression(const get_all_optional_t &get, bool noTableName) const { - std::stringstream ss = this->string_from_expression_impl_get_all(noTableName); - this->process_conditions(ss, get.conditions); + template + std::string operator()(const statement_type &rem, const C &context) const { + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "DELETE FROM '" << tImpl.table.name << "' "; + iterate_tuple(rem.conditions, [&context, &ss](auto &v) { + ss << serialize(v, context); + }); return ss.str(); } -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + }; - template - std::string string_from_expression(const update_all_t, Wargs...> &upd, - bool /*noTableName*/) const { + template + struct statement_serializator, void> { + using statement_type = replace_t; + + template + std::string operator()(const statement_type &rep, const C &context) const { + using expression_type = typename std::decay::type; + using object_type = typename expression_object_type::type; + auto &tImpl = context.impl.template get_impl(); std::stringstream ss; - ss << "UPDATE "; - std::set> tableNamesSet; - iterate_tuple(upd.set.assigns, [this, &tableNamesSet](auto &asgn) { - auto tableName = this->parse_table_name(asgn.lhs); - tableNamesSet.insert(tableName.begin(), tableName.end()); - }); - if(!tableNamesSet.empty()) { - if(tableNamesSet.size() == 1) { - ss << " '" << tableNamesSet.begin()->first << "' "; - ss << static_cast(upd.set) << " "; - std::vector setPairs; - iterate_tuple(upd.set.assigns, [this, &setPairs](auto &asgn) { - std::stringstream sss; - sss << this->string_from_expression(asgn.lhs, true); - sss << " " << static_cast(asgn) << " "; - sss << this->string_from_expression(asgn.rhs, false) << " "; - setPairs.push_back(sss.str()); - }); - auto setPairsCount = setPairs.size(); - for(size_t i = 0; i < setPairsCount; ++i) { - ss << setPairs[i] << " "; - if(i < setPairsCount - 1) { - ss << ", "; - } - } - this->process_conditions(ss, upd.conditions); - return ss.str(); + ss << "REPLACE INTO '" << tImpl.table.name << "' ("; + auto columnNames = tImpl.table.column_names(); + auto columnNamesCount = columnNames.size(); + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNamesCount - 1) { + ss << ","; } else { - throw std::system_error(std::make_error_code(orm_error_code::too_many_tables_specified)); + ss << ")"; + } + ss << " "; + } + ss << "VALUES("; + for(size_t i = 0; i < columnNamesCount; ++i) { + ss << "?"; + if(i < columnNamesCount - 1) { + ss << ", "; + } else { + ss << ")"; } - } else { - throw std::system_error(std::make_error_code(orm_error_code::incorrect_set_fields_specified)); } - } - - template - std::string string_from_expression(const remove_all_t &rem, bool /*noTableName*/) const { - auto &impl = this->get_impl(); - std::stringstream ss; - ss << "DELETE FROM '" << impl.table.name << "' "; - this->process_conditions(ss, rem.conditions); return ss.str(); } + }; - // Common code for statements with conditions: get_t, get_pointer_t, get_optional_t. - template - std::string string_from_expression_impl_get(bool /*noTableName*/) const { - auto &impl = this->get_impl(); + template + struct statement_serializator, void> { + using statement_type = insert_explicit; + + template + std::string operator()(const statement_type &ins, const C &context) const { + constexpr const size_t colsCount = std::tuple_size>::value; + static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); + using expression_type = typename std::decay::type; + using object_type = typename expression_object_type::type; + auto &tImpl = context.impl.template get_impl(); std::stringstream ss; - ss << "SELECT "; - auto columnNames = impl.table.column_names(); + ss << "INSERT INTO '" << tImpl.table.name << "' "; + std::vector columnNames; + columnNames.reserve(colsCount); + { + auto columnsContext = context; + columnsContext.skip_table_name = true; + iterate_tuple(ins.columns.columns, [&columnNames, &columnsContext](auto &m) { + auto columnName = serialize(m, columnsContext); + if(!columnName.empty()) { + columnNames.push_back(columnName); + } else { + throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + } + }); + } + ss << "("; for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "\"" << columnNames[i] << "\""; + ss << columnNames[i]; if(i < columnNames.size() - 1) { ss << ","; + } else { + ss << ")"; } ss << " "; } - ss << "FROM '" << impl.table.name << "' WHERE "; - auto primaryKeyColumnNames = impl.table.primary_key_column_names(); - if(!primaryKeyColumnNames.empty()) { - for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { - ss << "\"" << primaryKeyColumnNames[i] << "\"" - << " = ? "; - if(i < primaryKeyColumnNames.size() - 1) { - ss << "AND "; - } - ss << ' '; + ss << "VALUES ("; + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << "?"; + if(i < columnNames.size() - 1) { + ss << ","; + } else { + ss << ")"; } - return ss.str(); - } else { - throw std::system_error(std::make_error_code(orm_error_code::table_has_no_primary_key_column)); + ss << " "; } + return ss.str(); } + }; - template - std::string string_from_expression(const get_t &, bool noTableName) const { - return this->string_from_expression_impl_get(noTableName); - } - - template - std::string string_from_expression(const get_pointer_t &, bool noTableName) const { - return this->string_from_expression_impl_get(noTableName); - } - -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - std::string string_from_expression(const get_optional_t &, bool noTableName) const { - return this->string_from_expression_impl_get(noTableName); - } -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializator, void> { + using statement_type = update_t; - template - std::string string_from_expression(const update_t &upd, bool /*noTableName*/) const { + template + std::string operator()(const statement_type &upd, const C &context) const { using expression_type = typename std::decay::type; using object_type = typename expression_object_type::type; - auto &impl = this->get_impl(); + auto &tImpl = context.impl.template get_impl(); std::stringstream ss; - ss << "UPDATE '" << impl.table.name << "' SET "; + ss << "UPDATE '" << tImpl.table.name << "' SET "; std::vector setColumnNames; - impl.table.for_each_column([&setColumnNames](auto &c) { + tImpl.table.for_each_column([&setColumnNames](auto &c) { if(!c.template has>()) { setColumnNames.emplace_back(c.name); } @@ -9554,7 +10439,7 @@ namespace sqlite_orm { ss << " "; } ss << "WHERE "; - auto primaryKeyColumnNames = impl.table.primary_key_column_names(); + auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { ss << "\"" << primaryKeyColumnNames[i] << "\"" << " = ?"; @@ -9565,81 +10450,70 @@ namespace sqlite_orm { } return ss.str(); } + }; - template - std::string string_from_expression(const remove_t &, bool /*noTableName*/) const { - auto &impl = this->get_impl(); - std::stringstream ss; - ss << "DELETE FROM '" << impl.table.name << "' "; - ss << "WHERE "; - auto primaryKeyColumnNames = impl.table.primary_key_column_names(); - for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { - ss << "\"" << primaryKeyColumnNames[i] << "\"" - << " = ? "; - if(i < primaryKeyColumnNames.size() - 1) { - ss << "AND "; - } - } - return ss.str(); - } + template + struct statement_serializator, Wargs...>, void> { + using statement_type = update_all_t, Wargs...>; - template - std::string string_from_expression(const insert_explicit &ins, bool /*noTableName*/) const { - constexpr const size_t colsCount = std::tuple_size>::value; - static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); - using expression_type = typename std::decay::type; - using object_type = typename expression_object_type::type; - this->assert_mapped_type(); - auto &impl = this->get_impl(); + template + std::string operator()(const statement_type &upd, const C &context) const { std::stringstream ss; - ss << "INSERT INTO '" << impl.table.name << "' "; - std::vector columnNames; - columnNames.reserve(colsCount); - iterate_tuple(ins.columns.columns, [&columnNames, this](auto &m) { - auto columnName = this->string_from_expression(m, true); - if(!columnName.empty()) { - columnNames.push_back(columnName); - } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); - } - }); - ss << "("; - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << columnNames[i]; - if(i < columnNames.size() - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - ss << "VALUES ("; - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "?"; - if(i < columnNames.size() - 1) { - ss << ","; + ss << "UPDATE "; + table_name_collector collector{[&context](std::type_index ti) { + return context.impl.find_table_name(ti); + }}; + iterate_ast(upd.set.assigns, collector); + if(!collector.table_names.empty()) { + if(collector.table_names.size() == 1) { + ss << " '" << collector.table_names.begin()->first << "' "; + ss << static_cast(upd.set) << " "; + std::vector setPairs; + auto leftContext = context; + leftContext.skip_table_name = true; + iterate_tuple(upd.set.assigns, [&context, &leftContext, &setPairs](auto &asgn) { + std::stringstream sss; + sss << serialize(asgn.lhs, leftContext); + sss << " " << static_cast(asgn) << " "; + sss << serialize(asgn.rhs, context) << " "; + setPairs.push_back(sss.str()); + }); + auto setPairsCount = setPairs.size(); + for(size_t i = 0; i < setPairsCount; ++i) { + ss << setPairs[i] << " "; + if(i < setPairsCount - 1) { + ss << ", "; + } + } + iterate_tuple(upd.conditions, [&context, &ss](auto &v) { + ss << serialize(v, context); + }); + return ss.str(); } else { - ss << ")"; + throw std::system_error(std::make_error_code(orm_error_code::too_many_tables_specified)); } - ss << " "; + } else { + throw std::system_error(std::make_error_code(orm_error_code::incorrect_set_fields_specified)); } - return ss.str(); } + }; - template - std::string string_from_expression(const insert_t &ins, bool /*noTableName*/) const { - using expression_type = typename std::decay::type; - using object_type = typename expression_object_type::type; - this->assert_mapped_type(); - auto &impl = this->get_impl(); + template + struct statement_serializator, void> { + using statement_type = insert_t; + + template + std::string operator()(const statement_type &, const C &context) const { + using object_type = typename expression_object_type::type; + auto &tImpl = context.impl.template get_impl(); std::stringstream ss; - ss << "INSERT INTO '" << impl.table.name << "' "; + ss << "INSERT INTO '" << tImpl.table.name << "' "; std::vector columnNames; - auto compositeKeyColumnNames = impl.table.composite_key_columns_names(); + auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); - impl.table.for_each_column([&impl, &columnNames, &compositeKeyColumnNames](auto &c) { - if(impl.table._without_rowid || !c.template has>()) { - auto it = std::find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); + tImpl.table.for_each_column([&tImpl, &columnNames, &compositeKeyColumnNames](auto &c) { + if(tImpl.table._without_rowid || !c.template has>()) { + auto it = find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); if(it == compositeKeyColumnNames.end()) { columnNames.emplace_back(c.name); } @@ -9675,46 +10549,42 @@ namespace sqlite_orm { } return ss.str(); } + }; - template - std::string string_from_expression(const replace_t &rep, bool /*noTableName*/) const { - using expression_type = typename std::decay::type; - using object_type = typename expression_object_type::type; - this->assert_mapped_type(); - auto &impl = this->get_impl(); + template + struct statement_serializator, void> { + using statement_type = remove_t; + + template + std::string operator()(const statement_type &, const C &context) const { + auto &tImpl = context.impl.template get_impl(); std::stringstream ss; - ss << "REPLACE INTO '" << impl.table.name << "' ("; - auto columnNames = impl.table.column_names(); - auto columnNamesCount = columnNames.size(); - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - ss << "VALUES("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "?"; - if(i < columnNamesCount - 1) { - ss << ", "; - } else { - ss << ")"; + ss << "DELETE FROM '" << tImpl.table.name << "' "; + ss << "WHERE "; + auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); + for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { + ss << "\"" << primaryKeyColumnNames[i] << "\"" + << " = ? "; + if(i < primaryKeyColumnNames.size() - 1) { + ss << "AND "; } } return ss.str(); } + }; - template - std::string string_from_expression(const replace_range_t &rep, bool /*noTableName*/) const { + template + struct statement_serializator, void> { + using statement_type = replace_range_t; + + template + std::string operator()(const statement_type &rep, const C &context) const { using expression_type = typename std::decay::type; using object_type = typename expression_type::object_type; - auto &impl = this->get_impl(); + auto &tImpl = context.impl.template get_impl(); std::stringstream ss; - ss << "REPLACE INTO '" << impl.table.name << "' ("; - auto columnNames = impl.table.column_names(); + ss << "REPLACE INTO '" << tImpl.table.name << "' ("; + auto columnNames = tImpl.table.column_names(); auto columnNamesCount = columnNames.size(); for(size_t i = 0; i < columnNamesCount; ++i) { ss << "\"" << columnNames[i] << "\""; @@ -9726,17 +10596,17 @@ namespace sqlite_orm { } ss << "VALUES "; auto valuesString = [columnNamesCount] { - std::stringstream ss; - ss << "("; + std::stringstream ss_; + ss_ << "("; for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "?"; + ss_ << "?"; if(i < columnNamesCount - 1) { - ss << ", "; + ss_ << ", "; } else { - ss << ")"; + ss_ << ")"; } } - return ss.str(); + return ss_.str(); }(); auto valuesCount = static_cast(std::distance(rep.range.first, rep.range.second)); for(auto i = 0; i < valuesCount; ++i) { @@ -9748,17 +10618,22 @@ namespace sqlite_orm { } return ss.str(); } + }; - template - std::string string_from_expression(const insert_range_t &ins, bool /*noTableName*/) const { - using expression_type = typename std::decay::type; + template + struct statement_serializator, void> { + using statement_type = insert_range_t; + + template + std::string operator()(const statement_type &statement, const C &context) const { + using expression_type = typename std::decay::type; using object_type = typename expression_type::object_type; - auto &impl = this->get_impl(); + auto &tImpl = context.impl.template get_impl(); std::stringstream ss; - ss << "INSERT INTO '" << impl.table.name << "' ("; + ss << "INSERT INTO '" << tImpl.table.name << "' ("; std::vector columnNames; - impl.table.for_each_column([&columnNames](auto &c) { + tImpl.table.for_each_column([&columnNames](auto &c) { if(!c.template has>()) { columnNames.emplace_back(c.name); } @@ -9776,19 +10651,19 @@ namespace sqlite_orm { } ss << "VALUES "; auto valuesString = [columnNamesCount] { - std::stringstream ss; - ss << "("; + std::stringstream ss_; + ss_ << "("; for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "?"; + ss_ << "?"; if(i < columnNamesCount - 1) { - ss << ", "; + ss_ << ", "; } else { - ss << ")"; + ss_ << ")"; } } - return ss.str(); + return ss_.str(); }(); - auto valuesCount = static_cast(std::distance(ins.range.first, ins.range.second)); + auto valuesCount = static_cast(std::distance(statement.range.first, statement.range.second)); for(auto i = 0; i < valuesCount; ++i) { ss << valuesString; if(i < valuesCount - 1) { @@ -9798,306 +10673,441 @@ namespace sqlite_orm { } return ss.str(); } + }; - template - std::string string_from_expression(const conditions::cast_t &c, bool noTableName) const { - std::stringstream ss; - ss << static_cast(c) << " ("; - ss << this->string_from_expression(c.expression, noTableName) << " AS " << type_printer().print() - << ")"; - return ss.str(); - } + template + std::string serialize_get_all_impl(const T &get, const C &context) { + using primary_type = typename T::type; - template - typename std::enable_if::value, std::string>::type - string_from_expression(const T &op, bool noTableName) const { - std::stringstream ss; - ss << this->string_from_expression(op.left, noTableName) << " "; - ss << static_cast(op) << " "; - ss << this->string_from_expression(op.right, noTableName); - return ss.str(); + table_name_collector collector; + collector.table_names.insert( + std::make_pair(context.impl.find_table_name(typeid(primary_type)), std::string{})); + iterate_ast(get.conditions, collector); + std::stringstream ss; + ss << "SELECT "; + auto &tImpl = context.impl.template get_impl(); + auto columnNames = tImpl.table.column_names(); + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << "\"" << tImpl.table.name << "\"." + << "\"" << columnNames[i] << "\""; + if(i < columnNames.size() - 1) { + ss << ", "; + } else { + ss << " "; + } } - - template - std::string string_from_expression(const internal::simple_case_t &c, - bool noTableName) const { - std::stringstream ss; - ss << "CASE "; - c.case_expression.apply([&ss, this, noTableName](auto &c) { - ss << this->string_from_expression(c, noTableName) << " "; - }); - iterate_tuple(c.args, [&ss, this, noTableName](auto &pair) { - ss << "WHEN " << this->string_from_expression(pair.first, noTableName) << " "; - ss << "THEN " << this->string_from_expression(pair.second, noTableName) << " "; - }); - c.else_expression.apply([&ss, this, noTableName](auto &el) { - ss << "ELSE " << this->string_from_expression(el, noTableName) << " "; - }); - ss << "END"; - return ss.str(); + ss << "FROM "; + std::vector> tableNames(collector.table_names.begin(), + collector.table_names.end()); + for(size_t i = 0; i < tableNames.size(); ++i) { + auto &tableNamePair = tableNames[i]; + ss << "'" << tableNamePair.first << "' "; + if(!tableNamePair.second.empty()) { + ss << tableNamePair.second << " "; + } + if(int(i) < int(tableNames.size()) - 1) { + ss << ","; + } + ss << " "; } + iterate_tuple(get.conditions, [&context, &ss](auto &v) { + ss << serialize(v, context); + }); + return ss.str(); + } - template - std::string string_from_expression(const conditions::is_null_t &c, bool noTableName) const { - std::stringstream ss; - ss << this->string_from_expression(c.t, noTableName) << " " << static_cast(c) << " "; - return ss.str(); +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializator, void> { + using statement_type = get_all_optional_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_all_impl(get, context); } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - std::string string_from_expression(const conditions::is_not_null_t &c, bool noTableName) const { - std::stringstream ss; - ss << this->string_from_expression(c.t, noTableName) << " " << static_cast(c) << " "; - return ss.str(); + template + struct statement_serializator, void> { + using statement_type = get_all_pointer_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_all_impl(get, context); } + }; + + template + struct statement_serializator, void> { + using statement_type = get_all_t; template - std::string string_from_expression(const conditions::negated_condition_t &c, bool noTableName) const { - std::stringstream ss; - ss << static_cast(c) << " "; - auto cString = this->string_from_expression(c.c, noTableName); - ss << " (" << cString << " ) "; - return ss.str(); + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_all_impl(get, context); } + }; - template - std::string string_from_expression(const conditions::is_equal_t &c, bool noTableName) const { - auto leftString = this->string_from_expression(c.l, noTableName); - auto rightString = this->string_from_expression(c.r, noTableName); - std::stringstream ss; - ss << leftString << " " << static_cast(c) << " " << rightString; + template + std::string serialize_get_impl(const T &, const C &context) { + using primary_type = typename T::type; + auto &tImpl = context.impl.template get_impl(); + std::stringstream ss; + ss << "SELECT "; + auto columnNames = tImpl.table.column_names(); + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << "\"" << columnNames[i] << "\""; + if(i < columnNames.size() - 1) { + ss << ","; + } + ss << " "; + } + ss << "FROM '" << tImpl.table.name << "' WHERE "; + auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); + if(!primaryKeyColumnNames.empty()) { + for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { + ss << "\"" << primaryKeyColumnNames[i] << "\"" + << " = ? "; + if(i < primaryKeyColumnNames.size() - 1) { + ss << "AND"; + } + ss << ' '; + } return ss.str(); + } else { + throw std::system_error(std::make_error_code(orm_error_code::table_has_no_primary_key_column)); } + } + + template + struct statement_serializator, void> { + using statement_type = get_t; template - typename std::enable_if::value, std::string>::type - string_from_expression(const C &c, bool noTableName) const { - auto leftString = this->string_from_expression(c.l, noTableName); - auto rightString = this->string_from_expression(c.r, noTableName); - std::stringstream ss; - ss << "(" << leftString << " " << static_cast(c) << " " << rightString << ")"; - return ss.str(); + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_impl(get, context); } + }; - template - std::string string_from_expression(const conditions::named_collate &col, bool noTableName) const { - auto res = this->string_from_expression(col.expr, noTableName); - return res + " " + static_cast(col); + template + struct statement_serializator, void> { + using statement_type = get_pointer_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_impl(get, context); } + }; - template - std::string string_from_expression(const conditions::collate_t &col, bool noTableName) const { - auto res = this->string_from_expression(col.expr, noTableName); - return res + " " + static_cast(col); +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializator, void> { + using statement_type = get_optional_t; + + template + std::string operator()(const statement_type &get, const C &context) const { + return serialize_get_impl(get, context); } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_serializator, void> { + using statement_type = select_t; - template - std::string string_from_expression(const conditions::in_t &inCondition, bool noTableName) const { + template + std::string operator()(const statement_type &sel, const C &context) const { std::stringstream ss; - auto leftString = this->string_from_expression(inCondition.l, noTableName); - ss << leftString << " " << static_cast(inCondition) << " "; - ss << this->string_from_expression(inCondition.arg, noTableName); + if(!is_base_of_template::value) { + if(!sel.highest_level) { + ss << "( "; + } + ss << "SELECT "; + } + if(get_distinct(sel.col)) { + ss << static_cast(distinct(0)) << " "; + } + auto columnNames = get_column_names(sel.col, context); + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << columnNames[i]; + if(i < columnNames.size() - 1) { + ss << ","; + } + ss << " "; + } + table_name_collector collector{[&context](std::type_index ti) { + return context.impl.find_table_name(ti); + }}; + iterate_ast(sel.col, collector); + iterate_ast(sel.conditions, collector); + internal::join_iterator()([&collector, &context](const auto &c) { + using original_join_type = typename std::decay::type::join_type::type; + using cross_join_type = typename internal::mapped_type_proxy::type; + auto crossJoinedTableName = context.impl.find_table_name(typeid(cross_join_type)); + auto tableAliasString = alias_extractor::get(); + std::pair tableNameWithAlias(std::move(crossJoinedTableName), + std::move(tableAliasString)); + collector.table_names.erase(tableNameWithAlias); + }); + if(!collector.table_names.empty()) { + ss << "FROM "; + std::vector> tableNames(collector.table_names.begin(), + collector.table_names.end()); + for(size_t i = 0; i < tableNames.size(); ++i) { + auto &tableNamePair = tableNames[i]; + ss << "'" << tableNamePair.first << "' "; + if(!tableNamePair.second.empty()) { + ss << tableNamePair.second << " "; + } + if(int(i) < int(tableNames.size()) - 1) { + ss << ","; + } + ss << " "; + } + } + iterate_tuple(sel.conditions, [&context, &ss](auto &v) { + ss << serialize(v, context); + }); + if(!is_base_of_template::value) { + if(!sel.highest_level) { + ss << ") "; + } + } return ss.str(); } + }; + + template + struct statement_serializator, void> { + using statement_type = indexed_column_t; - template - std::string string_from_expression(const conditions::in_t> &inCondition, - bool noTableName) const { + template + std::string operator()(const statement_type &statement, const C &context) const { std::stringstream ss; - auto leftString = this->string_from_expression(inCondition.l, noTableName); - ss << leftString << " " << static_cast(inCondition) << " ( "; - for(size_t index = 0; index < inCondition.arg.size(); ++index) { - auto &value = inCondition.arg[index]; - ss << " " << this->string_from_expression(value, noTableName); - if(index < inCondition.arg.size() - 1) { - 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(std::make_error_code(orm_error_code::incorrect_order)); } } - ss << " )"; return ss.str(); } + }; + + template + struct statement_serializator, void> { + using statement_type = index_t; - template - std::string string_from_expression(const conditions::like_t &l, bool noTableName) const { + template + std::string operator()(const statement_type &statement, const C &context) const { std::stringstream ss; - ss << this->string_from_expression(l.arg, noTableName) << " "; - ss << static_cast(l) << " "; - ss << this->string_from_expression(l.pattern, noTableName); - l.arg3.apply([&ss, this, noTableName](auto &value) { - ss << " ESCAPE " << this->string_from_expression(value, noTableName); + ss << "CREATE "; + if(statement.unique) { + ss << "UNIQUE "; + } + using columns_type = typename std::decay::type::columns_type; + using head_t = typename std::tuple_element<0, columns_type>::type::column_type; + using indexed_type = typename table_type::type; + ss << "INDEX IF NOT EXISTS '" << statement.name << "' ON '" + << context.impl.find_table_name(typeid(indexed_type)) << "' ("; + std::vector columnNames; + iterate_tuple(statement.columns, [&columnNames, &context](auto &v) { + columnNames.push_back(context.column_name(v.column_or_expression)); }); + for(size_t i = 0; i < columnNames.size(); ++i) { + ss << "'" << columnNames[i] << "'"; + if(i < columnNames.size() - 1) { + ss << ", "; + } + } + ss << ")"; return ss.str(); } + }; - template - std::string string_from_expression(const conditions::glob_t &l, bool noTableName) const { - std::stringstream ss; - ss << this->string_from_expression(l.arg, noTableName) << " "; - ss << static_cast(l) << " "; - ss << this->string_from_expression(l.pattern, noTableName); - return ss.str(); - } + template + struct statement_serializator, void> { + using statement_type = where_t; - template - std::string string_from_expression(const conditions::between_t &bw, bool noTableName) const { + template + std::string operator()(const statement_type &w, const C &context) const { std::stringstream ss; - auto expr = this->string_from_expression(bw.expr, noTableName); - ss << expr << " " << static_cast(bw) << " "; - ss << this->string_from_expression(bw.b1, noTableName); - ss << " AND "; - ss << this->string_from_expression(bw.b2, noTableName); + ss << static_cast(w) << " "; + auto whereString = serialize(w.c, context); + ss << "( " << whereString << ") "; return ss.str(); } + }; - template - std::string string_from_expression(const conditions::exists_t &e, bool noTableName) const { - std::stringstream ss; - ss << static_cast(e) << " "; - ss << this->string_from_expression(e.t, noTableName); - return ss.str(); - } + template + struct statement_serializator, void> { + using statement_type = order_by_t; - template - std::string process_order_by(const conditions::order_by_t &orderBy) const { + template + std::string operator()(const statement_type &orderBy, const C &context) const { std::stringstream ss; - auto columnName = this->string_from_expression(orderBy.o, false); - ss << columnName << " "; - if(orderBy._collate_argument.length()) { - ss << "COLLATE " << orderBy._collate_argument << " "; - } - switch(orderBy.asc_desc) { - case 1: - ss << "ASC"; - break; - case -1: - ss << "DESC"; - break; - } + ss << static_cast(orderBy) << " "; + auto orderByString = serialize_order_by(orderBy, context); + ss << orderByString << " "; return ss.str(); } + }; - template - void process_join_constraint(std::stringstream &ss, const conditions::on_t &t) const { - ss << static_cast(t) << " " << this->string_from_expression(t.arg, false); - } + template + struct statement_serializator, void> { + using statement_type = dynamic_order_by_t; - template - void process_join_constraint(std::stringstream &ss, const conditions::using_t &u) const { - ss << static_cast(u) << " (" << this->string_from_expression(u.column, true) << " )"; + template + std::string operator()(const statement_type &orderBy, const CC &context) const { + return serialize_order_by(orderBy, context); } + }; - /** - * HO - has offset - * OI - offset is implicit - */ - template - void process_single_condition(std::stringstream &ss, const conditions::limit_t &limt) const { - ss << static_cast(limt) << " "; - if(HO) { - if(OI) { - limt.off.apply([this, &ss](auto &value) { - ss << this->string_from_expression(value, false); - }); + template + struct statement_serializator, void> { + using statement_type = multi_order_by_t; + + template + std::string operator()(const statement_type &orderBy, const C &context) const { + std::stringstream ss; + std::vector expressions; + iterate_tuple(orderBy.args, [&expressions, &context](auto &v) { + auto expression = serialize_order_by(v, context); + expressions.push_back(move(expression)); + }); + ss << static_cast(orderBy) << " "; + for(size_t i = 0; i < expressions.size(); ++i) { + ss << expressions[i]; + if(i < expressions.size() - 1) { ss << ", "; - ss << this->string_from_expression(limt.lim, false); - } else { - ss << this->string_from_expression(limt.lim, false) << " OFFSET "; - limt.off.apply([this, &ss](auto &value) { - ss << this->string_from_expression(value, false); - }); } - } else { - ss << this->string_from_expression(limt.lim, false); } + ss << " "; + return ss.str(); } + }; - template - void process_single_condition(std::stringstream &ss, const conditions::cross_join_t &c) const { - ss << static_cast(c) << " "; - ss << " '" << this->impl.template find_table_name() << "'"; - } + template + struct statement_serializator, void> { + using statement_type = cross_join_t; - template - void process_single_condition(std::stringstream &ss, const conditions::natural_join_t &c) const { + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; ss << static_cast(c) << " "; - ss << " '" << this->impl.template find_table_name() << "'"; + ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; + return ss.str(); } + }; + + template + struct statement_serializator, void> { + using statement_type = inner_join_t; - template - void process_single_condition(std::stringstream &ss, const conditions::inner_join_t &l) const { + template + std::string operator()(const statement_type &l, const C &context) const { + std::stringstream ss; ss << static_cast(l) << " "; auto aliasString = alias_extractor::get(); - ss << " '" << this->impl.template find_table_name::type>() << "' "; + ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy::type)) << "' "; if(aliasString.length()) { ss << "'" << aliasString << "' "; } - this->process_join_constraint(ss, l.constraint); + ss << serialize(l.constraint, context); + return ss.str(); } + }; - template - void process_single_condition(std::stringstream &ss, const conditions::left_outer_join_t &l) const { - ss << static_cast(l) << " "; - ss << " '" << this->impl.template find_table_name() << "' "; - this->process_join_constraint(ss, l.constraint); - } + template + struct statement_serializator, void> { + using statement_type = on_t; - template - void process_single_condition(std::stringstream &ss, const conditions::left_join_t &l) const { - ss << static_cast(l) << " "; - ss << " '" << this->impl.template find_table_name() << "' "; - this->process_join_constraint(ss, l.constraint); + template + std::string operator()(const statement_type &t, const C &context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << static_cast(t) << " " << serialize(t.arg, newContext) << " "; + return ss.str(); } + }; - template - void process_single_condition(std::stringstream &ss, const conditions::join_t &l) const { - ss << static_cast(l) << " "; - ss << " '" << this->impl.template find_table_name() << "' "; - this->process_join_constraint(ss, l.constraint); - } + template + struct statement_serializator, void> { + using statement_type = join_t; template - void process_single_condition(std::stringstream &ss, const conditions::where_t &w) const { - ss << static_cast(w) << " "; - auto whereString = this->string_from_expression(w.c, false); - ss << "( " << whereString << ") "; + std::string operator()(const statement_type &l, const C &context) const { + std::stringstream ss; + ss << static_cast(l) << " "; + ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + ss << serialize(l.constraint, context); + return ss.str(); } + }; - template - void process_single_condition(std::stringstream &ss, const conditions::order_by_t &orderBy) const { - ss << static_cast(orderBy) << " "; - auto orderByString = this->process_order_by(orderBy); - ss << orderByString << " "; - } + template + struct statement_serializator, void> { + using statement_type = left_join_t; - template - void process_single_condition(std::stringstream &ss, - const conditions::multi_order_by_t &orderBy) const { - std::vector expressions; - iterate_tuple(orderBy.args, [&expressions, this](auto &v) { - auto expression = this->process_order_by(v); - expressions.push_back(std::move(expression)); - }); - ss << static_cast(orderBy) << " "; - for(size_t i = 0; i < expressions.size(); ++i) { - ss << expressions[i]; - if(i < expressions.size() - 1) { - ss << ", "; - } - } - ss << " "; + template + std::string operator()(const statement_type &l, const C &context) const { + std::stringstream ss; + ss << static_cast(l) << " "; + ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + ss << serialize(l.constraint, context); + return ss.str(); } + }; - template - void process_single_condition(std::stringstream &ss, - const conditions::dynamic_order_by_t &orderBy) const { - ss << this->storage_base::process_order_by(orderBy) << " "; + template + struct statement_serializator, void> { + using statement_type = left_outer_join_t; + + template + std::string operator()(const statement_type &l, const C &context) const { + std::stringstream ss; + ss << static_cast(l) << " "; + ss << " '" << context.impl.find_table_name(typeid(T)) << "' "; + ss << serialize(l.constraint, context); + return ss.str(); } + }; - template - void process_single_condition(std::stringstream &ss, const conditions::group_by_t &groupBy) const { + template + struct statement_serializator, void> { + using statement_type = natural_join_t; + + template + std::string operator()(const statement_type &c, const C &context) const { + std::stringstream ss; + ss << static_cast(c) << " "; + ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; + return ss.str(); + } + }; + + template + struct statement_serializator, void> { + using statement_type = group_by_t; + + template + std::string operator()(const statement_type &groupBy, const C &context) const { + std::stringstream ss; std::vector expressions; - iterate_tuple(groupBy.args, [&expressions, this](auto &v) { - auto expression = this->string_from_expression(v, false); + auto newContext = context; + newContext.skip_table_name = false; + iterate_tuple(groupBy.args, [&expressions, &newContext](auto &v) { + auto expression = serialize(v, newContext); expressions.push_back(expression); }); ss << static_cast(groupBy) << " "; @@ -10108,356 +11118,331 @@ namespace sqlite_orm { } } ss << " "; + return ss.str(); } + }; - template - void process_single_condition(std::stringstream &ss, const conditions::having_t &hav) const { + template + struct statement_serializator, void> { + using statement_type = having_t; + + template + std::string operator()(const statement_type &hav, const C &context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; ss << static_cast(hav) << " "; - ss << this->string_from_expression(hav.t, false) << " "; + ss << serialize(hav.t, newContext) << " "; + return ss.str(); } + }; - template - void process_conditions(std::stringstream &ss, const std::tuple &args) const { - iterate_tuple(args, [this, &ss](auto &v) { - this->process_single_condition(ss, v); - }); + /** + * HO - has offset + * OI - offset is implicit + */ + template + struct statement_serializator, void> { + using statement_type = limit_t; + + template + std::string operator()(const statement_type &limt, const C &context) const { + auto newContext = context; + newContext.skip_table_name = false; + std::stringstream ss; + ss << static_cast(limt) << " "; + if(HO) { + if(OI) { + limt.off.apply([&newContext, &ss](auto &value) { + 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(); } + }; - public: - template - view_t iterate(Args &&... args) { - this->assert_mapped_type(); + template + struct statement_serializator, void> { + using statement_type = using_t; - auto con = this->get_connection(); - return {*this, std::move(con), std::forward(args)...}; + template + std::string operator()(const statement_type &statement, const C &context) const { + auto newContext = context; + newContext.skip_table_name = true; + return static_cast(statement) + " (" + serialize(statement.column, newContext) + " )"; } + }; - template - void remove_all(Args &&... args) { - this->assert_mapped_type(); - auto statement = this->prepare(sqlite_orm::remove_all(std::forward(args)...)); - this->execute(statement); - } + template + struct statement_serializator, void> { + using statement_type = std::tuple; - /** - * 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); + template + std::string operator()(const statement_type &statement, const C &context) const { + std::stringstream ss; + ss << '('; + auto index = 0; + using TupleSize = std::tuple_size; + iterate_tuple(statement, [&context, &index, &ss](auto &value) { + ss << serialize(value, context); + if(index < TupleSize::value - 1) { + ss << ", "; + } + ++index; + }); + ss << ')'; + return ss.str(); } + }; - /** - * 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 + struct statement_serializator, void> { + using statement_type = values_t; - template - void update_all(internal::set_t set, Wargs... wh) { - auto statement = this->prepare(sqlite_orm::update_all(std::move(set), std::forward(wh)...)); - this->execute(statement); + template + std::string operator()(const statement_type &statement, const C &context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << "VALUES "; + { + auto index = 0; + auto &tuple = statement.tuple; + using tuple_type = typename std::decay::type; + using TupleSize = std::tuple_size; + iterate_tuple(tuple, [&context, &index, &ss](auto &value) { + ss << serialize(value, context); + if(index < TupleSize::value - 1) { + ss << ", "; + } + ++index; + }); + } + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); } + }; - protected: - template - std::set> parse_table_name(const T &) const { - return {}; - } + template + struct statement_serializator, void> { + using statement_type = dynamic_values_t; - template - std::set> parse_table_name(F O::*, std::string alias = {}) const { - return {std::make_pair(this->impl.template find_table_name(), std::move(alias))}; + template + std::string operator()(const statement_type &statement, const C &context) const { + std::stringstream ss; + if(context.use_parentheses) { + ss << '('; + } + ss << "VALUES "; + { + auto vectorSize = statement.vector.size(); + for(decltype(vectorSize) index = 0; index < vectorSize; ++index) { + auto &value = statement.vector[index]; + ss << serialize(value, context); + if(index < vectorSize - 1) { + ss << ", "; + } + } + } + if(context.use_parentheses) { + ss << ')'; + } + return ss.str(); } + }; - template - std::set> - parse_table_name(const aggregate_functions::min_t &f) const { - return this->parse_table_name(f.arg); - } + } +} - template - std::set> - parse_table_name(const aggregate_functions::max_t &f) const { - return this->parse_table_name(f.arg); - } +// #include "table_name_collector.h" - template - std::set> - parse_table_name(const aggregate_functions::sum_t &f) const { - return this->parse_table_name(f.arg); - } +// #include "object_from_column_builder.h" - template - std::set> - parse_table_name(const aggregate_functions::total_t &f) const { - return this->parse_table_name(f.arg); - } +namespace sqlite_orm { - template - std::set> - parse_table_name(const aggregate_functions::group_concat_double_t &f) const { - auto res = this->parse_table_name(f.arg); - auto secondSet = this->parse_table_name(f.y); - res.insert(secondSet.begin(), secondSet.end()); - return res; - } + namespace internal { - template - std::set> - parse_table_name(const aggregate_functions::group_concat_single_t &f) const { - return this->parse_table_name(f.arg); - } + /** + * 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 impl_type = storage_impl; - template - std::set> - parse_table_name(const aggregate_functions::count_t &f) const { - return this->parse_table_name(f.arg); - } + /** + * @param filename database filename. + * @param impl_ storage_impl head + */ + storage_t(const std::string &filename, impl_type impl_) : + storage_base{filename, foreign_keys_count(impl_)}, impl(std::move(impl_)) {} - template - std::set> - parse_table_name(const aggregate_functions::avg_t &a) const { - return this->parse_table_name(a.arg); - } - - template - std::set> - parse_table_name(const core_functions::core_function_t &f) const { - std::set> res; - iterate_tuple(f.args, [&res, this](auto &v) { - auto tableNames = this->parse_table_name(v); - res.insert(tableNames.begin(), tableNames.end()); - }); - return res; - } + storage_t(const storage_t &other) : storage_base(other), impl(other.impl) {} - template - std::set> parse_table_name(const distinct_t &f) const { - return this->parse_table_name(f.t); - } + protected: + impl_type impl; - template - std::set> parse_table_name(const all_t &f) const { - return this->parse_table_name(f.t); - } - - template - std::set> - parse_table_name(const binary_operator &f) const { - std::set> res; - auto leftSet = this->parse_table_name(f.lhs); - res.insert(leftSet.begin(), leftSet.end()); - auto rightSet = this->parse_table_name(f.rhs); - res.insert(rightSet.begin(), rightSet.end()); - return res; - } + template + friend struct view_t; - template - std::set> parse_table_name(const column_pointer &) const { - std::set> res; - res.insert({this->impl.template find_table_name(), ""}); - return res; - } + template + friend struct dynamic_order_by_t; - template - std::set> parse_table_name(const alias_column_t &a) const { - return this->parse_table_name(a.column, alias_extractor::get()); - } + template + friend struct iterator_t; - template - std::set> - parse_table_name(const aggregate_functions::count_asterisk_t &) const { - auto tableName = this->impl.template find_table_name(); - if(!tableName.empty()) { - return {std::make_pair(std::move(tableName), "")}; + template + friend struct serializator_context_builder; + + template + void create_table(sqlite3 *db, const std::string &tableName, const I &tableImpl) { + std::stringstream ss; + ss << "CREATE TABLE '" << tableName << "' ( "; + auto columnsCount = tableImpl.table.columns_count; + auto index = 0; + using context_t = serializator_context; + context_t context{this->impl}; + iterate_tuple(tableImpl.table.columns, [columnsCount, &index, &ss, &context](auto &c) { + ss << serialize(c, context); + if(index < columnsCount - 1) { + ss << ", "; + } + index++; + }); + ss << ") "; + if(tableImpl.table._without_rowid) { + ss << "WITHOUT ROWID "; + } + auto query = ss.str(); + sqlite3_stmt *stmt; + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { + statement_finalizer finalizer{stmt}; + if(sqlite3_step(stmt) == SQLITE_DONE) { + // done.. + } else { + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); + } } else { - return {}; + throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), + sqlite3_errmsg(db)); } } - std::set> - parse_table_name(const aggregate_functions::count_asterisk_without_type &) const { - return {}; - } - - template - std::set> parse_table_name(const asterisk_t &) const { - auto tableName = this->impl.template find_table_name(); - return {std::make_pair(std::move(tableName), "")}; - } - - template - std::set> parse_table_name(const conditions::cast_t &c) const { - return this->parse_table_name(c.expression); - } + template + void backup_table(sqlite3 *db, const I &tableImpl, const std::vector &columnsToIgnore) { - template - std::set> - parse_table_name(const simple_case_t &c) const { - std::set> res; - c.case_expression.apply([this, &res](auto &c) { - auto caseExpressionSet = this->parse_table_name(c); - res.insert(caseExpressionSet.begin(), caseExpressionSet.end()); - }); - iterate_tuple(c.args, [this, &res](auto &pair) { - auto leftSet = this->parse_table_name(pair.first); - res.insert(leftSet.begin(), leftSet.end()); - auto rightSet = this->parse_table_name(pair.second); - res.insert(rightSet.begin(), rightSet.end()); - }); - c.else_expression.apply([this, &res](auto &el) { - auto tableNames = this->parse_table_name(el); - res.insert(tableNames.begin(), tableNames.end()); - }); - return res; - } + // 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 = tableImpl.table.name + "_backup"; + if(tableImpl.table_exists(backupTableName, db)) { + int suffix = 1; + do { + std::stringstream stream; + stream << suffix; + auto anotherBackupTableName = backupTableName + stream.str(); + if(!tableImpl.table_exists(anotherBackupTableName, db)) { + backupTableName = anotherBackupTableName; + break; + } + ++suffix; + } while(true); + } - template - std::set> - parse_table_name(const conditions::and_condition_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; - } + this->create_table(db, backupTableName, tableImpl); - template - std::set> - parse_table_name(const conditions::or_condition_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; - } + tableImpl.copy_table(db, backupTableName, columnsToIgnore); - template - std::set> - parse_table_name(const conditions::is_equal_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; - } + this->drop_table_internal(tableImpl.table.name, db); - template - std::set> - parse_table_name(const conditions::is_not_equal_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; + tableImpl.rename_table(db, backupTableName, tableImpl.table.name); } - template - std::set> - parse_table_name(const conditions::greater_than_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; + template + void assert_mapped_type() const { + using mapped_types_tuples = std::tuple; + static_assert(tuple_helper::has_type::value, "type is not mapped to a storage"); } - template - std::set> - parse_table_name(const conditions::greater_or_equal_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; + template + auto &get_impl() const { + return this->impl.template get_impl(); } - template - std::set> - parse_table_name(const conditions::lesser_than_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; + template + auto &get_impl() { + return this->impl.template get_impl(); } - template - std::set> - parse_table_name(const conditions::lesser_or_equal_t &c) const { - std::set> res; - auto leftTableNames = this->parse_table_name(c.l); - res.insert(leftTableNames.begin(), leftTableNames.end()); - auto rightTableNames = this->parse_table_name(c.r); - res.insert(rightTableNames.begin(), rightTableNames.end()); - return res; - } + public: + template + view_t iterate(Args &&... args) { + this->assert_mapped_type(); - template - std::set> parse_table_name(const conditions::like_t &l) const { - std::set> res; - auto argTableNames = this->parse_table_name(l.arg); - res.insert(argTableNames.begin(), argTableNames.end()); - auto patternTableNames = this->parse_table_name(l.pattern); - res.insert(patternTableNames.begin(), patternTableNames.end()); - l.arg3.apply([&res, this](auto &value) { - auto escapeTableNames = this->parse_table_name(value); - res.insert(escapeTableNames.begin(), escapeTableNames.end()); - }); - return res; + auto con = this->get_connection(); + return {*this, std::move(con), std::forward(args)...}; } - template - std::set> parse_table_name(const conditions::glob_t &l) const { - std::set> res; - auto argTableNames = this->parse_table_name(l.arg); - res.insert(argTableNames.begin(), argTableNames.end()); - auto patternTableNames = this->parse_table_name(l.pattern); - res.insert(patternTableNames.begin(), patternTableNames.end()); - return res; + /** + * 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); } - template - std::set> - parse_table_name(const conditions::negated_condition_t &c) const { - return this->parse_table_name(c.c); + /** + * 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); } - template - std::set> parse_table_name(const as_t &a) const { - return this->parse_table_name(a.expression); + /** + * 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 - std::set> - parse_table_name(const internal::columns_t &cols) const { - std::set> res; - iterate_tuple(cols.columns, [&res, this](auto &m) { - auto tableName = this->parse_table_name(m); - res.insert(tableName.begin(), tableName.end()); - }); - return res; + template + void update_all(internal::set_t set, Wargs... wh) { + 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(); @@ -10476,29 +11461,65 @@ namespace sqlite_orm { public: /** - * Select * with no conditions routine. + * 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. + * @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, class... Args> - C get_all(Args &&... args) { + 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 * with no conditions routine. + * SELECT * routine. * O is an object type to be extracted. Must be specified explicitly. - * @return All objects of type O as std::unique_ptr stored in database at the moment. + * 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>, class... Args> - C get_all_pointer(Args &&... args) { + 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, orm_error_category) if object not found with given @@ -10728,14 +11749,49 @@ namespace sqlite_orm { return this->execute(statement); } + template + typename std::enable_if::value, std::string>::type + dump(const T &preparedStatement) const { + using context_t = serializator_context; + context_t context{this->impl}; + return serialize(preparedStatement.t, context); + } + /** * Returns a string representation of object of a class mapped to the storage. * Type of string has json-like style. */ template - std::string dump(const O &o) { - this->assert_mapped_type(); - return this->impl.dump(o); + typename std::enable_if::value, std::string>::type + dump(const O &o) { + auto &tImpl = this->get_impl(); + std::stringstream ss; + ss << "{ "; + using pair = std::pair; + std::vector pairs; + tImpl.table.for_each_column([&pairs, &o](auto &c) { + using column_type = typename std::decay::type; + using field_type = typename column_type::field_type; + pair p{c.name, std::string()}; + if(c.member_pointer) { + p.second = field_printer()(o.*c.member_pointer); + } else { + using getter_type = typename column_type::getter_type; + field_value_holder valueHolder{((o).*(c.getter))()}; + p.second = field_printer()(valueHolder.value); + } + pairs.push_back(move(p)); + }); + for(size_t i = 0; i < pairs.size(); ++i) { + auto &p = pairs[i]; + ss << p.first << " : '" << p.second << "'"; + if(i < pairs.size() - 1) { + ss << ", "; + } else { + ss << " }"; + } + } + return ss.str(); } /** @@ -10796,33 +11852,37 @@ namespace sqlite_orm { 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 &tImpl = this->get_impl(); + tImpl.table.name = 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 &tImpl = this->get_impl(); + return tImpl.table.name; + } + protected: template - sync_schema_result sync_table(storage_impl, Tss...> *impl, sqlite3 *db, bool) { + sync_schema_result sync_table(const storage_impl, Tss...> &tableImpl, sqlite3 *db, bool) { auto res = sync_schema_result::already_in_sync; - std::stringstream ss; - ss << "CREATE "; - if(impl->table.unique) { - ss << "UNIQUE "; - } - using columns_type = typename decltype(impl->table)::columns_type; - using head_t = typename std::tuple_element<0, columns_type>::type; - using indexed_type = typename internal::table_type::type; - ss << "INDEX IF NOT EXISTS '" << impl->table.name << "' ON '" - << this->impl.template find_table_name() << "' ( "; - std::vector columnNames; - iterate_tuple(impl->table.columns, [&columnNames, this](auto &v) { - columnNames.push_back(this->impl.column_name(v)); - }); - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "'" << columnNames[i] << "'"; - if(i < columnNames.size() - 1) { - ss << ","; - } - ss << " "; - } - ss << ") "; - auto query = ss.str(); + using context_t = serializator_context; + context_t context{this->impl}; + auto query = serialize(tableImpl.table, context); auto rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); if(rc != SQLITE_OK) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), @@ -10832,13 +11892,14 @@ namespace sqlite_orm { } template - sync_schema_result sync_table(storage_impl, Tss...> *impl, sqlite3 *db, bool preserve) { + sync_schema_result + sync_table(const storage_impl, Tss...> &tImpl, sqlite3 *db, bool preserve) { auto res = sync_schema_result::already_in_sync; - auto schema_stat = impl->schema_status(db, preserve); + auto schema_stat = tImpl.schema_status(db, preserve); if(schema_stat != decltype(schema_stat)::already_in_sync) { if(schema_stat == decltype(schema_stat)::new_table_created) { - this->create_table(db, impl->table.name, impl); + this->create_table(db, tImpl.table.name, tImpl); res = decltype(res)::new_table_created; } else { if(schema_stat == sync_schema_result::old_columns_removed || @@ -10846,26 +11907,26 @@ namespace sqlite_orm { schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { // get table info provided in `make_table` call.. - auto storageTableInfo = impl->table.get_table_info(); + auto storageTableInfo = tImpl.table.get_table_info(); // now get current table info from db using `PRAGMA table_info` query.. - auto dbTableInfo = impl->get_table_info(impl->table.name, db); + auto dbTableInfo = tImpl.get_table_info(tImpl.table.name, db); // this vector will contain pointers to columns that gotta be added.. std::vector columnsToAdd; - impl->get_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + tImpl.get_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); if(schema_stat == sync_schema_result::old_columns_removed) { // extra table columns than storage columns - this->backup_table(db, impl); + this->backup_table(db, tImpl, {}); res = decltype(res)::old_columns_removed; } if(schema_stat == sync_schema_result::new_columns_added) { for(auto columnPointer: columnsToAdd) { - impl->add_column(*columnPointer, db); + tImpl.add_column(*columnPointer, db); } res = decltype(res)::new_columns_added; } @@ -10873,15 +11934,12 @@ namespace sqlite_orm { if(schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { // remove extra columns - this->backup_table(db, impl); - for(auto columnPointer: columnsToAdd) { - impl->add_column(*columnPointer, db); - } + this->backup_table(db, tImpl, columnsToAdd); res = decltype(res)::new_columns_added_and_old_columns_removed; } } else if(schema_stat == sync_schema_result::dropped_and_recreated) { - this->drop_table_internal(impl->table.name, db); - this->create_table(db, impl->table.name, impl); + this->drop_table_internal(tImpl.table.name, db); + this->create_table(db, tImpl.table.name, tImpl); res = decltype(res)::dropped_and_recreated; } } @@ -10921,9 +11979,9 @@ namespace sqlite_orm { auto con = this->get_connection(); std::map result; auto db = con.get(); - this->impl.for_each([&result, db, preserve, this](auto impl) { - auto res = this->sync_table(impl, db, preserve); - result.insert({impl->table.name, res}); + this->impl.for_each([&result, db, preserve, this](auto &tableImpl) { + auto res = this->sync_table(tableImpl, db, preserve); + result.insert({tableImpl.table.name, res}); }); return result; } @@ -10937,8 +11995,8 @@ namespace sqlite_orm { auto con = this->get_connection(); std::map result; auto db = con.get(); - this->impl.for_each([&result, db, preserve](auto impl) { - result.insert({impl->table.name, impl->schema_status(db, preserve)}); + this->impl.for_each([&result, db, preserve](auto tableImpl) { + result.insert({tableImpl.table.name, tableImpl.schema_status(db, preserve)}); }); return result; } @@ -10959,7 +12017,11 @@ namespace sqlite_orm { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(sel, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(sel, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(sel), stmt, con}; } else { @@ -10969,13 +12031,17 @@ namespace sqlite_orm { } template - prepared_statement_t> prepare(get_all_t get) { + prepared_statement_t> prepare(get_all_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(get, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get), stmt, con}; + return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -10983,13 +12049,17 @@ namespace sqlite_orm { } template - prepared_statement_t> prepare(get_all_pointer_t get) { + prepared_statement_t> prepare(get_all_pointer_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(get, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get), stmt, con}; + return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -10997,14 +12067,18 @@ namespace sqlite_orm { } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - prepared_statement_t> prepare(get_all_optional_t get) { + template + prepared_statement_t> prepare(get_all_optional_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(get, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(get), stmt, con}; + return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -11018,7 +12092,11 @@ namespace sqlite_orm { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(upd, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(upd, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(upd), stmt, con}; } else { @@ -11032,7 +12110,11 @@ namespace sqlite_orm { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(rem, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(rem, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(rem), stmt, std::move(con)}; } else { @@ -11042,13 +12124,17 @@ namespace sqlite_orm { } template - prepared_statement_t> prepare(get_t g) { + prepared_statement_t> prepare(get_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(g, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(g), stmt, con}; + return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -11056,13 +12142,17 @@ namespace sqlite_orm { } template - prepared_statement_t> prepare(get_pointer_t g) { + prepared_statement_t> prepare(get_pointer_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(g, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(g), stmt, con}; + return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -11071,13 +12161,17 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - prepared_statement_t> prepare(get_optional_t g) { + prepared_statement_t> prepare(get_optional_t get_) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(g, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(get_, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(g), stmt, con}; + return {std::move(get_), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -11090,7 +12184,11 @@ namespace sqlite_orm { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(upd, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(upd, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(upd), stmt, con}; } else { @@ -11104,7 +12202,11 @@ namespace sqlite_orm { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(rem, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(rem, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(rem), stmt, con}; } else { @@ -11115,10 +12217,16 @@ namespace sqlite_orm { template prepared_statement_t> prepare(insert_t ins) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(ins, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(ins, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(ins), stmt, con}; } else { @@ -11131,8 +12239,14 @@ namespace sqlite_orm { prepared_statement_t> prepare(replace_t rep) { auto con = this->get_connection(); sqlite3_stmt *stmt; + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); auto db = con.get(); - auto query = this->string_from_expression(rep, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(rep, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(rep), stmt, con}; } else { @@ -11142,13 +12256,17 @@ namespace sqlite_orm { } template - prepared_statement_t> prepare(insert_range_t ins) { + prepared_statement_t> prepare(insert_range_t statement) { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(ins, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(statement, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return {std::move(ins), stmt, con}; + return {std::move(statement), stmt, con}; } else { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -11160,7 +12278,11 @@ namespace sqlite_orm { auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(rep, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(rep, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(rep), stmt, con}; } else { @@ -11171,10 +12293,16 @@ namespace sqlite_orm { template prepared_statement_t> prepare(insert_explicit ins) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); auto con = this->get_connection(); sqlite3_stmt *stmt; auto db = con.get(); - auto query = this->string_from_expression(ins, false); + using context_t = serializator_context; + context_t context{this->impl}; + context.skip_table_name = false; + context.replace_bindable_with_question = true; + auto query = serialize(ins, context); if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { return {std::move(ins), stmt, con}; } else { @@ -11192,13 +12320,13 @@ namespace sqlite_orm { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); auto &o = statement.t.obj; sqlite3_reset(stmt); - iterate_tuple(statement.t.columns.columns, [&o, &index, &stmt, &impl, db](auto &m) { + iterate_tuple(statement.t.columns.columns, [&o, &index, &stmt, &tImpl, db](auto &m) { using column_type = typename std::decay::type; using field_type = typename column_result_t::type; - const field_type *value = impl.table.template get_object_field_pointer(o, m); + const field_type *value = tImpl.table.template get_object_field_pointer(o, m); if(SQLITE_OK != statement_binder().bind(stmt, index++, *value)) { throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), sqlite3_errmsg(db)); @@ -11217,7 +12345,7 @@ namespace sqlite_orm { using statement_type = typename std::decay::type; using expression_type = typename statement_type::expression_type; using object_type = typename expression_type::object_type; - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); auto index = 1; auto con = this->get_connection(); auto db = con.get(); @@ -11225,7 +12353,7 @@ namespace sqlite_orm { sqlite3_reset(stmt); for(auto it = statement.t.range.first; it != statement.t.range.second; ++it) { auto &o = *it; - impl.table.for_each_column([&o, &index, &stmt, db](auto &c) { + tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; if(c.member_pointer) { @@ -11262,11 +12390,11 @@ namespace sqlite_orm { auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); sqlite3_reset(stmt); for(auto it = statement.t.range.first; it != statement.t.range.second; ++it) { auto &o = *it; - impl.table.for_each_column([&o, &index, &stmt, db](auto &c) { + tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { if(!c.template has>()) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; @@ -11307,9 +12435,9 @@ namespace sqlite_orm { auto stmt = statement.stmt; auto index = 1; auto &o = get_object(statement.t); - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); sqlite3_reset(stmt); - impl.table.for_each_column([&o, &index, &stmt, db](auto &c) { + tImpl.table.for_each_column([&o, &index, &stmt, db](auto &c) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; if(c.member_pointer) { @@ -11344,12 +12472,12 @@ namespace sqlite_orm { auto db = con.get(); auto stmt = statement.stmt; auto index = 1; - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); auto &o = get_object(statement.t); - auto compositeKeyColumnNames = impl.table.composite_key_columns_names(); + auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); sqlite3_reset(stmt); - impl.table.for_each_column([&o, &index, &stmt, &impl, &compositeKeyColumnNames, db](auto &c) { - if(impl.table._without_rowid || !c.template has>()) { + tImpl.table.for_each_column([&o, &index, &stmt, &tImpl, &compositeKeyColumnNames, db](auto &c) { + if(tImpl.table._without_rowid || !c.template has>()) { auto it = std::find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); if(it == compositeKeyColumnNames.end()) { using column_type = typename std::decay::type; @@ -11411,12 +12539,12 @@ namespace sqlite_orm { using object_type = typename expression_object_type::type; auto con = this->get_connection(); auto db = con.get(); - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); auto stmt = statement.stmt; auto index = 1; auto &o = get_object(statement.t); sqlite3_reset(stmt); - impl.table.for_each_column([&o, stmt, &index, db](auto &c) { + tImpl.table.for_each_column([&o, stmt, &index, db](auto &c) { if(!c.template has>()) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; @@ -11438,7 +12566,7 @@ namespace sqlite_orm { } } }); - impl.table.for_each_column([&o, stmt, &index, db](auto &c) { + tImpl.table.for_each_column([&o, stmt, &index, db](auto &c) { if(c.template has>()) { using column_type = typename std::decay::type; using field_type = typename column_type::field_type; @@ -11469,7 +12597,7 @@ namespace sqlite_orm { template std::unique_ptr execute(const prepared_statement_t> &statement) { - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; @@ -11486,16 +12614,8 @@ namespace sqlite_orm { switch(stepRes) { case SQLITE_ROW: { auto res = std::make_unique(); - index = 0; - impl.table.for_each_column([&index, &res, stmt](auto &c) { - using field_type = typename std::decay::type::field_type; - auto value = row_extractor().extract(stmt, index++); - if(c.member_pointer) { - (*res).*c.member_pointer = std::move(value); - } else { - ((*res).*(c.setter))(std::move(value)); - } - }); + object_from_column_builder builder{*res, stmt}; + tImpl.table.for_each_column(builder); return res; } break; case SQLITE_DONE: { @@ -11511,7 +12631,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template std::optional execute(const prepared_statement_t> &statement) { - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; @@ -11528,16 +12648,8 @@ namespace sqlite_orm { switch(stepRes) { case SQLITE_ROW: { auto res = std::make_optional(); - index = 0; - impl.table.for_each_column([&index, &res, stmt](auto &c) { - using field_type = typename std::decay::type::field_type; - auto value = row_extractor().extract(stmt, index++); - if(c.member_pointer) { - (*res).*c.member_pointer = std::move(value); - } else { - ((*res).*(c.setter))(std::move(value)); - } - }); + object_from_column_builder builder{res.value(), stmt}; + tImpl.table.for_each_column(builder); return res; } break; case SQLITE_DONE: { @@ -11553,7 +12665,7 @@ namespace sqlite_orm { template T execute(const prepared_statement_t> &statement) { - auto &impl = this->get_impl(); + auto &tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; @@ -11570,17 +12682,8 @@ namespace sqlite_orm { switch(stepRes) { case SQLITE_ROW: { T res; - index = 0; - impl.table.for_each_column([&index, &res, stmt](auto &c) { - using column_type = typename std::decay::type; - using field_type = typename column_type::field_type; - auto value = row_extractor().extract(stmt, index++); - if(c.member_pointer) { - res.*c.member_pointer = std::move(value); - } else { - ((res).*(c.setter))(std::move(value)); - } - }); + object_from_column_builder builder{res, stmt}; + tImpl.table.for_each_column(builder); return res; } break; case SQLITE_DONE: { @@ -11665,12 +12768,18 @@ namespace sqlite_orm { } }); std::vector res; + auto tableInfoPointer = this->impl.template find_table(); int stepRes; do { stepRes = sqlite3_step(stmt); switch(stepRes) { case SQLITE_ROW: { - res.push_back(row_extractor().extract(stmt, 0)); + using table_info_pointer_t = typename std::remove_pointer::type; + using table_info_t = typename std::decay::type; + row_extractor_builder::value, table_info_t> + builder; + auto rowExtractor = builder(tableInfoPointer); + res.push_back(rowExtractor.extract(stmt, 0)); } break; case SQLITE_DONE: break; @@ -11683,9 +12792,9 @@ namespace sqlite_orm { return res; } - template - std::vector execute(const prepared_statement_t> &statement) { - auto &impl = this->get_impl(); + template + R execute(const prepared_statement_t> &statement) { + auto &tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; @@ -11699,23 +12808,15 @@ namespace sqlite_orm { sqlite3_errmsg(db)); } }); - std::vector res; + R res; int stepRes; do { stepRes = sqlite3_step(stmt); switch(stepRes) { case SQLITE_ROW: { T obj; - auto index = 0; - impl.table.for_each_column([&index, &obj, stmt](auto &c) { - using field_type = typename std::decay::type::field_type; - auto value = row_extractor().extract(stmt, index++); - if(c.member_pointer) { - obj.*c.member_pointer = std::move(value); - } else { - ((obj).*(c.setter))(std::move(value)); - } - }); + object_from_column_builder builder{obj, stmt}; + tImpl.table.for_each_column(builder); res.push_back(std::move(obj)); } break; case SQLITE_DONE: @@ -11729,10 +12830,9 @@ namespace sqlite_orm { return res; } - template - std::vector> - execute(const prepared_statement_t> &statement) { - auto &impl = this->get_impl(); + template + R execute(const prepared_statement_t> &statement) { + auto &tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; @@ -11746,24 +12846,16 @@ namespace sqlite_orm { sqlite3_errmsg(db)); } }); - std::vector> res; + R res; int stepRes; do { stepRes = sqlite3_step(stmt); switch(stepRes) { case SQLITE_ROW: { auto obj = std::make_unique(); - auto index = 0; - impl.table.for_each_column([&index, &obj, stmt](auto &c) { - using field_type = typename std::decay::type::field_type; - auto value = row_extractor().extract(stmt, index++); - if(c.member_pointer) { - (*obj).*c.member_pointer = std::move(value); - } else { - ((*obj).*(c.setter))(std::move(value)); - } - }); - res.push_back(std::move(obj)); + object_from_column_builder builder{*obj, stmt}; + tImpl.table.for_each_column(builder); + res.push_back(move(obj)); } break; case SQLITE_DONE: break; @@ -11777,10 +12869,9 @@ namespace sqlite_orm { } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - std::vector> - execute(const prepared_statement_t> &statement) { - auto &impl = this->get_impl(); + template + R execute(const prepared_statement_t> &statement) { + auto &tImpl = this->get_impl(); auto con = this->get_connection(); auto db = con.get(); auto stmt = statement.stmt; @@ -11794,24 +12885,16 @@ namespace sqlite_orm { sqlite3_errmsg(db)); } }); - std::vector> res; + R res; int stepRes; do { stepRes = sqlite3_step(stmt); switch(stepRes) { case SQLITE_ROW: { auto obj = std::make_optional(); - auto index = 0; - impl.table.for_each_column([&index, &obj, stmt](auto &c) { - using field_type = typename std::decay::type::field_type; - auto value = row_extractor().extract(stmt, index++); - if(c.member_pointer) { - (*obj).*c.member_pointer = std::move(value); - } else { - ((*obj).*(c.setter))(std::move(value)); - } - }); - res.push_back(std::move(obj)); + object_from_column_builder builder{*obj, stmt}; + tImpl.table.for_each_column(builder); + res.push_back(move(obj)); } break; case SQLITE_DONE: break; @@ -11895,14 +12978,13 @@ __pragma(pop_macro("min")) }; template - struct node_tuple, void> { - using node_type = conditions::where_t; + struct node_tuple, void> { + using node_type = where_t; using type = typename node_tuple::type; }; template - struct node_tuple::value>::type> { + struct node_tuple::value>::type> { using node_type = T; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; @@ -11928,8 +13010,8 @@ __pragma(pop_macro("min")) }; template - struct node_tuple, void> { - using node_type = conditions::in_t; + struct node_tuple, void> { + using node_type = in_t; using left_tuple = typename node_tuple::type; using right_tuple = typename node_tuple::type; using type = typename conc_tuple::type; @@ -11953,9 +13035,9 @@ __pragma(pop_macro("min")) using type = typename conc_tuple::type; }; - template - struct node_tuple, void> { - using node_type = get_all_t; + template + struct node_tuple, void> { + using node_type = get_all_t; using type = typename conc_tuple::type...>::type; }; @@ -11988,20 +13070,20 @@ __pragma(pop_macro("min")) }; template - struct node_tuple, void> { - using node_type = conditions::having_t; + struct node_tuple, void> { + using node_type = having_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::cast_t; + struct node_tuple, void> { + using node_type = cast_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::exists_t; + struct node_tuple, void> { + using node_type = exists_t; using type = typename node_tuple::type; }; @@ -12018,8 +13100,8 @@ __pragma(pop_macro("min")) }; template - struct node_tuple, void> { - using node_type = conditions::like_t; + struct node_tuple, void> { + using node_type = like_t; using arg_tuple = typename node_tuple::type; using pattern_tuple = typename node_tuple::type; using escape_tuple = typename node_tuple::type; @@ -12027,16 +13109,16 @@ __pragma(pop_macro("min")) }; template - struct node_tuple, void> { - using node_type = conditions::glob_t; + struct node_tuple, void> { + using node_type = glob_t; using arg_tuple = typename node_tuple::type; using pattern_tuple = typename node_tuple::type; using type = typename conc_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::between_t; + struct node_tuple, void> { + using node_type = between_t; using expression_tuple = typename node_tuple::type; using lower_tuple = typename node_tuple::type; using upper_tuple = typename node_tuple::type; @@ -12044,62 +13126,62 @@ __pragma(pop_macro("min")) }; template - struct node_tuple, void> { - using node_type = conditions::named_collate; + struct node_tuple, void> { + using node_type = named_collate; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::is_null_t; + struct node_tuple, void> { + using node_type = is_null_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::is_not_null_t; + struct node_tuple, void> { + using node_type = is_not_null_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::negated_condition_t; + struct node_tuple, void> { + using node_type = negated_condition_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = core_functions::core_function_t; + struct node_tuple, void> { + using node_type = core_function_t; using type = typename conc_tuple::type...>::type; }; template - struct node_tuple, void> { - using node_type = conditions::left_join_t; + struct node_tuple, void> { + using node_type = left_join_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::on_t; + struct node_tuple, void> { + using node_type = on_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::join_t; + struct node_tuple, void> { + using node_type = join_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::left_outer_join_t; + struct node_tuple, void> { + using node_type = left_outer_join_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::inner_join_t; + struct node_tuple, void> { + using node_type = inner_join_t; using type = typename node_tuple::type; }; @@ -12127,20 +13209,20 @@ __pragma(pop_macro("min")) }; template - struct node_tuple, void> { - using node_type = conditions::limit_t; + struct node_tuple, void> { + using node_type = limit_t; using type = typename node_tuple::type; }; template - struct node_tuple, void> { - using node_type = conditions::limit_t; + struct node_tuple, void> { + using node_type = limit_t; using type = typename conc_tuple::type, typename node_tuple::type>::type; }; template - struct node_tuple, void> { - using node_type = conditions::limit_t; + struct node_tuple, void> { + using node_type = limit_t; using type = typename conc_tuple::type, typename node_tuple::type>::type; }; } @@ -12284,8 +13366,8 @@ namespace sqlite_orm { ++index; } if(index == N) { - internal::static_if{}>([](auto &result, auto &node) { - result = const_cast::type>(&node); + internal::static_if{}>([](auto &r, auto &n) { + r = const_cast::type>(&n); })(result, node); } }); @@ -12307,130 +13389,11 @@ namespace sqlite_orm { ++index; } if(index == N) { - internal::static_if{}>([](auto &result, auto &node) { - result = const_cast::type>(&node); + internal::static_if{}>([](auto &r, auto &n) { + r = const_cast::type>(&n); })(result, node); } }); return internal::get_ref(*result); } } -#pragma once - -#include -#include // std::string - -// #include "core_functions.h" - -// #include "constraints.h" - -namespace sqlite_orm { - - namespace internal { - - template - struct statement_serializator { - using statement_type = T; - - std::string operator()(const statement_type &t) const { - std::stringstream ss; - ss << t; - return ss.str(); - } - }; - - template - std::string serialize(const T &t) { - statement_serializator serializator; - return serializator(t); - } - - template - struct statement_serializator, void> { - using statement_type = core_functions::core_function_t; - - std::string operator()(const statement_type &c) const { - std::stringstream ss; - ss << static_cast(c) << "("; - std::vector args; - using args_type = typename std::decay::type::args_type; - args.reserve(std::tuple_size::value); - iterate_tuple(c.args, [&args](auto &v) { - args.push_back(serialize(v)); - }); - for(size_t i = 0; i < args.size(); ++i) { - ss << args[i]; - if(i < args.size() - 1) { - ss << ", "; - } - } - ss << ")"; - return ss.str(); - } - }; - - template<> - struct statement_serializator { - using statement_type = constraints::autoincrement_t; - - std::string operator()(const statement_type &c) const { - return static_cast(c); - } - }; - - template - struct statement_serializator, void> { - using statement_type = constraints::primary_key_t; - - std::string operator()(const statement_type &c) const { - return static_cast(c); - } - }; - - template<> - struct statement_serializator { - using statement_type = constraints::unique_t; - - std::string operator()(const statement_type &c) const { - return static_cast(c); - } - }; - - template<> - struct statement_serializator { - using statement_type = constraints::collate_t; - - std::string operator()(const statement_type &c) const { - return static_cast(c); - } - }; - - template - struct statement_serializator, void> { - using statement_type = constraints::default_t; - - std::string operator()(const statement_type &c) const { - return static_cast(c) + " (" + serialize(c.value) + ")"; - } - }; - - template<> - struct statement_serializator { - using statement_type = std::string; - - std::string operator()(const statement_type &c) const { - return "\"" + c + "\""; - } - }; - - template<> - struct statement_serializator { - using statement_type = const char *; - - std::string operator()(const char *c) const { - return std::string("'") + c + "'"; - } - }; - - } -} diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt new file mode 100644 index 000000000..045194261 --- /dev/null +++ b/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/prepare-osx-with-clang.sh b/prepare-osx-with-clang.sh index 267cc3253..c824f65d2 100755 --- a/prepare-osx-with-clang.sh +++ b/prepare-osx-with-clang.sh @@ -6,4 +6,4 @@ set -e brew install llvm brew info llvm export PATH="/usr/local/opt/llvm/bin:$PATH" -echo 'export PATH="/usr/local/opt/llvm/bin:$PATH"' >> ~/.bash_profile +echo 'export PATH="/usr/local/opt/llvm/bin:$PATH"' >> ~/.zshenv diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 578cbed58..fd41e703e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,7 +10,7 @@ else() add_subdirectory(third_party/sqlite) endif() -add_executable(unit_tests tests.cpp tests2.cpp tests3.cpp tests4.cpp tests4.cpp private_getters_tests.cpp pragma_tests.cpp explicit_columns.cpp core_functions_tests.cpp composite_key.cpp static_tests.cpp operators.cpp operators/like.cpp operators/glob.cpp operators/in.cpp operators/cast.cpp operators/is_null.cpp dynamic_order_by.cpp prepared_statement_tests/select.cpp prepared_statement_tests/get_all.cpp prepared_statement_tests/get_all_pointer.cpp prepared_statement_tests/get_all_optional.cpp prepared_statement_tests/update_all.cpp prepared_statement_tests/remove_all.cpp prepared_statement_tests/get.cpp prepared_statement_tests/get_pointer.cpp prepared_statement_tests/get_optional.cpp prepared_statement_tests/update.cpp prepared_statement_tests/remove.cpp prepared_statement_tests/insert.cpp prepared_statement_tests/replace.cpp prepared_statement_tests/insert_range.cpp prepared_statement_tests/replace_range.cpp prepared_statement_tests/insert_explicit.cpp pragma_tests.cpp simple_query.cpp static_tests/is_bindable.cpp static_tests/arithmetic_operators_result_type.cpp static_tests/tuple_conc.cpp static_tests/node_tuple.cpp static_tests/bindable_filter.cpp static_tests/count_tuple.cpp constraints/default.cpp constraints/foreign_key.cpp) +add_executable(unit_tests tests.cpp tests2.cpp tests3.cpp tests4.cpp tests5.cpp private_getters_tests.cpp pragma_tests.cpp explicit_columns.cpp core_functions_tests.cpp index_tests.cpp constraints/composite_key.cpp static_tests.cpp operators/arithmetic_operators.cpp operators/like.cpp operators/glob.cpp operators/in.cpp operators/cast.cpp operators/is_null.cpp operators/not_operator.cpp operators/bitwise.cpp dynamic_order_by.cpp prepared_statement_tests/select.cpp prepared_statement_tests/get_all.cpp prepared_statement_tests/get_all_pointer.cpp prepared_statement_tests/get_all_optional.cpp prepared_statement_tests/update_all.cpp prepared_statement_tests/remove_all.cpp prepared_statement_tests/get.cpp prepared_statement_tests/get_pointer.cpp prepared_statement_tests/get_optional.cpp prepared_statement_tests/update.cpp prepared_statement_tests/remove.cpp prepared_statement_tests/insert.cpp prepared_statement_tests/replace.cpp prepared_statement_tests/insert_range.cpp prepared_statement_tests/replace_range.cpp prepared_statement_tests/insert_explicit.cpp pragma_tests.cpp simple_query.cpp static_tests/is_bindable.cpp static_tests/arithmetic_operators_result_type.cpp static_tests/tuple_conc.cpp static_tests/node_tuple.cpp static_tests/bindable_filter.cpp static_tests/count_tuple.cpp static_tests/member_traits_tests.cpp static_tests/select_return_type.cpp constraints/default.cpp constraints/unique.cpp constraints/foreign_key.cpp constraints/check.cpp table_tests.cpp statement_serializator_tests/primary_key.cpp statement_serializator_tests/column_names.cpp statement_serializator_tests/autoincrement.cpp statement_serializator_tests/arithmetic_operators.cpp statement_serializator_tests/core_functions.cpp statement_serializator_tests/comparison_operators.cpp statement_serializator_tests/unique.cpp statement_serializator_tests/foreign_key.cpp statement_serializator_tests/collate.cpp statement_serializator_tests/check.cpp statement_serializator_tests/index.cpp statement_serializator_tests/indexed_column.cpp unique_cases/get_all_with_two_tables.cpp unique_cases/prepare_get_all_with_case.cpp unique_cases/index_named_table_with_fk.cpp unique_cases/issue525.cpp unique_cases/delete_with_two_fields.cpp unique_cases/join_iterator_ctor_compilation_error.cpp get_all_custom_containers.cpp select_asterisk.cpp backup_tests.cpp transaction_tests.cpp) if(SQLITE_ORM_OMITS_CODECVT) @@ -18,10 +18,7 @@ if(SQLITE_ORM_OMITS_CODECVT) target_compile_definitions(unit_tests PRIVATE SQLITE_ORM_OMITS_CODECVT=1) endif() -find_package(Catch2 REQUIRED) -target_link_libraries(unit_tests PRIVATE sqlite_orm sqlite3 Catch2::Catch2) - -enable_testing() +target_link_libraries(unit_tests PRIVATE sqlite_orm Catch2::Catch2) add_test(NAME "All_in_one_unit_test" COMMAND unit_tests diff --git a/tests/backup_tests.cpp b/tests/backup_tests.cpp index 990692682..e20c68b38 100644 --- a/tests/backup_tests.cpp +++ b/tests/backup_tests.cpp @@ -40,7 +40,8 @@ TEST_CASE("backup") { filename, make_table(usersTableName, make_column("id", &User::id, primary_key()), make_column("name", &User::name))); }; - const std::string backupFilename = "backup.sqlite"; + 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); diff --git a/tests/constraints/check.cpp b/tests/constraints/check.cpp new file mode 100644 index 000000000..083c4da17 --- /dev/null +++ b/tests/constraints/check.cpp @@ -0,0 +1,40 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("check") { + { + 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(); + } + { + struct Book { + int id = 0; + std::string name; + std::string pubName; + int price = 0; + }; + 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(); + } +} diff --git a/tests/composite_key.cpp b/tests/constraints/composite_key.cpp similarity index 100% rename from tests/composite_key.cpp rename to tests/constraints/composite_key.cpp diff --git a/tests/constraints/foreign_key.cpp b/tests/constraints/foreign_key.cpp index 1003644a0..328e5ce5e 100644 --- a/tests/constraints/foreign_key.cpp +++ b/tests/constraints/foreign_key.cpp @@ -38,8 +38,8 @@ TEST_CASE("Foreign key") { foreign_key(&Visit::location).references(&Location::id))); storage.sync_schema(); - int fromDate = int(time(nullptr)); - int toDate = int(time(nullptr)); + 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), diff --git a/tests/constraints/unique.cpp b/tests/constraints/unique.cpp new file mode 100644 index 000000000..f207d70c0 --- /dev/null +++ b/tests/constraints/unique.cpp @@ -0,0 +1,65 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Unique") { + + struct Contact { + int id = 0; + std::string firstName; + std::string lastName; + std::string email; + }; + struct Shape { + int id = 0; + std::string backgroundColor; + std::string foregroundColor; + }; + struct List { + int id = 0; + std::unique_ptr email; + }; + + 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"}); + + try { + storage.insert(Contact{0, "Johnny", "Doe", "john.doe@gmail.com"}); + REQUIRE(false); + } catch(const std::system_error &e) { + //.. + } catch(...) { + REQUIRE(false); + } + + storage.insert(Shape{0, "red", "green"}); + storage.insert(Shape{0, "red", "blue"}); + try { + storage.insert(Shape{0, "red", "green"}); + REQUIRE(false); + } catch(const std::system_error &e) { + //.. + } catch(...) { + REQUIRE(false); + } + + std::vector lists; + lists.push_back(List{0, nullptr}); + lists.push_back(List{0, nullptr}); + storage.insert_range(lists.begin(), lists.end()); +} diff --git a/tests/core_functions_tests.cpp b/tests/core_functions_tests.cpp index f8edb59e2..0ea38cae0 100644 --- a/tests/core_functions_tests.cpp +++ b/tests/core_functions_tests.cpp @@ -72,46 +72,6 @@ TEST_CASE("zeroblob") { } } -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()); -} - #if SQLITE_VERSION_NUMBER >= 3007016 TEST_CASE("char") { auto storage = make_storage({}); @@ -182,3 +142,254 @@ TEST_CASE("abs") { REQUIRE(rows.front()); REQUIRE(*rows.front() == 10); } + +TEST_CASE("hex") { + auto storage = make_storage({}); + { + auto rows = storage.select(hex(67)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "3637"); + } + { + auto rows = storage.select(hex("ä")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "C3A4"); + } + { + auto rows = storage.select(hex(nullptr)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == std::string()); + } +} + +TEST_CASE("quote") { + using Catch::Matchers::UnorderedEquals; + struct Department { + int id = 0; + std::string name; + int managerId = 0; + int locationId = 0; + }; + auto storage = make_storage({}, + make_table("departments", + make_column("department_id", &Department::id, primary_key()), + make_column("department_name", &Department::name), + make_column("manager_id", &Department::managerId), + make_column("location_id", &Department::locationId))); + storage.sync_schema(); + storage.replace(Department{10, "Administration", 200, 1700}); + storage.replace(Department{20, "Marketing", 201, 1800}); + storage.replace(Department{30, "Purchasing", 114, 1700}); + storage.replace(Department{40, "Human Resources", 203, 2400}); + storage.replace(Department{50, "Shipping", 121, 1500}); + storage.replace(Department{60, "IT", 103, 1400}); + storage.replace(Department{70, "Public Relation", 204, 2700}); + storage.replace(Department{80, "Sales", 145, 2500}); + storage.replace(Department{90, "Executive", 100, 1700}); + storage.replace(Department{100, "Finance", 108, 1700}); + storage.replace(Department{110, "Accounting", 205, 1700}); + storage.replace(Department{120, "Treasury", 0, 1700}); + storage.replace(Department{130, "Corporate Tax", 0, 1700}); + storage.replace(Department{140, "Control And Cre", 0, 1700}); + storage.replace(Department{150, "Shareholder Ser", 0, 1700}); + storage.replace(Department{160, "Benefits", 0, 1700}); + storage.replace(Department{170, "Manufacturing", 0, 1700}); + storage.replace(Department{180, "Construction", 0, 1700}); + storage.replace(Department{190, "Contracting", 0, 1700}); + storage.replace(Department{200, "Operations", 0, 1700}); + storage.replace(Department{210, "IT Support", 0, 1700}); + storage.replace(Department{220, "NOC", 0, 1700}); + storage.replace(Department{230, "IT Helpdesk", 0, 1700}); + storage.replace(Department{240, "Government Sale", 0, 1700}); + storage.replace(Department{250, "Retail Sales", 0, 1700}); + storage.replace(Department{260, "Recruiting", 0, 1700}); + storage.replace(Department{270, "Payroll", 0, 1700}); + { + auto rows = storage.select(quote("hi")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "'hi'"); + } + { + auto rows = + storage.select(columns(&Department::name, quote(&Department::name)), where(c(&Department::id) > 150)); + std::vector> expected; + expected.push_back(std::make_tuple("Benefits", "'Benefits'")); + expected.push_back(std::make_tuple("Manufacturing", "'Manufacturing'")); + expected.push_back(std::make_tuple("Construction", "'Construction'")); + expected.push_back(std::make_tuple("Contracting", "'Contracting'")); + expected.push_back(std::make_tuple("Operations", "'Operations'")); + expected.push_back(std::make_tuple("IT Support", "'IT Support'")); + expected.push_back(std::make_tuple("NOC", "'NOC'")); + expected.push_back(std::make_tuple("IT Helpdesk", "'IT Helpdesk'")); + expected.push_back(std::make_tuple("Government Sale", "'Government Sale'")); + expected.push_back(std::make_tuple("Retail Sales", "'Retail Sales'")); + expected.push_back(std::make_tuple("Recruiting", "'Recruiting'")); + expected.push_back(std::make_tuple("Payroll", "'Payroll'")); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } +} + +TEST_CASE("randomblob") { + auto storage = make_storage({}); + for(auto i = 0; i < 20; ++i) { + auto blobLength = i + 1; + auto rows = storage.select(randomblob(blobLength)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front().size() == size_t(blobLength)); + } +} + +TEST_CASE("instr") { + using Catch::Matchers::UnorderedEquals; + + struct Employee { + int id = 0; + std::string firstName; + std::string lastName; + std::string address; + }; + + struct sw : alias_tag { + static const std::string &get() { + static const std::string res = "sw"; + return res; + } + }; + auto storage = make_storage({}, + make_table("employees", + make_column("id", &Employee::id, primary_key()), + make_column("first_name", &Employee::firstName), + make_column("last_name", &Employee::lastName), + make_column("address", &Employee::address))); + storage.sync_schema(); + { + auto rows = storage.select(instr("SQLite Tutorial", "Tutorial")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == 8); + } + { + auto rows = storage.select(instr("SQLite Tutorial", "I")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == 0); + } + Employee nancy{1, "Nancy", "Edwards", "825 8 Ave SW"}; + Employee jane{2, "Jane", "Peacock", "1111 6 Ave SW"}; + Employee margaret{3, "Margaret", "Park", "683 10 Street SW"}; + Employee patrick{4, "Patrick", "Jane", "Sacramento Empty House"}; + Employee teresa{5, "Terese", "Lisbon", "Secramento Middle of Nowhere"}; + storage.replace(nancy); + storage.replace(jane); + storage.replace(margaret); + storage.replace(patrick); + storage.replace(teresa); + { + auto rows = storage.select( + columns(&Employee::lastName, &Employee::firstName, &Employee::address, instr(&Employee::address, "SW"))); + std::vector> expected; + expected.push_back(std::make_tuple(nancy.lastName, nancy.firstName, nancy.address, 11)); + expected.push_back(std::make_tuple(jane.lastName, jane.firstName, jane.address, 12)); + expected.push_back(std::make_tuple(margaret.lastName, margaret.firstName, margaret.address, 15)); + expected.push_back(std::make_tuple(patrick.lastName, patrick.firstName, patrick.address, 0)); + expected.push_back(std::make_tuple(teresa.lastName, teresa.firstName, teresa.address, 0)); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } + { + auto rows = storage.select(columns(&Employee::lastName, + &Employee::firstName, + &Employee::address, + as(instr(&Employee::address, "SW"))), + where(greater_than(get(), 0))); + std::vector> expected; + expected.push_back(std::make_tuple(nancy.lastName, nancy.firstName, nancy.address, 11)); + expected.push_back(std::make_tuple(jane.lastName, jane.firstName, jane.address, 12)); + expected.push_back(std::make_tuple(margaret.lastName, margaret.firstName, margaret.address, 15)); + REQUIRE_THAT(rows, UnorderedEquals(expected)); + } +} + +namespace replace_func_local { + struct Contact { + int id = 0; + std::string firstName; + std::string lastName; + std::string phone; + }; + + bool operator==(const Contact &lhs, const Contact &rhs) { + return lhs.id == rhs.id && lhs.firstName == rhs.firstName && lhs.lastName == rhs.lastName && + lhs.phone == rhs.phone; + } +} + +TEST_CASE("replace func") { + using Catch::Matchers::UnorderedEquals; + using namespace replace_func_local; + + auto storage = make_storage({}, + make_table("contacts", + make_column("contact_id", &Contact::id, primary_key()), + make_column("first_name", &Contact::firstName), + make_column("last_name", &Contact::lastName), + make_column("phone", &Contact::phone))); + storage.sync_schema(); + { + auto rows = storage.select(replace("AA B CC AAA", "A", "Z")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "ZZ B CC ZZZ"); + } + { + auto rows = storage.select(replace("This is a cat", "This", "That")); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == "That is a cat"); + } + Contact john{0, "John", "Doe", "410-555-0168"}; + Contact lily{0, "Lily", "Bush", "410-444-9862"}; + john.id = storage.insert(john); + lily.id = storage.insert(lily); + { + auto contacts = storage.get_all(); + std::vector expected; + expected.push_back(john); + expected.push_back(lily); + REQUIRE_THAT(contacts, UnorderedEquals(expected)); + } + storage.update_all(set(c(&Contact::phone) = replace(&Contact::phone, "410", "+1-410"))); + { + auto contacts = storage.get_all(); + john.phone = "+1-410-555-0168"; + lily.phone = "+1-410-444-9862"; + std::vector expected; + expected.push_back(john); + expected.push_back(lily); + REQUIRE_THAT(contacts, UnorderedEquals(expected)); + } +} + +TEST_CASE("round") { + auto storage = make_storage({}); + auto test = [&storage](auto input, double expected) { + auto rows = storage.select(round(input)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == expected); + }; + auto test2 = [&storage](auto inputA, auto inputB, double expected) { + auto rows = storage.select(round(inputA, inputB)); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front() == expected); + }; + test(23.4, 23.0); + test(23.6, 24.0); + test2(23.6985, 2, 23.7); + test2(190.3985, 3, 190.399); + test2(99.9, 0, 100.0); + test2(23.3985, nullptr, 0); // maybe this is an error but noone cares AFAIK + test2(1304.67, -1, 1305.0); + test2(1929.236, 2, 1929.24); + test2(1929.236, 1, 1929.2); + test(1929.236, 1929); + test(0.5, 1); + test2(59.9, 0, 60.0); + test2(-59.9, 0, -60.0); + test2(-4.535, 2, -4.54); + test2(34.4158, -1, 34.0); +} diff --git a/tests/datetime_function_tests.cpp b/tests/datetime_function_tests.cpp new file mode 100644 index 000000000..f360c3204 --- /dev/null +++ b/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/tests/dynamic_order_by.cpp b/tests/dynamic_order_by.cpp index d092e117c..0091ceab3 100644 --- a/tests/dynamic_order_by.cpp +++ b/tests/dynamic_order_by.cpp @@ -28,7 +28,8 @@ TEST_CASE("Dynamic order by") { std::vector expectedIds; SECTION("id") { - orderBy.push_back(order_by(&User::id)); + auto ob = order_by(&User::id); + orderBy.push_back(ob); expectedIds = { 1, 2, diff --git a/tests/explicit_columns.cpp b/tests/explicit_columns.cpp index 665adc6f3..3ea6e77c5 100644 --- a/tests/explicit_columns.cpp +++ b/tests/explicit_columns.cpp @@ -28,7 +28,7 @@ TEST_CASE("Explicit colums") { make_table("tokens", make_column("id", &Token::id, primary_key()), make_column("token", &Token::token), - make_column("used_id", &Token::usedId), + make_column("user_id", &Token::usedId), foreign_key(&Token::usedId).references(column(&User::id)))); storage.sync_schema(); REQUIRE(storage.table_exists("users")); diff --git a/tests/get_all_custom_containers.cpp b/tests/get_all_custom_containers.cpp new file mode 100644 index 000000000..592be9028 --- /dev/null +++ b/tests/get_all_custom_containers.cpp @@ -0,0 +1,131 @@ +#include +#include + +#include +#include + +using namespace sqlite_orm; + +struct User { + int id = 0; + std::string name; +}; + +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_assert(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/tests/index_tests.cpp b/tests/index_tests.cpp new file mode 100644 index 000000000..ccb64f6ad --- /dev/null +++ b/tests/index_tests.cpp @@ -0,0 +1,47 @@ +#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)); + { + auto storage = make_storage({}, make_index("id_index", &User::id), table); + storage.sync_schema(); + } + { + auto storage = make_storage({}, make_index("id_index", indexed_column(&User::id).asc()), table); + storage.sync_schema(); + } + { + auto storage = make_storage({}, make_index("id_index", indexed_column(&User::id).desc()), table); + storage.sync_schema(); + } + { + auto storage = make_storage({}, make_index("name_index", &User::name), table); + storage.sync_schema(); + } + { + auto storage = make_storage({}, make_index("name_index", indexed_column(&User::name)), table); + storage.sync_schema(); + } + { + auto storage = make_storage({}, make_index("name_index", indexed_column(&User::name).collate("binary")), table); + storage.sync_schema(); + } + { + auto storage = + make_storage({}, make_index("name_index", indexed_column(&User::name).collate("binary").asc()), table); + storage.sync_schema(); + } + { + auto storage = + make_storage({}, make_index("name_index", indexed_column(&User::name).collate("binary").desc()), table); + storage.sync_schema(); + } +} diff --git a/tests/operators.cpp b/tests/operators/arithmetic_operators.cpp similarity index 98% rename from tests/operators.cpp rename to tests/operators/arithmetic_operators.cpp index 8cdc970a9..85318eacb 100644 --- a/tests/operators.cpp +++ b/tests/operators/arithmetic_operators.cpp @@ -3,7 +3,7 @@ using namespace sqlite_orm; -TEST_CASE("Operators") { +TEST_CASE("Arithmetic operators") { struct Object { std::string name; int nameLen; @@ -26,7 +26,7 @@ TEST_CASE("Operators") { 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]; @@ -34,7 +34,7 @@ TEST_CASE("Operators") { 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]; @@ -42,7 +42,7 @@ TEST_CASE("Operators") { 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) { @@ -51,7 +51,7 @@ TEST_CASE("Operators") { REQUIRE(row == name + suffix); } } - { + { // || std::string suffix = "ototo"; auto rows = storage.select(columns(conc(&Object::name, suffix))); for(size_t i = 0; i < rows.size(); ++i) { @@ -60,7 +60,7 @@ TEST_CASE("Operators") { 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, @@ -131,7 +131,7 @@ TEST_CASE("Operators") { 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), diff --git a/tests/operators/between.cpp b/tests/operators/between.cpp new file mode 100644 index 000000000..341c64ad5 --- /dev/null +++ b/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, autoincrement(), primary_key()))); + 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/tests/operators/bitwise.cpp b/tests/operators/bitwise.cpp new file mode 100644 index 000000000..3d78fa647 --- /dev/null +++ b/tests/operators/bitwise.cpp @@ -0,0 +1,58 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("bitwise operators") { + struct Entry { + int lhs = 0; + int rhs = 0; + }; + 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/tests/operators/glob.cpp b/tests/operators/glob.cpp index 8ad24bf0d..7b461b892 100644 --- a/tests/operators/glob.cpp +++ b/tests/operators/glob.cpp @@ -25,19 +25,20 @@ TEST_CASE("Glob") { make_column("salary", &Employee::salary), make_column("dept_id", &Employee::deptId))); storage.sync_schema(); - - std::vector employees = { - Employee{1, "Honey", "Patel", 10100, 1}, - Employee{2, "Shweta", "Jariwala", 19300, 2}, - Employee{3, "Vinay", "Jariwala", 35100, 3}, - Employee{4, "Jagruti", "Viras", 9500, 2}, - Employee{5, "Shweta", "Rana", 12000, 3}, - Employee{6, "sonal", "Menpara", 13000, 1}, - Employee{7, "Yamini", "Patel", 10000, 2}, - Employee{8, "Khyati", "Shah", 500000, 3}, - Employee{9, "Shwets", "Jariwala", 19400, 2}, - }; - storage.replace_range(employees.begin(), employees.end()); + { + std::vector employees = { + Employee{1, "Honey", "Patel", 10100, 1}, + Employee{2, "Shweta", "Jariwala", 19300, 2}, + Employee{3, "Vinay", "Jariwala", 35100, 3}, + Employee{4, "Jagruti", "Viras", 9500, 2}, + Employee{5, "Shweta", "Rana", 12000, 3}, + Employee{6, "sonal", "Menpara", 13000, 1}, + Employee{7, "Yamini", "Patel", 10000, 2}, + Employee{8, "Khyati", "Shah", 500000, 3}, + Employee{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) { diff --git a/tests/operators/is_null.cpp b/tests/operators/is_null.cpp index d7137f5ac..5d7b7e6e9 100644 --- a/tests/operators/is_null.cpp +++ b/tests/operators/is_null.cpp @@ -13,10 +13,12 @@ TEST_CASE("Is null") { 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/tests/operators/like.cpp b/tests/operators/like.cpp index 32a23f2a6..4d9d4c308 100644 --- a/tests/operators/like.cpp +++ b/tests/operators/like.cpp @@ -61,14 +61,4 @@ TEST_CASE("Like operator") { REQUIRE(rows.size() == 1); REQUIRE(rows.front() == false); } - { - auto rows = storage.select(like("ototo", "ototo%").escape("%")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == true); - } - { - auto rows = storage.select(like("ototo", "ototo%", "%")); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == true); - } } diff --git a/tests/operators/not_operator.cpp b/tests/operators/not_operator.cpp new file mode 100644 index 000000000..367ae501d --- /dev/null +++ b/tests/operators/not_operator.cpp @@ -0,0 +1,68 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Not operator") { + struct Object { + int id = 0; + }; + + 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/tests/pragma_tests.cpp b/tests/pragma_tests.cpp index 564e68b8b..acec737ad 100644 --- a/tests/pragma_tests.cpp +++ b/tests/pragma_tests.cpp @@ -99,3 +99,22 @@ TEST_CASE("Auto vacuum") { 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); +} diff --git a/tests/prepared_statement_tests/get_all.cpp b/tests/prepared_statement_tests/get_all.cpp index 5093ee179..5f6a05614 100644 --- a/tests/prepared_statement_tests/get_all.cpp +++ b/tests/prepared_statement_tests/get_all.cpp @@ -39,6 +39,7 @@ TEST_CASE("Prepared get all") { { auto statement = storage.prepare(get_all()); + auto str = storage.dump(statement); testSerializing(statement); SECTION("nothing") { //.. @@ -54,6 +55,7 @@ TEST_CASE("Prepared get all") { } { // 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; @@ -84,6 +86,7 @@ TEST_CASE("Prepared get all") { { // 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; @@ -126,6 +129,7 @@ TEST_CASE("Prepared get all") { { // 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); @@ -135,6 +139,7 @@ TEST_CASE("Prepared get all") { 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"); @@ -144,40 +149,45 @@ TEST_CASE("Prepared get all") { 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)))); - 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)); - } + { + { // 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) = 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)); + 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); diff --git a/tests/prepared_statement_tests/get_all_optional.cpp b/tests/prepared_statement_tests/get_all_optional.cpp index e123f79f2..954e49fcc 100644 --- a/tests/prepared_statement_tests/get_all_optional.cpp +++ b/tests/prepared_statement_tests/get_all_optional.cpp @@ -58,18 +58,6 @@ TEST_CASE("Prepared get all optional") { using Statement = decltype(statement); using Expression = Statement::expression_type; using NodeTuple = internal::node_tuple::type; - { - static_assert(std::tuple_size::value == 2, ""); - { - using Arg0 = std::tuple_element<0, NodeTuple>::type; - static_assert(std::is_same::value, ""); - } - { - using Arg1 = std::tuple_element<1, NodeTuple>::type; - static_assert(std::is_same::value, ""); - } - } - using BindTuple = typename internal::bindable_filter::type; { static_assert(std::tuple_size::value == 1, ""); @@ -110,18 +98,6 @@ TEST_CASE("Prepared get all optional") { using Statement = decltype(statement); using Expression = Statement::expression_type; using NodeTuple = internal::node_tuple::type; - { - static_assert(std::tuple_size::value == 2, ""); - { - using Arg0 = std::tuple_element<0, NodeTuple>::type; - static_assert(std::is_same::value, ""); - } - { - using Arg1 = std::tuple_element<1, NodeTuple>::type; - static_assert(std::is_same::value, ""); - } - } - using BindTuple = typename internal::bindable_filter::type; { static_assert(std::tuple_size::value == 1, ""); diff --git a/tests/prepared_statement_tests/get_all_pointer.cpp b/tests/prepared_statement_tests/get_all_pointer.cpp index c3ea39199..0a588f947 100644 --- a/tests/prepared_statement_tests/get_all_pointer.cpp +++ b/tests/prepared_statement_tests/get_all_pointer.cpp @@ -37,7 +37,7 @@ TEST_CASE("Prepared get all pointer") { 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") { @@ -55,23 +55,11 @@ TEST_CASE("Prepared get all pointer") { REQUIRE(*rows[2].get() == expected[2]); } } - { // by val + 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; - { - static_assert(std::tuple_size::value == 2, ""); - { - using Arg0 = std::tuple_element<0, NodeTuple>::type; - static_assert(std::is_same::value, ""); - } - { - using Arg1 = std::tuple_element<1, NodeTuple>::type; - static_assert(std::is_same::value, ""); - } - } - using BindTuple = typename internal::bindable_filter::type; { static_assert(std::tuple_size::value == 1, ""); @@ -106,24 +94,13 @@ TEST_CASE("Prepared get all pointer") { } } } - { // by ref + 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; - { - static_assert(std::tuple_size::value == 2, ""); - { - using Arg0 = std::tuple_element<0, NodeTuple>::type; - static_assert(std::is_same::value, ""); - } - { - using Arg1 = std::tuple_element<1, NodeTuple>::type; - static_assert(std::is_same::value, ""); - } - } - using BindTuple = typename internal::bindable_filter::type; { static_assert(std::tuple_size::value == 1, ""); diff --git a/tests/prepared_statement_tests/prepared_common.h b/tests/prepared_statement_tests/prepared_common.h index c56980923..cbcfd0e00 100644 --- a/tests/prepared_statement_tests/prepared_common.h +++ b/tests/prepared_statement_tests/prepared_common.h @@ -3,6 +3,7 @@ #include // std::string #include // std::ignore #include +#include // std::ostream namespace PreparedStatementTests { struct User { @@ -42,4 +43,8 @@ namespace PreparedStatementTests { std::ignore = normalized; #endif } + + inline std::ostream &operator<<(std::ostream &os, const User &user) { + return os << "{" << user.id << ", " << user.name << "}"; + } } diff --git a/tests/prepared_statement_tests/select.cpp b/tests/prepared_statement_tests/select.cpp index 1b31c7692..97b98e1bf 100644 --- a/tests/prepared_statement_tests/select.cpp +++ b/tests/prepared_statement_tests/select.cpp @@ -40,6 +40,7 @@ TEST_CASE("Prepared select") { {// one simple argument {// by val auto statement = storage.prepare(select(10)); + auto str = storage.dump(statement); REQUIRE(get<0>(statement) == 10); { auto rows = storage.execute(statement); @@ -55,6 +56,7 @@ TEST_CASE("Prepared select") { { // 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); { @@ -63,6 +65,7 @@ TEST_CASE("Prepared select") { } id = 20; REQUIRE(get<0>(statement) == 20); + str = storage.dump(statement); REQUIRE(&get<0>(statement) == &id); { auto rows = storage.execute(statement); @@ -73,6 +76,7 @@ TEST_CASE("Prepared select") { {// two simple arguments {// by val auto statement = storage.prepare(select(columns("ototo", 25))); +auto str = storage.dump(statement); REQUIRE(strcmp(get<0>(statement), "ototo") == 0); REQUIRE(get<1>(statement) == 25); { @@ -84,6 +88,7 @@ REQUIRE(get<1>(statement) == 25); } get<0>(statement) = "Rock"; get<1>(statement) = -15; +str = storage.dump(statement); { auto rows = storage.execute(statement); REQUIRE(rows.size() == 1); @@ -96,6 +101,7 @@ get<1>(statement) = -15; 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); @@ -126,6 +132,7 @@ get<1>(statement) = -15; {// 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); { @@ -137,6 +144,7 @@ REQUIRE(get<1>(statement) == 10); } get<0>(statement) = 4; get<1>(statement) = 2; +str = storage.dump(statement); { auto rows = storage.execute(statement); REQUIRE(rows.size() == 1); @@ -150,6 +158,7 @@ get<1>(statement) = 2; 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); @@ -164,6 +173,7 @@ get<1>(statement) = 2; first = 4; REQUIRE(&get<0>(statement) == &first); id = 2; + str = storage.dump(statement); { auto rows = storage.execute(statement); REQUIRE(rows.size() == 1); @@ -189,6 +199,7 @@ get<1>(statement) = 2; { 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") { //.. @@ -201,6 +212,7 @@ get<1>(statement) = 2; } {{// 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") { @@ -214,6 +226,7 @@ SECTION("execute") { { // 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); @@ -228,6 +241,7 @@ SECTION("execute") { } {{// 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); @@ -244,6 +258,7 @@ SECTION("execute") { 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); @@ -260,6 +275,7 @@ SECTION("execute") { } { auto statement = storage.prepare(select(columns(&User::id, &User::name))); + auto str = storage.dump(statement); testSerializing(statement); SECTION("nothing") { //.. @@ -277,6 +293,7 @@ SECTION("execute") { { // 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") { //.. @@ -294,6 +311,7 @@ SECTION("execute") { 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); diff --git a/tests/prepared_statement_tests/update_all.cpp b/tests/prepared_statement_tests/update_all.cpp index aedb9608d..00efd6c32 100644 --- a/tests/prepared_statement_tests/update_all.cpp +++ b/tests/prepared_statement_tests/update_all.cpp @@ -74,11 +74,11 @@ TEST_CASE("Prepared update all") { expected.push_back("Maître Gims_123"); REQUIRE_THAT(names, UnorderedEquals(expected)); } - auto statement = storage.prepare( + auto statement2 = storage.prepare( update_all(set(c(&User::name) = c(&User::name) || "!"), where(like(&User::name, "T%")))); - REQUIRE(strcmp(get<0>(statement), "!") == 0); - REQUIRE(strcmp(get<1>(statement), "T%") == 0); - storage.execute(statement); + 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; @@ -87,11 +87,11 @@ TEST_CASE("Prepared update all") { expected.push_back("Maître Gims_123"); REQUIRE_THAT(names, UnorderedEquals(expected)); } - get<0>(statement) = "@"; - get<1>(statement) = "Sh%"; - REQUIRE(strcmp(get<0>(statement), "@") == 0); - REQUIRE(strcmp(get<1>(statement), "Sh%") == 0); - storage.execute(statement); + 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; @@ -144,13 +144,13 @@ TEST_CASE("Prepared update all") { } std::string name = "!"; std::string pattern = "T%"; - auto statement = storage.prepare(update_all(set(c(&User::name) = c(&User::name) || std::ref(name)), - where(like(&User::name, std::ref(pattern))))); - REQUIRE(get<0>(statement) == "!"); - REQUIRE(&get<0>(statement) == &name); - REQUIRE(get<1>(statement) == "T%"); - REQUIRE(&get<1>(statement) == &pattern); - storage.execute(statement); + 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; @@ -161,9 +161,9 @@ TEST_CASE("Prepared update all") { } name = "@"; pattern = "Sh%"; - REQUIRE(get<0>(statement) == "@"); - REQUIRE(get<1>(statement) == "Sh%"); - storage.execute(statement); + REQUIRE(get<0>(statement2) == "@"); + REQUIRE(get<1>(statement2) == "Sh%"); + storage.execute(statement2); { auto names = storage.select(&User::name); std::vector expected; diff --git a/tests/private_getters_tests.cpp b/tests/private_getters_tests.cpp index 494b68837..846ef756a 100644 --- a/tests/private_getters_tests.cpp +++ b/tests/private_getters_tests.cpp @@ -16,8 +16,8 @@ TEST_CASE("Issue 343") { return this->id; } - void setId(int id) { - this->id = id; + void setId(int id_) { + this->id = id_; } std::string name; diff --git a/tests/select_asterisk.cpp b/tests/select_asterisk.cpp new file mode 100644 index 000000000..aa973d466 --- /dev/null +++ b/tests/select_asterisk.cpp @@ -0,0 +1,75 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("select asterisk") { + using Catch::Matchers::UnorderedEquals; + + struct Employee { + int id; + std::string name; + int age; + std::string address; // optional + double salary; // optional + + bool operator==(const Employee &other) const { + return this->id == other.id && this->name == other.name && this->age == other.age && + this->address == other.address && this->salary == other.salary; + } + }; + + auto storage = make_storage({}, + make_table("COMPANY", + make_column("ID", &Employee::id, primary_key()), + make_column("NAME", &Employee::name), + make_column("AGE", &Employee::age), + make_column("ADDRESS", &Employee::address), + make_column("SALARY", &Employee::salary))); + storage.sync_schema(); + + // create employees.. + Employee paul{-1, "Paul", 32, "California", 20000.0}; + Employee allen{-1, "Allen", 25, "Texas", 15000.0}; + Employee teddy{-1, "Teddy", 23, "Norway", 20000.0}; + Employee mark{-1, "Mark", 25, "Rich-Mond", 65000.0}; + Employee david{-1, "David", 27, "Texas", 85000.0}; + Employee kim{-1, "Kim", 22, "South-Hall", 45000.0}; + Employee james{-1, "James", 24, "Houston", 10000.0}; + + // insert employees. `insert` function returns id of inserted object.. + paul.id = storage.insert(paul); + allen.id = storage.insert(allen); + teddy.id = storage.insert(teddy); + mark.id = storage.insert(mark); + david.id = storage.insert(david); + kim.id = storage.insert(kim); + james.id = storage.insert(james); + + SECTION("asterisk") { + auto allEmployeesTuples = storage.select(asterisk()); + + std::vector> expected; + + expected.push_back(std::make_tuple(paul.id, "Paul", 32, "California", 20000.0)); + expected.push_back(std::make_tuple(allen.id, "Allen", 25, "Texas", 15000.0)); + expected.push_back(std::make_tuple(teddy.id, "Teddy", 23, "Norway", 20000.0)); + expected.push_back(std::make_tuple(mark.id, "Mark", 25, "Rich-Mond", 65000.0)); + expected.push_back(std::make_tuple(david.id, "David", 27, "Texas", 85000.0)); + expected.push_back(std::make_tuple(kim.id, "Kim", 22, "South-Hall", 45000.0)); + expected.push_back(std::make_tuple(james.id, "James", 24, "Houston", 10000.0)); + REQUIRE_THAT(allEmployeesTuples, UnorderedEquals(expected)); + } + SECTION("object") { + auto allEmployees = storage.select(object()); + std::vector expected; + expected.push_back(paul); + expected.push_back(allen); + expected.push_back(teddy); + expected.push_back(mark); + expected.push_back(david); + expected.push_back(kim); + expected.push_back(james); + REQUIRE_THAT(allEmployees, UnorderedEquals(expected)); + } +} diff --git a/tests/statement_serializator_tests/arithmetic_operators.cpp b/tests/statement_serializator_tests/arithmetic_operators.cpp new file mode 100644 index 000000000..5c82413d5 --- /dev/null +++ b/tests/statement_serializator_tests/arithmetic_operators.cpp @@ -0,0 +1,58 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator arithmetic operators") { + internal::serializator_context_base context; + SECTION("add") { + std::string value; + SECTION("func") { + value = serialize(add(3, 5), context); + } + SECTION("operator") { + value = serialize(c(3) + 5, context); + } + REQUIRE(value == "(3 + 5)"); + } + SECTION("sub") { + std::string value; + SECTION("func") { + value = serialize(sub(5, -9), context); + } + SECTION("operator") { + value = serialize(c(5) - -9, context); + } + REQUIRE(value == "(5 - -9)"); + } + SECTION("mul") { + std::string value; + SECTION("func") { + value = serialize(mul(10, 0.5), context); + } + SECTION("operator") { + value = serialize(c(10) * 0.5, context); + } + REQUIRE(value == "(10 * 0.5)"); + } + SECTION("div") { + std::string value; + SECTION("func") { + value = serialize(sqlite_orm::div(10, 2), context); + } + SECTION("operator") { + value = serialize(c(10) / 2, context); + } + REQUIRE(value == "(10 / 2)"); + } + SECTION("mod") { + std::string value; + SECTION("func") { + value = serialize(mod(20, 3), context); + } + SECTION("operator") { + value = serialize(c(20) % 3, context); + } + REQUIRE(value == "(20 % 3)"); + } +} diff --git a/tests/statement_serializator_tests/autoincrement.cpp b/tests/statement_serializator_tests/autoincrement.cpp new file mode 100644 index 000000000..a1e2f4f50 --- /dev/null +++ b/tests/statement_serializator_tests/autoincrement.cpp @@ -0,0 +1,11 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator autoincrement") { + internal::serializator_context_base context; + auto autoinc = autoincrement(); + auto value = serialize(autoinc, context); + REQUIRE(value == "AUTOINCREMENT"); +} diff --git a/tests/statement_serializator_tests/base_types.cpp b/tests/statement_serializator_tests/base_types.cpp new file mode 100644 index 000000000..dbf9ff1db --- /dev/null +++ b/tests/statement_serializator_tests/base_types.cpp @@ -0,0 +1,32 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator base types") { + internal::serializator_context_base context; + SECTION("std::string") { + std::string str("calma"); + SECTION("no question") { + auto value = serialize(str, context); + REQUIRE(value == "\"calma\""); + } + SECTION("question") { + context.replace_bindable_with_question = true; + auto value = serialize(str, context); + REQUIRE(value == "?"); + } + } + SECTION("const char *") { + const char *str = "baby"; + SECTION("no question") { + auto value = serialize(str, context); + REQUIRE(value == "\'baby\'"); + } + SECTION("question") { + context.replace_bindable_with_question = true; + auto value = serialize(str, context); + REQUIRE(value == "?"); + } + } +} diff --git a/tests/statement_serializator_tests/check.cpp b/tests/statement_serializator_tests/check.cpp new file mode 100644 index 000000000..44482d6c9 --- /dev/null +++ b/tests/statement_serializator_tests/check.cpp @@ -0,0 +1,72 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator 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 storage_impl_t = internal::storage_impl; + + storage_impl_t storageImpl{table}; + + using context_t = internal::serializator_context; + + context_t context{storageImpl}; + std::string value; + std::string expected; + SECTION("with parentheses") { + value = serialize(ch, context); + expected = "CHECK (\"col3\" > 0)"; + } + SECTION("without parentheses") { + context.use_parentheses = false; + value = serialize(ch, context); + expected = "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 storage_impl_t = internal::storage_impl; + + storage_impl_t storageImpl{table}; + + using context_t = internal::serializator_context; + + context_t context{storageImpl}; + std::string value; + std::string expected; + SECTION("with parentheses") { + value = serialize(ch, context); + expected = "CHECK (0 < \"PRICE\")"; + } + SECTION("without parentheses") { + context.use_parentheses = false; + value = serialize(ch, context); + expected = "CHECK 0 < \"PRICE\""; + } + REQUIRE(value == expected); + } +} diff --git a/tests/statement_serializator_tests/collate.cpp b/tests/statement_serializator_tests/collate.cpp new file mode 100644 index 000000000..4d1b97d39 --- /dev/null +++ b/tests/statement_serializator_tests/collate.cpp @@ -0,0 +1,23 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator collate") { + internal::serializator_context_base context; + { + 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/tests/statement_serializator_tests/column_names.cpp b/tests/statement_serializator_tests/column_names.cpp new file mode 100644 index 000000000..40fc3a7be --- /dev/null +++ b/tests/statement_serializator_tests/column_names.cpp @@ -0,0 +1,113 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator 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 storage_impl_t = internal::storage_impl; + storage_impl_t storageImpl{table}; + { + using context_t = internal::serializator_context; + context_t context{storageImpl}; + SECTION("id") { + SECTION("skip table name") { + auto value = serialize(&User::id, context); + REQUIRE(value == "\"id\""); + } + SECTION("don't skip table name") { + context.skip_table_name = false; + auto value = serialize(&User::id, context); + REQUIRE(value == "\"users\".\"id\""); + } + } + SECTION("name") { + auto value = serialize(&User::name, context); + REQUIRE(value == "\"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 = move(value); + } + + private: + int id = 0; + std::string name; + }; + { + auto table = make_table("users", + make_column("id", &User::getId, &User::setId), + make_column("name", &User::getName, &User::setName)); + using storage_impl_t = internal::storage_impl; + storage_impl_t storageImpl{table}; + { + using context_t = internal::serializator_context; + context_t context{storageImpl}; + { + auto value = serialize(&User::getId, context); + REQUIRE(value == "\"id\""); + } + { + auto value = serialize(&User::setId, context); + REQUIRE(value == "\"id\""); + } + { + auto value = serialize(&User::getName, context); + REQUIRE(value == "\"name\""); + } + { + auto value = serialize(&User::setName, context); + REQUIRE(value == "\"name\""); + } + } + } + { // 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 storage_impl_t = internal::storage_impl; + storage_impl_t storageImpl{table}; + { + using context_t = internal::serializator_context; + context_t context{storageImpl}; + { + auto value = serialize(&User::getId, context); + REQUIRE(value == "\"id\""); + } + { + auto value = serialize(&User::setId, context); + REQUIRE(value == "\"id\""); + } + { + auto value = serialize(&User::getName, context); + REQUIRE(value == "\"name\""); + } + { + auto value = serialize(&User::setName, context); + REQUIRE(value == "\"name\""); + } + } + } + } +} diff --git a/tests/statement_serializator_tests/comparison_operators.cpp b/tests/statement_serializator_tests/comparison_operators.cpp new file mode 100644 index 000000000..d4839046a --- /dev/null +++ b/tests/statement_serializator_tests/comparison_operators.cpp @@ -0,0 +1,86 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator comparison operators") { + internal::serializator_context_base context; + SECTION("lesser_than") { + std::string value; + 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); + } + REQUIRE(value == "(4 < 5)"); + } + SECTION("lesser_or_equal") { + std::string value; + 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); + } + REQUIRE(value == "(10 <= 15)"); + } + SECTION("greater_than") { + std::string value; + 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); + } + REQUIRE(value == "(1 > 0.5)"); + } + SECTION("greater_or_equal") { + std::string value; + 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); + } + REQUIRE(value == "(10 >= -5)"); + } + SECTION("is_equal") { + std::string value; + 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); + } + REQUIRE(value == "('ototo' = 'Hey')"); + } + SECTION("is_not_equal") { + std::string value; + 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); + } + REQUIRE(value == "('lala' != 7)"); + } +} diff --git a/tests/statement_serializator_tests/core_functions.cpp b/tests/statement_serializator_tests/core_functions.cpp new file mode 100644 index 000000000..94c1fba17 --- /dev/null +++ b/tests/statement_serializator_tests/core_functions.cpp @@ -0,0 +1,132 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator core functions") { + internal::serializator_context_base context; + { + auto value = serialize(length("hi"), context); + REQUIRE(value == "LENGTH('hi')"); + } + { + auto value = serialize(sqlite_orm::abs(-100), context); + REQUIRE(value == "ABS(-100)"); + } + { + auto value = serialize(lower("dancefloor"), context); + REQUIRE(value == "LOWER('dancefloor')"); + } + { + auto value = serialize(upper("call"), context); + REQUIRE(value == "UPPER('call')"); + } + { + auto value = serialize(changes(), context); + REQUIRE(value == "CHANGES()"); + } + { + auto value = serialize(trim("hey"), context); + REQUIRE(value == "TRIM('hey')"); + } + { + auto value = serialize(trim("hey", "h"), context); + REQUIRE(value == "TRIM('hey', 'h')"); + } + { + auto value = serialize(ltrim("hey"), context); + REQUIRE(value == "LTRIM('hey')"); + } + { + auto value = serialize(ltrim("hey", "h"), context); + REQUIRE(value == "LTRIM('hey', 'h')"); + } + { + auto value = serialize(rtrim("hey"), context); + REQUIRE(value == "RTRIM('hey')"); + } + { + auto value = serialize(rtrim("hey", "h"), context); + REQUIRE(value == "RTRIM('hey', 'h')"); + } + { + auto value = serialize(hex("love"), context); + REQUIRE(value == "HEX('love')"); + } + { + auto value = serialize(quote("one"), context); + REQUIRE(value == "QUOTE('one')"); + } + { + auto value = serialize(randomblob(5), context); + REQUIRE(value == "RANDOMBLOB(5)"); + } + { + auto value = serialize(instr("hi", "i"), context); + REQUIRE(value == "INSTR('hi', 'i')"); + } + { + auto value = serialize(replace("contigo", "o", "a"), context); + REQUIRE(value == "REPLACE('contigo', 'o', 'a')"); + } + { + auto value = serialize(sqlite_orm::round(10.5), context); + REQUIRE(value == "ROUND(10.5)"); + } + { + auto value = serialize(sqlite_orm::round(10.5, 0.5), context); + REQUIRE(value == "ROUND(10.5, 0.5)"); + } +#if SQLITE_VERSION_NUMBER >= 3007016 + { + auto value = serialize(char_(40, 45), context); + REQUIRE(value == "CHAR(40, 45)"); + } + { + auto value = serialize(sqlite_orm::random(), context); + REQUIRE(value == "RANDOM()"); + } +#endif + { + auto value = serialize(coalesce(10, 15), context); + REQUIRE(value == "COALESCE(10, 15)"); + } + { + auto value = serialize(date("now"), context); + REQUIRE(value == "DATE('now')"); + } + { + auto value = serialize(time("12:00", "localtime"), context); + REQUIRE(value == "TIME('12:00', 'localtime')"); + } + { + auto value = serialize(datetime("now"), context); + REQUIRE(value == "DATETIME('now')"); + } + { + auto value = serialize(julianday("now"), context); + REQUIRE(value == "JULIANDAY('now')"); + } + { + auto value = serialize(strftime("%s", "2014-10-07 02:34:56"), context); + REQUIRE(value == "STRFTIME('%s', '2014-10-07 02:34:56')"); + } + { + auto value = serialize(zeroblob(5), context); + REQUIRE(value == "ZEROBLOB(5)"); + } + { + auto value = serialize(substr("Zara", 2), context); + REQUIRE(value == "SUBSTR('Zara', 2)"); + } + { + auto value = serialize(substr("Natasha", 3, 2), context); + REQUIRE(value == "SUBSTR('Natasha', 3, 2)"); + } + { +#ifdef SQLITE_SOUNDEX + auto value = serialize(soundex("Vaso"), context); + REQUIRE(value == "SOUNDEX('Vaso')"); +#endif + } +} diff --git a/tests/statement_serializator_tests/default.cpp b/tests/statement_serializator_tests/default.cpp new file mode 100644 index 000000000..f7a9146c1 --- /dev/null +++ b/tests/statement_serializator_tests/default.cpp @@ -0,0 +1,23 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator default") { + internal::serializator_context_base context; + { // int literal + auto def = default_value(1); + auto value = serialize(def, context); + REQUIRE(value == "DEFAULT (1)"); + } + { // string literal + auto def = default_value("hi"); + auto value = serialize(def, context); + REQUIRE(value == "DEFAULT ('hi')"); + } + { // func + auto def = default_value(datetime("now")); + auto value = serialize(def, context); + REQUIRE(value == "DEFAULT (DATETIME('now'))"); + } +} diff --git a/tests/statement_serializator_tests/foreign_key.cpp b/tests/statement_serializator_tests/foreign_key.cpp new file mode 100644 index 000000000..564bcd9d1 --- /dev/null +++ b/tests/statement_serializator_tests/foreign_key.cpp @@ -0,0 +1,313 @@ +#include +#include + +using namespace sqlite_orm; + +#if SQLITE_VERSION_NUMBER >= 3006019 + +TEST_CASE("statement_serializator 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); + + 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 storage_impl_t = internal::storage_impl; + + storage_impl_t storageImpl{usersTable, visitsTable}; + + using context_t = internal::serializator_context; + + context_t context{storageImpl}; + auto value = serialize(fk, context); + REQUIRE(value == "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(); + + 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 storage_impl_t = internal::storage_impl; + + storage_impl_t storageImpl{usersTable, visitsTable}; + + using context_t = internal::serializator_context; + + context_t context{storageImpl}; + auto value = serialize(fk, context); + REQUIRE(value == "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_(); + + 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 storage_impl_t = internal::storage_impl; + + storage_impl_t storageImpl{usersTable, visitsTable}; + + using context_t = internal::serializator_context; + + context_t context{storageImpl}; + auto value = serialize(fk, context); + REQUIRE(value == "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(); + + 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 storage_impl_t = internal::storage_impl; + + storage_impl_t storageImpl{usersTable, visitsTable}; + + using context_t = internal::serializator_context; + + context_t context{storageImpl}; + auto value = serialize(fk, context); + REQUIRE(value == "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(); + + 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 storage_impl_t = internal::storage_impl; + + storage_impl_t storageImpl{usersTable, visitsTable}; + + using context_t = internal::serializator_context; + + context_t context{storageImpl}; + auto value = serialize(fk, context); + REQUIRE(value == "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(); + + 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 storage_impl_t = internal::storage_impl; + + storage_impl_t storageImpl{usersTable, visitsTable}; + + using context_t = internal::serializator_context; + + context_t context{storageImpl}; + auto value = serialize(fk, context); + REQUIRE(value == "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(); + + 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 storage_impl_t = internal::storage_impl; + + storage_impl_t storageImpl{usersTable, visitsTable}; + + using context_t = internal::serializator_context; + + context_t context{storageImpl}; + auto value = serialize(fk, context); + REQUIRE(value == "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_(); + + 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 storage_impl_t = internal::storage_impl; + + storage_impl_t storageImpl{usersTable, visitsTable}; + + using context_t = internal::serializator_context; + + context_t context{storageImpl}; + auto value = serialize(fk, context); + REQUIRE(value == "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(); + + 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 storage_impl_t = internal::storage_impl; + + storage_impl_t storageImpl{usersTable, visitsTable}; + + using context_t = internal::serializator_context; + + context_t context{storageImpl}; + auto value = serialize(fk, context); + REQUIRE(value == "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(); + + 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 storage_impl_t = internal::storage_impl; + + storage_impl_t storageImpl{usersTable, visitsTable}; + + using context_t = internal::serializator_context; + + context_t context{storageImpl}; + auto value = serialize(fk, context); + REQUIRE(value == "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(); + + 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 storage_impl_t = internal::storage_impl; + + storage_impl_t storageImpl{usersTable, visitsTable}; + + using context_t = internal::serializator_context; + + context_t context{storageImpl}; + auto value = serialize(fk, context); + REQUIRE(value == "FOREIGN KEY('user_id') REFERENCES 'users'('id') ON DELETE CASCADE"); + } + } + } + SECTION("one to explicit one") { + struct Object { + int id = 0; + }; + + struct User : Object { + std::string name; + + User(decltype(id) id_, decltype(name) name_) : Object{id_}, name(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)); + 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 storage_impl_t = internal::storage_impl; + + storage_impl_t storageImpl{usersTable, tokensTable}; + + using context_t = internal::serializator_context; + + context_t context{storageImpl}; + auto value = serialize(fk, context); + REQUIRE(value == "FOREIGN KEY('user_id') REFERENCES 'users'('id')"); + } + SECTION("composite key") { + 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); + 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)); + auto visitsTable = make_table("visits", + make_column("user_id", &UserVisit::userId), + make_column("user_first_name", &UserVisit::userFirstName), + make_column("time", &UserVisit::time), + fk); + + using storage_impl_t = internal::storage_impl; + + storage_impl_t storageImpl{usersTable, visitsTable}; + + using context_t = internal::serializator_context; + + context_t context{storageImpl}; + auto value = serialize(fk, context); + REQUIRE(value == "FOREIGN KEY('user_id', 'user_first_name') REFERENCES 'users'('id', 'first_name')"); + } +} + +#endif // SQLITE_VERSION_NUMBER diff --git a/tests/statement_serializator_tests/index.cpp b/tests/statement_serializator_tests/index.cpp new file mode 100644 index 000000000..64e247a9a --- /dev/null +++ b/tests/statement_serializator_tests/index.cpp @@ -0,0 +1,19 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator 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 storage_impl_t = internal::storage_impl; + auto storageImpl = storage_impl_t{table}; + using context_t = internal::serializator_context; + context_t context{storageImpl}; + auto index = make_index("id_index", &User::id); + auto value = internal::serialize(index, context); + REQUIRE(value == "CREATE INDEX IF NOT EXISTS 'id_index' ON 'users' ('id')"); +} diff --git a/tests/statement_serializator_tests/indexed_column.cpp b/tests/statement_serializator_tests/indexed_column.cpp new file mode 100644 index 000000000..3736056ac --- /dev/null +++ b/tests/statement_serializator_tests/indexed_column.cpp @@ -0,0 +1,46 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator 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 storage_impl_t = internal::storage_impl; + auto storageImpl = storage_impl_t{table}; + using context_t = internal::serializator_context; + context_t context{storageImpl}; + { + auto column = indexed_column(&User::id); + auto value = internal::serialize(column, context); + REQUIRE(value == "\"id\""); + } + { + auto column = indexed_column(&User::id).asc(); + auto value = internal::serialize(column, context); + REQUIRE(value == "\"id\" ASC"); + } + { + auto column = indexed_column(&User::id).desc(); + auto value = internal::serialize(column, context); + REQUIRE(value == "\"id\" DESC"); + } + { + auto column = indexed_column(&User::id).collate("BINARY"); + auto value = internal::serialize(column, context); + REQUIRE(value == "\"id\" COLLATE BINARY"); + } + { + auto column = indexed_column(&User::name).collate("BINARY").asc(); + auto value = internal::serialize(column, context); + REQUIRE(value == "\"name\" COLLATE BINARY ASC"); + } + { + auto column = indexed_column(&User::name).collate("OTHER").desc(); + auto value = internal::serialize(column, context); + REQUIRE(value == "\"name\" COLLATE OTHER DESC"); + } +} diff --git a/tests/statement_serializator_tests/primary_key.cpp b/tests/statement_serializator_tests/primary_key.cpp new file mode 100644 index 000000000..e6593d19f --- /dev/null +++ b/tests/statement_serializator_tests/primary_key.cpp @@ -0,0 +1,44 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator primary key") { + { // empty pk + internal::serializator_context_base context; + auto pk = primary_key(); + auto value = serialize(pk, context); + REQUIRE(value == "PRIMARY KEY"); + } + { + struct User { + int id = 0; + std::string name; + }; + auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); + using storage_impl_t = internal::storage_impl; + auto storageImpl = storage_impl_t{table}; + using context_t = internal::serializator_context; + context_t context{storageImpl}; + { // single column pk + auto pk = primary_key(&User::id); + auto value = serialize(pk, context); + REQUIRE(value == "PRIMARY KEY(id)"); + } + { // double column pk + auto pk = primary_key(&User::id, &User::name); + auto value = serialize(pk, context); + REQUIRE(value == "PRIMARY KEY(id, name)"); + } + { // empty pk asc + auto pk = primary_key().asc(); + auto value = serialize(pk, context); + REQUIRE(value == "PRIMARY KEY ASC"); + } + { // empty pk desc + auto pk = primary_key().desc(); + auto value = serialize(pk, context); + REQUIRE(value == "PRIMARY KEY DESC"); + } + } +} diff --git a/tests/statement_serializator_tests/rowid.cpp b/tests/statement_serializator_tests/rowid.cpp new file mode 100644 index 000000000..68fa97432 --- /dev/null +++ b/tests/statement_serializator_tests/rowid.cpp @@ -0,0 +1,12 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator rowid") { + { + internal::serializator_context_base context; + auto value = serialize(rowid(), context); + REQUIRE(value == "rowid"); + } +} diff --git a/tests/statement_serializator_tests/unique.cpp b/tests/statement_serializator_tests/unique.cpp new file mode 100644 index 000000000..4f64be198 --- /dev/null +++ b/tests/statement_serializator_tests/unique.cpp @@ -0,0 +1,11 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializator unique") { + internal::serializator_context_base context; + auto un = unique(); + auto value = serialize(un, context); + REQUIRE(value == "UNIQUE"); +} diff --git a/tests/static_if_tests.cpp b/tests/static_if_tests.cpp new file mode 100644 index 000000000..f56fffafb --- /dev/null +++ b/tests/static_if_tests.cpp @@ -0,0 +1,67 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("static_if") { + { // simple true + auto value = 0; + internal::static_if( + [&value] { + value = 1; + }, + [&value] { + value = -1; + })(); + REQUIRE(value == 1); + } + { // simple false + auto value = 0; + internal::static_if( + [&value] { + value = 1; + }, + [&value] { + value = -1; + })(); + REQUIRE(value == -1); + } + { // tuple is empty + auto value = 0; + internal::static_if>{}>( + [&value] { + value = 1; + }, + [&value] { + value = -1; + })(); + REQUIRE(value == 1); + } + { // tuple is not empty + auto value = 0; + internal::static_if>>{}>( + [&value] { + value = 1; + }, + [&value] { + value = -1; + })(); + REQUIRE(value == -1); + } + { + struct User { + std::string name; + }; + auto ch = check(length(&User::name) > 5); + static_assert(!internal::is_column::value, ""); + int called = 0; + internal::static_if{}>( + [&called] { + called = 1; + }, + [&called] { + called = -1; + })(); + REQUIRE(called == -1); + } +} diff --git a/tests/static_tests.cpp b/tests/static_tests.cpp index f78062115..6ae6bd1a9 100644 --- a/tests/static_tests.cpp +++ b/tests/static_tests.cpp @@ -1,44 +1,11 @@ - #include #include #include #include -using namespace sqlite_orm; - -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; -}; +#include "static_tests/static_tests_common.h" -struct Token : Object {}; +using namespace sqlite_orm; TEST_CASE("Column") { { @@ -131,32 +98,32 @@ TEST_CASE("Aggregate function return types") { return this->id; } - void setIdByVal(int id) { - this->id = id; + void setIdByVal(int id_) { + this->id = id_; } std::string getNameByVal() { return this->name; } - void setNameByConstRef(const std::string &name) { - this->name = name; + void setNameByConstRef(const std::string &name_) { + this->name = name_; } const int &getConstIdByRefConst() const { return this->id; } - void setIdByRef(int &id) { - this->id = 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); + void setNameByRef(std::string &name_) { + this->name = std::move(name_); } }; const std::string filename = "static_tests.sqlite"; @@ -271,64 +238,3 @@ TEST_CASE("Compound operators") { static_assert(internal::is_base_of_template::value, "except must be base of compound_operator"); } - -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_assert(std::is_same>::value, "Incorrect select id vector type"); - - using SelectVectorTuple = decltype(storage.select(columns(&User::id))); - auto ids = storage.select(columns(&User::id)); - static_assert(std::is_same::value, ""); - static_assert(std::is_same>>::value, - "Incorrect select id vector type"); - using IdsTuple = SelectVectorTuple::value_type; - static_assert(std::tuple_size::value == 1, "Incorrect tuple size"); - } - { - // test storage traits - struct Visit { - int id; - std::string date; - }; - using namespace sqlite_orm::internal::storage_traits; - - // test type_is_mapped - static_assert(type_is_mapped::value, "User must be mapped to a storage"); - static_assert(!type_is_mapped::value, "User must be mapped to a storage"); - - // test is_storage - static_assert(internal::is_storage::value, "is_storage works incorrectly"); - static_assert(!internal::is_storage::value, "is_storage works incorrectly"); - static_assert(!internal::is_storage::value, "is_storage works incorrectly"); - static_assert(!internal::is_storage::value, "is_storage works incorrectly"); - - auto storage2 = make_storage( - "", - make_table("visits", make_column("id", &Visit::id, primary_key()), make_column("date", &Visit::date))); - - // test storage_columns_count - static_assert(storage_columns_count::value == 1, - "Incorrect storage columns count value"); - static_assert(storage_columns_count::value == 0, - "Incorrect storage columns count value"); - static_assert(storage_columns_count::value == 2, - "Incorrect storage columns count value"); - - // test storage mapped columns - using UserColumnsTuple = storage_mapped_columns::type; - static_assert(std::is_same>::value, - "Incorrect storage_mapped_columns result"); - - using VisitColumsEmptyType = storage_mapped_columns::type; - static_assert(std::is_same>::value, - "Incorrect storage_mapped_columns result"); - - using VisitColumnTypes = storage_mapped_columns::type; - static_assert(std::is_same>::value, - "Incorrect storage_mapped_columns result"); - } -} diff --git a/tests/static_tests/count_tuple.cpp b/tests/static_tests/count_tuple.cpp index e61d5d690..046f80bdd 100644 --- a/tests/static_tests/count_tuple.cpp +++ b/tests/static_tests/count_tuple.cpp @@ -13,44 +13,44 @@ TEST_CASE("count_tuple") { { auto t = std::make_tuple(where(is_equal(&User::id, 5)), limit(5), order_by(&User::name)); using T = decltype(t); - static_assert(count_tuple::value == 1, ""); - static_assert(count_tuple::value == 0, ""); - static_assert(count_tuple::value == 1, ""); - static_assert(count_tuple::value == 1, ""); + static_assert(count_tuple::value == 1, ""); + static_assert(count_tuple::value == 0, ""); + static_assert(count_tuple::value == 1, ""); + static_assert(count_tuple::value == 1, ""); } { auto t = std::make_tuple(where(lesser_than(&User::id, 10)), where(greater_than(&User::id, 5)), group_by(&User::name)); using T = decltype(t); - static_assert(count_tuple::value == 2, ""); - static_assert(count_tuple::value == 1, ""); - static_assert(count_tuple::value == 0, ""); - static_assert(count_tuple::value == 0, ""); + static_assert(count_tuple::value == 2, ""); + static_assert(count_tuple::value == 1, ""); + static_assert(count_tuple::value == 0, ""); + static_assert(count_tuple::value == 0, ""); } { auto t = std::make_tuple(group_by(&User::name), limit(10, offset(5))); using T = decltype(t); - static_assert(count_tuple::value == 0, ""); - static_assert(count_tuple::value == 1, ""); - static_assert(count_tuple::value == 0, ""); - static_assert(count_tuple::value == 1, ""); + static_assert(count_tuple::value == 0, ""); + static_assert(count_tuple::value == 1, ""); + static_assert(count_tuple::value == 0, ""); + static_assert(count_tuple::value == 1, ""); } { auto t = std::make_tuple(where(is_null(&User::name)), order_by(&User::id), multi_order_by(order_by(&User::name))); using T = decltype(t); - static_assert(count_tuple::value == 1, ""); - static_assert(count_tuple::value == 0, ""); - static_assert(count_tuple::value == 2, ""); - static_assert(count_tuple::value == 0, ""); + static_assert(count_tuple::value == 1, ""); + static_assert(count_tuple::value == 0, ""); + static_assert(count_tuple::value == 2, ""); + static_assert(count_tuple::value == 0, ""); } { auto t = std::make_tuple(dynamic_order_by(make_storage(""))); using T = decltype(t); - static_assert(count_tuple::value == 0, ""); - static_assert(count_tuple::value == 0, ""); - static_assert(count_tuple::value == 1, ""); - static_assert(count_tuple::value == 0, ""); + static_assert(count_tuple::value == 0, ""); + static_assert(count_tuple::value == 0, ""); + static_assert(count_tuple::value == 1, ""); + static_assert(count_tuple::value == 0, ""); } } diff --git a/tests/static_tests/member_traits_tests.cpp b/tests/static_tests/member_traits_tests.cpp new file mode 100644 index 000000000..4a5a335c5 --- /dev/null +++ b/tests/static_tests/member_traits_tests.cpp @@ -0,0 +1,109 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("member_traits_tests") { + using internal::field_member_traits; + using internal::getter_traits; + using internal::is_field_member_pointer; + using internal::is_getter; + using internal::is_setter; + using internal::member_traits; + using internal::setter_traits; + using std::is_same; + + 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; + } + }; + + static_assert(is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + static_assert(!is_field_member_pointer::value, ""); + + static_assert(!is_getter::value, ""); + static_assert(is_getter::value, ""); + static_assert(is_getter::value, ""); + static_assert(is_getter::value, ""); + static_assert(!is_getter::value, ""); + static_assert(!is_getter::value, ""); + static_assert(!is_getter::value, ""); + + static_assert(!is_setter::value, ""); + static_assert(!is_setter::value, ""); + static_assert(!is_setter::value, ""); + static_assert(!is_setter::value, ""); + static_assert(is_setter::value, ""); + static_assert(is_setter::value, ""); + static_assert(is_setter::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); + + static_assert(is_same::object_type, User>::value, ""); + static_assert(is_same::field_type, int>::value, ""); +} diff --git a/tests/static_tests/node_tuple.cpp b/tests/static_tests/node_tuple.cpp index abc4e53c3..ca76b65ba 100644 --- a/tests/static_tests/node_tuple.cpp +++ b/tests/static_tests/node_tuple.cpp @@ -41,7 +41,7 @@ TEST_CASE("Node tuple") { } { // binary_condition - using namespace conditions; + using namespace internal; { // 5 < 6.0f auto c = lesser_than(5, 6.0f); using C = decltype(c); @@ -292,7 +292,7 @@ static_assert(is_same>::value, "get_all()"); } } { // having_t - using namespace conditions; + using namespace internal; auto hav = having(greater_or_equal(&User::id, 10)); using Having = decltype(hav); using Tuple = node_tuple::type; diff --git a/tests/static_tests/select_return_type.cpp b/tests/static_tests/select_return_type.cpp new file mode 100644 index 000000000..f94f64ef0 --- /dev/null +++ b/tests/static_tests/select_return_type.cpp @@ -0,0 +1,67 @@ +#include +#include + +#include "static_tests_common.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_assert(std::is_same>::value, "Incorrect select id vector type"); + + using SelectVectorTuple = decltype(storage.select(columns(&User::id))); + auto ids = storage.select(columns(&User::id)); + static_assert(std::is_same::value, ""); + static_assert(std::is_same>>::value, + "Incorrect select id vector type"); + using IdsTuple = SelectVectorTuple::value_type; + static_assert(std::tuple_size::value == 1, "Incorrect tuple size"); + } + { + // test storage traits + struct Visit { + int id = 0; + std::string date; + }; + using namespace sqlite_orm::internal::storage_traits; + + // test type_is_mapped + static_assert(type_is_mapped::value, "User must be mapped to a storage"); + static_assert(!type_is_mapped::value, "User must be mapped to a storage"); + + // test is_storage + static_assert(internal::is_storage::value, "is_storage works incorrectly"); + static_assert(!internal::is_storage::value, "is_storage works incorrectly"); + static_assert(!internal::is_storage::value, "is_storage works incorrectly"); + static_assert(!internal::is_storage::value, "is_storage works incorrectly"); + + auto storage2 = make_storage( + "", + make_table("visits", make_column("id", &Visit::id, primary_key()), make_column("date", &Visit::date))); + + // test storage_columns_count + static_assert(storage_columns_count::value == 1, + "Incorrect storage columns count value"); + static_assert(storage_columns_count::value == 0, + "Incorrect storage columns count value"); + static_assert(storage_columns_count::value == 2, + "Incorrect storage columns count value"); + + // test storage mapped columns + using UserColumnsTuple = storage_mapped_columns::type; + static_assert(std::is_same>::value, + "Incorrect storage_mapped_columns result"); + + using VisitColumsEmptyType = storage_mapped_columns::type; + static_assert(std::is_same>::value, + "Incorrect storage_mapped_columns result"); + + using VisitColumnTypes = storage_mapped_columns::type; + static_assert(std::is_same>::value, + "Incorrect storage_mapped_columns result"); + } +} diff --git a/tests/static_tests/static_tests_common.h b/tests/static_tests/static_tests_common.h new file mode 100644 index 000000000..b27ae1e26 --- /dev/null +++ b/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/tests/storage_tests.cpp b/tests/storage_tests.cpp new file mode 100644 index 000000000..3f9e1376c --- /dev/null +++ b/tests/storage_tests.cpp @@ -0,0 +1,124 @@ +#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); + } +} diff --git a/tests/sync_schema_tests.cpp b/tests/sync_schema_tests.cpp index 54c86d4c4..1169ef7c0 100644 --- a/tests/sync_schema_tests.cpp +++ b/tests/sync_schema_tests.cpp @@ -109,3 +109,307 @@ TEST_CASE("Sync schema") { }); REQUIRE(std::equal(ids.begin(), ids.end(), idsFromGetAll.begin(), idsFromGetAll.end())); } + +TEST_CASE("issue521") { + auto storagePath = "issue521.sqlite"; + + struct MockDatabasePoco { + int id{-1}; + std::string name{""}; + uint32_t alpha{0}; + float beta{0.0}; + }; + 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.10}); + pocosToInsert.push_back({-1, "Joyce", 20, 20.20}); + + 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(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); + 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 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") { + 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); + 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 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); + decltype(syncSchemaRes) expected{ + {tableName, sync_schema_result::dropped_and_recreated}, + }; + REQUIRE(syncSchemaRes == expected); + auto users = storage.get_all(); + REQUIRE(users.empty()); + } + } +} diff --git a/tests/table_tests.cpp b/tests/table_tests.cpp new file mode 100644 index 000000000..b96bb796a --- /dev/null +++ b/tests/table_tests.cpp @@ -0,0 +1,173 @@ +#include +#include +#include + +using namespace sqlite_orm; +using std::cout; +using std::endl; + +TEST_CASE("table") { + { + 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_assert(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"); + } + { + 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 = move(value); + } + + const std::string &lastName() const { + return this->_lastName; + } + + void setLastName(std::string value) { + this->_lastName = 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 = 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()); + } +} diff --git a/tests/tests.cpp b/tests/tests.cpp index 73d4900fc..adbe80e96 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -14,43 +14,7 @@ using namespace sqlite_orm; -TEST_CASE("Join iterator ctor compilation error") { - // TODO: move to static tests - 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); - } -} - -TEST_CASE("limits") { +TEST_CASE("Limits") { auto storage2 = make_storage("limits.sqlite"); auto storage = storage2; storage.sync_schema(); @@ -358,6 +322,32 @@ TEST_CASE("Custom collate") { REQUIRE(rows.size() == static_cast(storage.count())); } +TEST_CASE("collate") { + struct User { + int id = 0; + std::string firstName; + + bool operator==(const User &user) const { + return this->id == user.id && this->firstName == user.firstName; + } + }; + auto storage = make_storage( + {}, + make_table("users", make_column("id", &User::id, primary_key()), make_column("first_name", &User::firstName))); + storage.sync_schema(); + User user1{1, "HELLO"}; + User user2{2, "Hello"}; + User user3{3, "HEllo"}; + + storage.replace(user1); + storage.replace(user2); + storage.replace(user3); + + auto rows = storage.get_all(where(is_equal(&User::firstName, "hello").collate_nocase())); + std::vector expected = {user1, user2, user3}; + REQUIRE(rows == expected); +} + TEST_CASE("Vacuum") { struct Item { int id; diff --git a/tests/tests2.cpp b/tests/tests2.cpp index 2ffe3eee5..6483895ff 100644 --- a/tests/tests2.cpp +++ b/tests/tests2.cpp @@ -254,7 +254,7 @@ TEST_CASE("Select") { REQUIRE(storage.get(firstId).currentWord == "ototo"); } -TEST_CASE("Replace") { +TEST_CASE("Replace query") { struct Object { int id; std::string name; @@ -268,16 +268,16 @@ TEST_CASE("Replace") { return this->id; } - void setId(int id) { - this->id = id; + void setId(int id_) { + this->id = id_; } std::string getName() const { return this->name; } - void setName(std::string name) { - this->name = move(name); + void setName(std::string name_) { + this->name = move(name_); } private: @@ -403,78 +403,3 @@ TEST_CASE("Insert") { storage.insert(ObjectWithoutRowid{20, "Death"}); REQUIRE(storage.get(20).name == "Death"); } - -TEST_CASE("Type parsing") { - using namespace sqlite_orm::internal; - - // int - REQUIRE(*to_sqlite_type("INT") == sqlite_type::INTEGER); - REQUIRE(*to_sqlite_type("integeer") == sqlite_type::INTEGER); - REQUIRE(*to_sqlite_type("INTEGER") == sqlite_type::INTEGER); - REQUIRE(*to_sqlite_type("TINYINT") == sqlite_type::INTEGER); - REQUIRE(*to_sqlite_type("SMALLINT") == sqlite_type::INTEGER); - REQUIRE(*to_sqlite_type("MEDIUMINT") == sqlite_type::INTEGER); - REQUIRE(*to_sqlite_type("BIGINT") == sqlite_type::INTEGER); - REQUIRE(*to_sqlite_type("UNSIGNED BIG INT") == sqlite_type::INTEGER); - REQUIRE(*to_sqlite_type("INT2") == sqlite_type::INTEGER); - REQUIRE(*to_sqlite_type("INT8") == sqlite_type::INTEGER); - - // text - REQUIRE(*to_sqlite_type("TEXT") == sqlite_type::TEXT); - REQUIRE(*to_sqlite_type("CLOB") == sqlite_type::TEXT); - for(auto i = 0; i < 255; ++i) { - REQUIRE(*to_sqlite_type("CHARACTER(" + std::to_string(i) + ")") == sqlite_type::TEXT); - REQUIRE(*to_sqlite_type("VARCHAR(" + std::to_string(i) + ")") == sqlite_type::TEXT); - REQUIRE(*to_sqlite_type("VARYING CHARACTER(" + std::to_string(i) + ")") == sqlite_type::TEXT); - REQUIRE(*to_sqlite_type("NCHAR(" + std::to_string(i) + ")") == sqlite_type::TEXT); - REQUIRE(*to_sqlite_type("NATIVE CHARACTER(" + std::to_string(i) + ")") == sqlite_type::TEXT); - REQUIRE(*to_sqlite_type("NVARCHAR(" + std::to_string(i) + ")") == sqlite_type::TEXT); - } - - // blob.. - REQUIRE(*to_sqlite_type("BLOB") == sqlite_type::BLOB); - - // real - REQUIRE(*to_sqlite_type("REAL") == sqlite_type::REAL); - REQUIRE(*to_sqlite_type("DOUBLE") == sqlite_type::REAL); - REQUIRE(*to_sqlite_type("DOUBLE PRECISION") == sqlite_type::REAL); - REQUIRE(*to_sqlite_type("FLOAT") == sqlite_type::REAL); - - REQUIRE(*to_sqlite_type("NUMERIC") == sqlite_type::REAL); - for(auto i = 0; i < 255; ++i) { - for(auto j = 0; j < 10; ++j) { - REQUIRE(*to_sqlite_type("DECIMAL(" + std::to_string(i) + "," + std::to_string(j) + ")") == - sqlite_type::REAL); - } - } - REQUIRE(*to_sqlite_type("BOOLEAN") == sqlite_type::REAL); - REQUIRE(*to_sqlite_type("DATE") == sqlite_type::REAL); - REQUIRE(*to_sqlite_type("DATETIME") == sqlite_type::REAL); - - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable::value == false); - REQUIRE(type_is_nullable>::value == true); - REQUIRE(type_is_nullable>::value == true); - REQUIRE(type_is_nullable>::value == true); - REQUIRE(type_is_nullable>::value == true); - -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - REQUIRE(type_is_nullable>::value == true); - REQUIRE(type_is_nullable>::value == true); -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED -} diff --git a/tests/tests3.cpp b/tests/tests3.cpp index 0ca8b9de5..2a2418aae 100644 --- a/tests/tests3.cpp +++ b/tests/tests3.cpp @@ -98,13 +98,13 @@ TEST_CASE("Row id") { TEST_CASE("Issue 87") { struct Data { - uint8_t mDefault; /**< 0=User or 1=Default*/ - uint8_t mAppLang; // en_GB - uint8_t mContentLang1; // de_DE - uint8_t mContentLang2; // en_GB - uint8_t mContentLang3; - uint8_t mContentLang4; - uint8_t mContentLang5; + 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; @@ -450,93 +450,3 @@ TEST_CASE("Escape chars") { storage.update(selena); storage.remove(10); } - -TEST_CASE("Transaction guard") { - struct Object { - int id; - std::string name; - }; - - auto storage = make_storage( - "test_transaction_guard.sqlite", - make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name))); - - storage.sync_schema(); - storage.remove_all(); - - storage.insert(Object{0, "Jack"}); - - // insert, call make a storage to cakk 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(...) { - auto countNow = storage.count(); - - REQUIRE(countBefore == countNow); - } - - // check that one can call other transaction functions without exceptions - storage.transaction([&] { - return false; - }); - - // commit explicitly and check that after exception data was saved - countBefore = storage.count(); - try { - auto guard = storage.transaction_guard(); - storage.insert(Object{0, "John"}); - guard.commit(); - storage.get(-1); - REQUIRE(false); - } catch(...) { - auto countNow = storage.count(); - - REQUIRE(countNow == countBefore + 1); - } - - // rollback explicitly - countBefore = storage.count(); - try { - auto guard = storage.transaction_guard(); - storage.insert(Object{0, "Michael"}); - guard.rollback(); - storage.get(-1); - REQUIRE(false); - } catch(...) { - auto countNow = storage.count(); - REQUIRE(countNow == countBefore); - } - - // commit on exception - 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(...) { - auto countNow = storage.count(); - REQUIRE(countNow == countBefore + 1); - } - - // work witout exception - countBefore = storage.count(); - try { - auto guard = storage.transaction_guard(); - guard.commit_on_destroy = true; - storage.insert(Object{0, "Lincoln"}); - - } catch(...) { - throw std::runtime_error("Must not fire"); - } - auto countNow = storage.count(); - REQUIRE(countNow == countBefore + 1); -} diff --git a/tests/tests4.cpp b/tests/tests4.cpp index 6a96348a6..885b8c7ba 100644 --- a/tests/tests4.cpp +++ b/tests/tests4.cpp @@ -1,17 +1,12 @@ #include #include -#include #include // std::count_if -#include #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED #include // std::optional #endif // SQLITE_ORM_OPTIONAL_SUPPORTED using namespace sqlite_orm; -using std::cout; -using std::endl; - TEST_CASE("Case") { struct User { @@ -226,6 +221,14 @@ TEST_CASE("Join") { 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("Storage copy") { @@ -270,10 +273,11 @@ TEST_CASE("Set null") { 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); + { + auto rows = storage.get_all(); + REQUIRE(rows.size() == 1); + REQUIRE(rows.front().name); + } storage.update_all(set(assign(&User::name, nullptr))); { @@ -297,382 +301,3 @@ TEST_CASE("Set null") { REQUIRE(!rows.front().name); } } - -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("Not operator") { - struct Object { - int id = 0; - }; - - auto storage = make_storage("", make_table("objects", make_column("id", &Object::id, primary_key()))); - storage.sync_schema(); - - storage.replace(Object{2}); - - auto rows = storage.select(&Object::id, where(not is_equal(&Object::id, 1))); - REQUIRE(rows.size() == 1); - REQUIRE(rows.front() == 2); -} - -TEST_CASE("Between operator") { - struct Object { - int id = 0; - }; - - auto storage = - make_storage("", make_table("objects", make_column("id", &Object::id, autoincrement(), primary_key()))); - 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); -} - -TEST_CASE("Exists") { - struct User { - int id = 0; - std::string name; - }; - - struct Visit { - int id = 0; - int userId = 0; - time_t time = 0; - }; - - auto storage = - make_storage("", - make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)), - make_table("visits", - make_column("id", &Visit::id, primary_key()), - make_column("userId", &Visit::userId), - make_column("time", &Visit::time), - foreign_key(&Visit::userId).references(&User::id))); - storage.sync_schema(); - - storage.replace(User{1, "Daddy Yankee"}); - storage.replace(User{2, "Don Omar"}); - - storage.replace(Visit{1, 1, 100000}); - storage.replace(Visit{2, 1, 100001}); - storage.replace(Visit{3, 1, 100002}); - storage.replace(Visit{4, 1, 200000}); - - storage.replace(Visit{5, 2, 100000}); - - auto rows = storage.select( - &User::id, - where(exists(select(&Visit::id, where(c(&Visit::time) == 200000 and eq(&Visit::userId, &User::id)))))); - REQUIRE(!rows.empty() == 1); -} - -TEST_CASE("Iterate blob") { - struct Test { - int64_t id; - 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") { - // this code just shows this value on CI - cout << "threadsafe = " << threadsafe() << endl; -} - -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 diff --git a/tests/tests5.cpp b/tests/tests5.cpp new file mode 100644 index 000000000..866c17be4 --- /dev/null +++ b/tests/tests5.cpp @@ -0,0 +1,300 @@ +#include +#include +#include // std::iota + +using namespace sqlite_orm; + +TEST_CASE("Exists") { + struct User { + int id = 0; + std::string name; + }; + + struct Visit { + int id = 0; + int userId = 0; + time_t time = 0; + }; + + auto storage = + make_storage("", + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)), + make_table("visits", + make_column("id", &Visit::id, primary_key()), + make_column("userId", &Visit::userId), + make_column("time", &Visit::time), + foreign_key(&Visit::userId).references(&User::id))); + storage.sync_schema(); + + storage.replace(User{1, "Daddy Yankee"}); + storage.replace(User{2, "Don Omar"}); + + storage.replace(Visit{1, 1, 100000}); + storage.replace(Visit{2, 1, 100001}); + storage.replace(Visit{3, 1, 100002}); + storage.replace(Visit{4, 1, 200000}); + + storage.replace(Visit{5, 2, 100000}); + + auto rows = storage.select( + &User::id, + where(exists(select(&Visit::id, where(c(&Visit::time) == 200000 and eq(&Visit::userId, &User::id)))))); + REQUIRE(!rows.empty() == 1); +} + +TEST_CASE("Iterate blob") { + struct Test { + int64_t id; + 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 diff --git a/tests/third_party/sqlite/CMakeLists.txt b/tests/third_party/sqlite/CMakeLists.txt index d990826b8..c792b188d 100644 --- a/tests/third_party/sqlite/CMakeLists.txt +++ b/tests/third_party/sqlite/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required (VERSION 3.2) set(SQLITE3_ARCH_NAME "${CMAKE_CURRENT_BINARY_DIR}/sqlite.zip") -set(SQLITE3_LINK "https://www.sqlite.org/2017/sqlite-amalgamation-3210000.zip") -set(SQLITE3_ARCH_SHA1 "ebe33c20d37a715db95288010c1009cd560f2452") +set(SQLITE3_LINK "https://www.sqlite.org/2020/sqlite-amalgamation-3320300.zip") +set(SQLITE3_ARCH_SHA1 "0c805bea134712a903290a26b2a61c3a8a3bd8cc") add_custom_command( OUTPUT ${SQLITE3_ARCH_NAME} @@ -10,15 +10,15 @@ add_custom_command( ${CMAKE_COMMAND} -DSQLITE3_ARCH_NAME=${SQLITE3_ARCH_NAME} -DSQLITE3_LINK=${SQLITE3_LINK} -DSQLITE3_ARCH_SHA1=${SQLITE3_ARCH_SHA1} -P "${CMAKE_CURRENT_SOURCE_DIR}/DownloadSqlite3.cmake") add_custom_command( - OUTPUT sqlite-amalgamation-3210000 sqlite-amalgamation-3210000/sqlite3.c sqlite-amalgamation-3210000/sqlite3.h + OUTPUT sqlite-amalgamation-3320300 sqlite-amalgamation-3320300/sqlite3.c sqlite-amalgamation-3320300/sqlite3.h DEPENDS ${SQLITE3_ARCH_NAME} COMMAND ${CMAKE_COMMAND} -E tar xfz ${SQLITE3_ARCH_NAME}) -add_library(sqlite3 sqlite-amalgamation-3210000/sqlite3.c) +add_library(sqlite3 sqlite-amalgamation-3320300/sqlite3.c) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) -target_include_directories(sqlite3 INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/sqlite-amalgamation-3210000") +target_include_directories(sqlite3 INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/sqlite-amalgamation-3320300") target_link_libraries(sqlite3 PRIVATE ${CMAKE_DL_LIBS} Threads::Threads) diff --git a/tests/transaction_tests.cpp b/tests/transaction_tests.cpp new file mode 100644 index 000000000..301fe1f0f --- /dev/null +++ b/tests/transaction_tests.cpp @@ -0,0 +1,114 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("transaction") { + struct Object { + int id = 0; + std::string name; + }; + 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 guard") { + struct Object { + int id = 0; + std::string name; + }; + + auto storage = make_storage( + "test_transaction_guard.sqlite", + make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name))); + + storage.sync_schema(); + storage.remove_all(); + + storage.insert(Object{0, "Jack"}); + + SECTION("insert, call make a storage to call an exception and check that rollback was fired") { + auto countBefore = storage.count(); + try { + auto guard = storage.transaction_guard(); + + storage.insert(Object{0, "John"}); + + storage.get(-1); + + REQUIRE(false); + } catch(...) { + auto countNow = storage.count(); + + REQUIRE(countBefore == countNow); + } + } + SECTION("check that one can call other transaction functions without exceptions") { + 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(...) { + 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(...) { + 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(...) { + auto countNow = storage.count(); + REQUIRE(countNow == countBefore + 1); + } + } + SECTION("work witout exception") { + auto countBefore = storage.count(); + try { + auto guard = storage.transaction_guard(); + guard.commit_on_destroy = true; + storage.insert(Object{0, "Lincoln"}); + } catch(...) { + throw std::runtime_error("Must not fire"); + } + auto countNow = storage.count(); + REQUIRE(countNow == countBefore + 1); + } +} diff --git a/tests/unique_cases/delete_with_two_fields.cpp b/tests/unique_cases/delete_with_two_fields.cpp new file mode 100644 index 000000000..0d91fc9ce --- /dev/null +++ b/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/tests/unique_cases/get_all_with_two_tables.cpp b/tests/unique_cases/get_all_with_two_tables.cpp new file mode 100644 index 000000000..878904aaf --- /dev/null +++ b/tests/unique_cases/get_all_with_two_tables.cpp @@ -0,0 +1,70 @@ +#include +#include + +using namespace sqlite_orm; + +namespace GetAllWithTwoTablesInternal { + struct Pattern { + std::string value; + }; + struct Item { + int id = 0; + std::string attributes; + }; + + 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; + using namespace GetAllWithTwoTablesInternal; + + 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); + + std::vector patterns; + patterns.push_back({"n"}); + patterns.push_back({"w"}); + + storage.begin_transaction(); + storage.insert_range(patterns.begin(), patterns.end()); + { + 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/tests/unique_cases/index_named_table_with_fk.cpp b/tests/unique_cases/index_named_table_with_fk.cpp new file mode 100644 index 000000000..3ebd8183e --- /dev/null +++ b/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/tests/unique_cases/issue525.cpp b/tests/unique_cases/issue525.cpp new file mode 100644 index 000000000..fd9c0b6a1 --- /dev/null +++ b/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, autoincrement(), primary_key()), + 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, autoincrement(), primary_key()), + make_column("name", &UserType::name, default_value("name_placeholder")))); + + storage.sync_schema(); +} diff --git a/tests/unique_cases/join_iterator_ctor_compilation_error.cpp b/tests/unique_cases/join_iterator_ctor_compilation_error.cpp new file mode 100644 index 000000000..cbbb947e0 --- /dev/null +++ b/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/tests/unique_cases/prepare_get_all_with_case.cpp b/tests/unique_cases/prepare_get_all_with_case.cpp new file mode 100644 index 000000000..a4e66cafe --- /dev/null +++ b/tests/unique_cases/prepare_get_all_with_case.cpp @@ -0,0 +1,31 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("Prepare with case") { + struct UserProfile { + int id = 0; + std::string firstName; + }; + + 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"; + + { + 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; + } + { + auto statement = storage.prepare(get_all(where( + case_().when(length(name) > 0, then(like(&UserProfile::firstName, name))).else_(true).end()))); + std::ignore = statement; + } +} diff --git a/third_party/amalgamate/config.json b/third_party/amalgamate/config.json index b5345fc1e..d9dfc9c54 100755 --- a/third_party/amalgamate/config.json +++ b/third_party/amalgamate/config.json @@ -4,7 +4,6 @@ "sources": [ "dev/start_macros.h", "dev/error_code.h", - "dev/sqlite_type.h", "dev/tuple_helper.h", "dev/type_printer.h", "dev/collate_argument.h", @@ -18,7 +17,6 @@ "dev/alias.h", "dev/join_iterator.h", "dev/core_functions.h", - "dev/aggregate_functions.h", "dev/typed_comparator.h", "dev/select_constraints.h", "dev/table_type.h", @@ -37,8 +35,7 @@ "dev/storage.h", "dev/finish_macros.h", "dev/node_tuple.h", - "dev/get_prepared_statement.h", - "dev/statement_serializator.h" + "dev/get_prepared_statement.h" ], "include_paths": ["dev"] }